Data.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. import {
  2. createContext,
  3. useCallback,
  4. useContext,
  5. useEffect,
  6. useState,
  7. useRef,
  8. } from 'react';
  9. import { io, Socket } from 'socket.io-client';
  10. import { authContext } from '@/context';
  11. import { url, CollectionService, MilvusService, DatabaseService } from '@/http';
  12. import { IndexCreateParam, IndexManageParam } from '@/pages/databases/collections/overview/Types';
  13. import { getDbValueFromUrl } from '@/utils';
  14. import { DataContextType } from './Types';
  15. import { LAST_TIME_DATABASE } from '@/consts';
  16. import {
  17. CollectionObject,
  18. CollectionFullObject,
  19. DatabaseObject,
  20. } from '@server/types';
  21. import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
  22. import { checkIndexing, checkLoading } from '@server/utils/Shared';
  23. export const dataContext = createContext<DataContextType>({
  24. loading: true,
  25. loadingDatabases: true,
  26. collections: [],
  27. setCollections: () => {},
  28. database: '',
  29. setDatabase: () => {},
  30. databases: [],
  31. setDatabaseList: () => {},
  32. createDatabase: async () => {},
  33. dropDatabase: async () => {},
  34. fetchDatabases: async () => {
  35. return [];
  36. },
  37. fetchCollections: async () => {},
  38. fetchCollection: async () => {
  39. return {} as CollectionFullObject;
  40. },
  41. createCollection: async () => {
  42. return {} as CollectionFullObject;
  43. },
  44. loadCollection: async () => {
  45. return {} as CollectionFullObject;
  46. },
  47. releaseCollection: async () => {
  48. return {} as CollectionFullObject;
  49. },
  50. renameCollection: async () => {
  51. return {} as CollectionFullObject;
  52. },
  53. duplicateCollection: async () => {
  54. return {} as CollectionFullObject;
  55. },
  56. dropCollection: async () => {},
  57. createIndex: async () => {
  58. return {} as CollectionFullObject;
  59. },
  60. dropIndex: async () => {
  61. return {} as CollectionFullObject;
  62. },
  63. createAlias: async () => {
  64. return {} as CollectionFullObject;
  65. },
  66. dropAlias: async () => {
  67. return {} as CollectionFullObject;
  68. },
  69. });
  70. const { Provider } = dataContext;
  71. export const DataProvider = (props: { children: React.ReactNode }) => {
  72. // get database name from url
  73. const currentUrl = window.location.href;
  74. const initialDatabase = getDbValueFromUrl(currentUrl);
  75. // local data state
  76. const [collections, setCollections] = useState<CollectionObject[]>([]);
  77. const [connected, setConnected] = useState(false);
  78. const [loading, setLoading] = useState(true);
  79. const [loadingDatabases, setLoadingDatabases] = useState(true);
  80. const defaultDb =
  81. initialDatabase ||
  82. window.localStorage.getItem(LAST_TIME_DATABASE) ||
  83. 'default';
  84. const [database, setDatabase] = useState<string>(defaultDb);
  85. const [databases, setDatabases] = useState<DatabaseObject[]>([]);
  86. // auth context
  87. const { isAuth, clientId, logout } = useContext(authContext);
  88. // socket ref
  89. const socket = useRef<Socket | null>(null);
  90. // collection state test
  91. const detectLoadingIndexing = useCallback(
  92. (collections: CollectionObject[]) => {
  93. const LoadingOrBuildingCollections = collections.filter(v => {
  94. const isLoading = checkLoading(v);
  95. const isBuildingIndex = checkIndexing(v);
  96. return isLoading || isBuildingIndex;
  97. });
  98. // trigger cron if it has
  99. if (LoadingOrBuildingCollections.length > 0) {
  100. MilvusService.triggerCron({
  101. name: WS_EVENTS.COLLECTION_UPDATE,
  102. type: WS_EVENTS_TYPE.START,
  103. payload: {
  104. database,
  105. collections: LoadingOrBuildingCollections.map(
  106. c => c.collection_name
  107. ),
  108. },
  109. });
  110. }
  111. },
  112. [database]
  113. );
  114. // Websocket Callback: update single collection
  115. const updateCollections = useCallback(
  116. (updateCollections: CollectionFullObject[]) => {
  117. // check state to see if it is loading or building index, if so, start server cron job
  118. detectLoadingIndexing(updateCollections);
  119. // update single collection
  120. setCollections(prev => {
  121. // update exsit collection
  122. const newCollections = prev.map(v => {
  123. const collectionToUpdate = updateCollections.find(c => c.id === v.id);
  124. if (collectionToUpdate) {
  125. return collectionToUpdate;
  126. }
  127. return v;
  128. });
  129. return newCollections;
  130. });
  131. },
  132. [database]
  133. );
  134. // API: fetch databases
  135. const fetchDatabases = async (updateLoading?: boolean) => {
  136. updateLoading && setLoadingDatabases(true);
  137. const newDatabases = await DatabaseService.listDatabases();
  138. updateLoading && setLoadingDatabases(false);
  139. // if no database, logout
  140. if (newDatabases.length === 0) {
  141. logout();
  142. }
  143. setDatabases(newDatabases);
  144. return newDatabases;
  145. };
  146. // API: create database
  147. const createDatabase = async (params: { db_name: string }) => {
  148. await DatabaseService.createDatabase(params);
  149. await fetchDatabases();
  150. };
  151. // API: delete database
  152. const dropDatabase = async (params: { db_name: string }) => {
  153. await DatabaseService.dropDatabase(params);
  154. const newDatabases = await fetchDatabases();
  155. setDatabase(newDatabases[0].name);
  156. };
  157. // API:fetch collections
  158. const fetchCollections = async () => {
  159. try {
  160. // set loading true
  161. setLoading(true);
  162. // fetch collections
  163. const res = await CollectionService.getCollections();
  164. // check state
  165. detectLoadingIndexing(res);
  166. // set collections
  167. setCollections(res);
  168. // set loading false
  169. setLoading(false);
  170. } finally {
  171. setLoading(false);
  172. }
  173. };
  174. // API: fetch single collection
  175. const fetchCollection = async (name: string) => {
  176. // fetch collections
  177. const res = await CollectionService.getCollection(name);
  178. // update collection
  179. updateCollections([res]);
  180. return res;
  181. };
  182. // API: create collection
  183. const createCollection = async (data: any) => {
  184. // create collection
  185. const newCollection = await CollectionService.createCollection(data);
  186. // combine new collection with old collections
  187. // sort state by createdTime.
  188. const newCollections = collections.concat(newCollection).sort((a, b) => {
  189. if (a.loadedPercentage === b.loadedPercentage && a.schema && b.schema) {
  190. if (a.schema.hasVectorIndex === b.schema.hasVectorIndex) {
  191. return b.createdTime - a.createdTime;
  192. }
  193. return a.schema.hasVectorIndex ? -1 : 1;
  194. }
  195. return (b.loadedPercentage || 0) - (a.loadedPercentage || 0);
  196. });
  197. // update collection
  198. setCollections(newCollections);
  199. return newCollection;
  200. };
  201. // API: load collection
  202. const loadCollection = async (name: string, param?: any) => {
  203. // load collection
  204. const newCollection = await CollectionService.loadCollection(name, param);
  205. // update collection
  206. updateCollections([newCollection]);
  207. return newCollection;
  208. };
  209. // API: release collection
  210. const releaseCollection = async (name: string) => {
  211. // release collection
  212. const newCollection = await CollectionService.releaseCollection(name);
  213. // update collection
  214. updateCollections([newCollection]);
  215. return newCollection;
  216. };
  217. // API: rename collection
  218. const renameCollection = async (name: string, newName: string) => {
  219. // rename collection
  220. const newCollection = await CollectionService.renameCollection(name, {
  221. new_collection_name: newName,
  222. });
  223. updateCollections([newCollection]);
  224. return newCollection;
  225. };
  226. // API: duplicate collection
  227. const duplicateCollection = async (name: string, newName: string) => {
  228. // duplicate collection
  229. const newCollection = await CollectionService.duplicateCollection(name, {
  230. new_collection_name: newName,
  231. });
  232. // inset collection to state
  233. setCollections(prev => [...prev, newCollection]);
  234. return newCollection;
  235. };
  236. // API: drop collection
  237. const dropCollection = async (name: string) => {
  238. // drop collection
  239. const dropped = await CollectionService.dropCollection(name);
  240. if (dropped.data.error_code === 'Success') {
  241. // remove collection from state
  242. setCollections(prev => prev.filter(v => v.collection_name !== name));
  243. }
  244. };
  245. // API: create index
  246. const createIndex = async (param: IndexCreateParam) => {
  247. // create index
  248. const newCollection = await CollectionService.createIndex(param);
  249. // update collection
  250. updateCollections([newCollection]);
  251. return newCollection;
  252. };
  253. // API: drop index
  254. const dropIndex = async (params: IndexManageParam) => {
  255. // drop index
  256. const { data } = await CollectionService.dropIndex(params);
  257. // update collection
  258. updateCollections([data]);
  259. return data;
  260. };
  261. // API: create alias
  262. const createAlias = async (collectionName: string, alias: string) => {
  263. // create alias
  264. const newCollection = await CollectionService.createAlias(collectionName, {
  265. alias,
  266. });
  267. // update collection
  268. updateCollections([newCollection]);
  269. return newCollection;
  270. };
  271. // API: drop alias
  272. const dropAlias = async (collectionName: string, alias: string) => {
  273. // drop alias
  274. const { data } = await CollectionService.dropAlias(collectionName, {
  275. alias,
  276. });
  277. // update collection
  278. updateCollections([data]);
  279. return data;
  280. };
  281. useEffect(() => {
  282. if (isAuth) {
  283. // connect to socket server
  284. socket.current = io(url as string);
  285. // register client
  286. socket.current.emit(WS_EVENTS.REGISTER, clientId);
  287. socket.current.on('connect', async () => {
  288. console.log('--- ws connected ---', clientId);
  289. // fetch db
  290. await fetchDatabases(true);
  291. // set connected to trues
  292. setConnected(true);
  293. });
  294. } else {
  295. socket.current?.disconnect();
  296. // clear collections
  297. setCollections([]);
  298. // clear database
  299. setDatabases([]);
  300. // set connected to false
  301. setConnected(false);
  302. }
  303. }, [isAuth]);
  304. useEffect(() => {
  305. if (connected) {
  306. // clear data
  307. setCollections([]);
  308. // remove all listeners
  309. socket.current?.offAny();
  310. // listen to backend collection event
  311. socket.current?.on(WS_EVENTS.COLLECTION_UPDATE, updateCollections);
  312. // fetch db
  313. fetchCollections();
  314. }
  315. return () => {
  316. // remove all listeners when component unmount
  317. socket.current?.offAny();
  318. };
  319. }, [updateCollections, connected]);
  320. return (
  321. <Provider
  322. value={{
  323. loading,
  324. loadingDatabases,
  325. collections,
  326. setCollections,
  327. database,
  328. databases,
  329. setDatabase,
  330. setDatabaseList: setDatabases,
  331. createDatabase,
  332. dropDatabase,
  333. fetchDatabases,
  334. fetchCollections,
  335. fetchCollection,
  336. createCollection,
  337. loadCollection,
  338. releaseCollection,
  339. renameCollection,
  340. duplicateCollection,
  341. dropCollection,
  342. createIndex,
  343. dropIndex,
  344. createAlias,
  345. dropAlias,
  346. }}
  347. >
  348. {props.children}
  349. </Provider>
  350. );
  351. };