useCollectionsuseCollectionsManagement.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import { useCallback, useRef, useState } from 'react';
  2. import { CollectionService, MilvusService } from '@/http';
  3. import { WS_EVENTS, WS_EVENTS_TYPE, LOADING_STATE } from '@server/utils/Const';
  4. import { checkIndexing, checkLoading } from '@server/utils/Shared';
  5. import type {
  6. IndexCreateParam,
  7. IndexManageParam,
  8. } from '@/pages/databases/collections/schema/Types';
  9. import type { CollectionObject, CollectionFullObject } from '@server/types';
  10. export function useCollectionsManagement(database: string) {
  11. const [collections, setCollections] = useState<CollectionObject[]>([]);
  12. const [loading, setLoading] = useState(true);
  13. const requestIdRef = useRef(0);
  14. const detectLoadingIndexing = useCallback(
  15. (collections: CollectionObject[]) => {
  16. const LoadingOrBuildingCollections = collections.filter(v => {
  17. const isLoading = checkLoading(v);
  18. const isBuildingIndex = checkIndexing(v);
  19. return isLoading || isBuildingIndex;
  20. });
  21. if (LoadingOrBuildingCollections.length > 0) {
  22. MilvusService.triggerCron({
  23. name: WS_EVENTS.COLLECTION_UPDATE,
  24. type: WS_EVENTS_TYPE.START,
  25. payload: {
  26. database,
  27. collections: LoadingOrBuildingCollections.map(
  28. c => c.collection_name
  29. ),
  30. },
  31. });
  32. }
  33. },
  34. [database]
  35. );
  36. const updateCollections = useCallback(
  37. (props: { collections: CollectionFullObject[]; database?: string }) => {
  38. const { collections: updated = [], database: remote } = props;
  39. if (
  40. remote !== database &&
  41. database !== undefined &&
  42. remote !== undefined
  43. ) {
  44. return;
  45. }
  46. detectLoadingIndexing(updated);
  47. setCollections(prev => {
  48. const prevMap = new Map(prev.map(c => [c.id, c]));
  49. updated.forEach(c => {
  50. prevMap.set(c.id, c);
  51. });
  52. return Array.from(prevMap.values());
  53. });
  54. },
  55. [database, detectLoadingIndexing]
  56. );
  57. const fetchCollections = async () => {
  58. const currentRequestId = ++requestIdRef.current;
  59. try {
  60. setLoading(true);
  61. setCollections([]);
  62. const res = await CollectionService.getAllCollections();
  63. if (currentRequestId === requestIdRef.current) {
  64. detectLoadingIndexing(res);
  65. setCollections(res);
  66. setLoading(false);
  67. }
  68. } catch (error) {
  69. if (currentRequestId === requestIdRef.current) {
  70. setLoading(false);
  71. }
  72. throw error;
  73. }
  74. };
  75. const fetchCollection = async (name: string) => {
  76. const res = await CollectionService.getCollection(name);
  77. updateCollections({ collections: [res] });
  78. return res;
  79. };
  80. const _fetchCollections = useCallback(
  81. async (collectionNames: string[]) => {
  82. const res = await CollectionService.getCollections({
  83. db_name: database,
  84. collections: collectionNames,
  85. });
  86. updateCollections({ collections: res });
  87. },
  88. [database, updateCollections]
  89. );
  90. const refreshCollectionsDebounceMapRef = useRef<
  91. Map<
  92. string,
  93. { timer: NodeJS.Timeout | null; names: string[]; pending: Set<string> }
  94. >
  95. >(new Map());
  96. const batchRefreshCollections = useCallback(
  97. (collectionNames: string[], key: string = 'default') => {
  98. let ref = refreshCollectionsDebounceMapRef.current.get(key);
  99. if (!ref) {
  100. ref = { timer: null, names: [], pending: new Set() };
  101. refreshCollectionsDebounceMapRef.current.set(key, ref);
  102. }
  103. const filteredCollectionNames = collectionNames.filter(name => {
  104. const collection = collections.find(v => v.collection_name === name);
  105. return collection && !collection.schema && !ref!.pending.has(name);
  106. });
  107. ref.names = filteredCollectionNames;
  108. if (ref.timer) {
  109. clearTimeout(ref.timer);
  110. }
  111. function getRandomBatchSize() {
  112. const weights = [2, 2, 2, 3, 3, 3, 4, 4, 5];
  113. return weights[Math.floor(Math.random() * weights.length)];
  114. }
  115. ref.timer = setTimeout(async () => {
  116. if (ref!.names.length === 0) return;
  117. try {
  118. while (ref!.names.length > 0) {
  119. const batchSize = getRandomBatchSize();
  120. let batch = ref!.names.slice(0, batchSize);
  121. batch = batch.filter(name => {
  122. const collection = collections.find(
  123. v => v.collection_name === name
  124. );
  125. return collection && !collection.schema;
  126. });
  127. batch.forEach(name => ref!.pending.add(name));
  128. await _fetchCollections(batch);
  129. batch.forEach(name => ref!.pending.delete(name));
  130. ref!.names = ref!.names.slice(batch.length);
  131. }
  132. } catch (error) {
  133. console.error('Failed to refresh collections:', error);
  134. }
  135. ref!.names = [];
  136. ref!.timer = null;
  137. }, 200);
  138. },
  139. [collections, _fetchCollections]
  140. );
  141. const createCollection = async (data: any) => {
  142. const newCollection = await CollectionService.createCollection(data);
  143. const newCollections = collections.concat(newCollection).sort((a, b) => {
  144. if (a.loadedPercentage === b.loadedPercentage && a.schema && b.schema) {
  145. if (a.schema.hasVectorIndex === b.schema.hasVectorIndex) {
  146. return b.createdTime - a.createdTime;
  147. }
  148. return a.schema.hasVectorIndex ? -1 : 1;
  149. }
  150. return (b.loadedPercentage || 0) - (a.loadedPercentage || 0);
  151. });
  152. setCollections(newCollections);
  153. return newCollection;
  154. };
  155. const loadCollection = async (name: string, param?: any) => {
  156. const res = await CollectionService.loadCollection(name, param);
  157. const collection = collections.find(
  158. v => v.collection_name === name
  159. ) as CollectionFullObject;
  160. if (collection) {
  161. collection.loadedPercentage = 0;
  162. collection.loaded = false;
  163. collection.status = LOADING_STATE.LOADING;
  164. }
  165. updateCollections({ collections: [collection] });
  166. return res;
  167. };
  168. const releaseCollection = async (name: string) => {
  169. return await CollectionService.releaseCollection(name);
  170. };
  171. const renameCollection = async (name: string, newName: string) => {
  172. const newCollection = await CollectionService.renameCollection(name, {
  173. new_collection_name: newName,
  174. });
  175. updateCollections({ collections: [newCollection] });
  176. return newCollection;
  177. };
  178. const duplicateCollection = async (name: string, newName: string) => {
  179. const newCollection = await CollectionService.duplicateCollection(name, {
  180. new_collection_name: newName,
  181. });
  182. setCollections(prev => [...prev, newCollection]);
  183. return newCollection;
  184. };
  185. const dropCollection = async (name: string) => {
  186. const dropped = await CollectionService.dropCollection(name);
  187. if (dropped.error_code === 'Success') {
  188. setCollections(prev => prev.filter(v => v.collection_name !== name));
  189. }
  190. return dropped;
  191. };
  192. const createIndex = async (param: IndexCreateParam) => {
  193. const newCollection = await CollectionService.createIndex(param);
  194. updateCollections({ collections: [newCollection] });
  195. return newCollection;
  196. };
  197. const dropIndex = async (params: IndexManageParam) => {
  198. const { data } = await CollectionService.dropIndex(params);
  199. updateCollections({ collections: [data] });
  200. return data;
  201. };
  202. const createAlias = async (collectionName: string, alias: string) => {
  203. const newCollection = await CollectionService.createAlias(collectionName, {
  204. alias,
  205. });
  206. updateCollections({ collections: [newCollection] });
  207. return newCollection;
  208. };
  209. const dropAlias = async (collectionName: string, alias: string) => {
  210. const { data } = await CollectionService.dropAlias(collectionName, {
  211. alias,
  212. });
  213. updateCollections({ collections: [data] });
  214. return data;
  215. };
  216. const setCollectionProperty = async (
  217. collectionName: string,
  218. key: string,
  219. value: any
  220. ) => {
  221. const newCollection = await CollectionService.setProperty(collectionName, {
  222. [key]: value,
  223. });
  224. updateCollections({ collections: [newCollection] });
  225. return newCollection;
  226. };
  227. return {
  228. collections,
  229. setCollections,
  230. loading,
  231. fetchCollections,
  232. fetchCollection,
  233. batchRefreshCollections,
  234. createCollection,
  235. loadCollection,
  236. releaseCollection,
  237. renameCollection,
  238. duplicateCollection,
  239. dropCollection,
  240. createIndex,
  241. dropIndex,
  242. createAlias,
  243. dropAlias,
  244. setCollectionProperty,
  245. updateCollections,
  246. };
  247. }