Databases.tsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import { useContext, useEffect, useState } from 'react';
  2. import { useParams } from 'react-router-dom';
  3. import { useTranslation } from 'react-i18next';
  4. import { makeStyles, Theme } from '@material-ui/core';
  5. import { useNavigationHook } from '@/hooks';
  6. import { ALL_ROUTER_TYPES } from '@/router/Types';
  7. import RouteTabList from '@/components/customTabList/RouteTabList';
  8. import DatabaseTree from '@/pages/databases/tree';
  9. import { ITab } from '@/components/customTabList/Types';
  10. import Partitions from './collections/partitions/Partitions';
  11. import Overview from './collections/overview/Overview';
  12. import Data from './collections/data/CollectionData';
  13. import Segments from './collections/segments/Segments';
  14. import Properties from './collections/properties/Properties';
  15. import Search from './collections/search/Search';
  16. import { dataContext, authContext } from '@/context';
  17. import Collections from './collections/Collections';
  18. import StatusIcon, { LoadingType } from '@/components/status/StatusIcon';
  19. import { ConsistencyLevelEnum, DYNAMIC_FIELD } from '@/consts';
  20. import RefreshButton from './RefreshButton';
  21. import CopyButton from '@/components/advancedSearch/CopyButton';
  22. import { SearchParams } from './types';
  23. import { CollectionObject, CollectionFullObject } from '@server/types';
  24. const useStyles = makeStyles((theme: Theme) => ({
  25. wrapper: {
  26. flexDirection: 'row',
  27. },
  28. tree: {
  29. boxShadow: 'none',
  30. flexBasis: theme.spacing(28),
  31. width: theme.spacing(28),
  32. flexGrow: 0,
  33. flexShrink: 0,
  34. height: 'calc(100vh - 96px)',
  35. overflow: 'auto',
  36. boxSizing: 'border-box',
  37. padding: theme.spacing(0, 2, 0, 0),
  38. },
  39. tab: {
  40. flexGrow: 1,
  41. flexShrink: 1,
  42. overflowX: 'auto',
  43. padding: theme.spacing(0, 2),
  44. },
  45. headerIcon: {
  46. marginLeft: theme.spacing(0.5),
  47. '& svg': {
  48. fontSize: 15,
  49. color: theme.palette.primary.main,
  50. },
  51. },
  52. }));
  53. // Databases page(tree and tabs)
  54. const Databases = () => {
  55. // context
  56. const { database, collections, loading, fetchCollection } =
  57. useContext(dataContext);
  58. // UI state
  59. const [searchParams, setSearchParams] = useState<SearchParams[]>(
  60. [] as SearchParams[]
  61. );
  62. // init search params
  63. useEffect(() => {
  64. collections.forEach(c => {
  65. // find search params for the collection
  66. const searchParam = searchParams.find(
  67. s => s.collection.collection_name === c.collection_name
  68. );
  69. // if search params not found, and the schema is ready, create new search params
  70. if (!searchParam && c.schema) {
  71. setSearchParams(prevParams => {
  72. const scalarFields = c.schema.scalarFields.map(s => s.name);
  73. return [
  74. ...prevParams,
  75. {
  76. collection: c,
  77. searchParams: c.schema.vectorFields.map(v => {
  78. return {
  79. anns_field: v.name,
  80. params: {},
  81. data: '',
  82. expanded: c.schema.vectorFields.length === 1,
  83. field: v,
  84. selected: c.schema.vectorFields.length === 1,
  85. };
  86. }),
  87. globalParams: {
  88. topK: 50,
  89. consistency_level: ConsistencyLevelEnum.Bounded,
  90. filter: '',
  91. rerank: 'rrf',
  92. rrfParams: { k: 60 },
  93. weightedParams: {
  94. weights: Array(c.schema.vectorFields.length).fill(0.5),
  95. },
  96. output_fields: c.schema.enable_dynamic_field
  97. ? [...scalarFields, DYNAMIC_FIELD]
  98. : scalarFields,
  99. },
  100. searchResult: null,
  101. searchLatency: 0,
  102. },
  103. ];
  104. });
  105. } else {
  106. // update collection
  107. setSearchParams(prevParams => {
  108. return prevParams.map(s => {
  109. if (s.collection.collection_name === c.collection_name) {
  110. // update field in search params
  111. const searchParams = s.searchParams.map(sp => {
  112. const field = c.schema?.vectorFields.find(
  113. v => v.name === sp.anns_field
  114. );
  115. if (field) {
  116. return { ...sp, field };
  117. }
  118. return sp;
  119. });
  120. // update collection
  121. const collection = c;
  122. return { ...s, searchParams, collection };
  123. }
  124. return s;
  125. });
  126. });
  127. }
  128. });
  129. // delete search params for the collection that is not in the collections
  130. setSearchParams(prevParams => {
  131. return prevParams.filter(s =>
  132. collections.find(
  133. c => c.collection_name === s.collection.collection_name
  134. )
  135. );
  136. });
  137. }, [collections]);
  138. // get current collection from url
  139. const params = useParams();
  140. const {
  141. databaseName = '',
  142. collectionName = '',
  143. collectionPage = '',
  144. } = params;
  145. // get style
  146. const classes = useStyles();
  147. // update navigation
  148. useNavigationHook(ALL_ROUTER_TYPES.DATABASES, {
  149. collectionName,
  150. databaseName,
  151. extra: (
  152. <>
  153. <CopyButton
  154. label=""
  155. value={collectionName}
  156. className={classes.headerIcon}
  157. />
  158. <RefreshButton
  159. className={classes.headerIcon}
  160. onClick={async () => {
  161. await fetchCollection(collectionName);
  162. }}
  163. />
  164. </>
  165. ),
  166. });
  167. const setCollectionSearchParams = (params: SearchParams) => {
  168. setSearchParams(prevParams => {
  169. return prevParams.map(s => {
  170. if (
  171. s.collection.collection_name === params.collection.collection_name
  172. ) {
  173. return { ...params };
  174. }
  175. return s;
  176. });
  177. });
  178. };
  179. // render
  180. return (
  181. <section className={`page-wrapper ${classes.wrapper}`}>
  182. <section className={classes.tree}>
  183. {loading ? (
  184. <StatusIcon type={LoadingType.CREATING} />
  185. ) : (
  186. <DatabaseTree
  187. key="collections"
  188. collections={collections}
  189. database={database}
  190. params={params}
  191. />
  192. )}
  193. </section>
  194. {!collectionName && (
  195. <DatabasesTab databaseName={databaseName} tabClass={classes.tab} />
  196. )}
  197. {collectionName && (
  198. <CollectionTabs
  199. collectionPage={collectionPage}
  200. collectionName={collectionName}
  201. tabClass={classes.tab}
  202. searchParams={
  203. searchParams.find(
  204. s => s.collection.collection_name === collectionName
  205. )!
  206. }
  207. setSearchParams={setCollectionSearchParams}
  208. collections={collections}
  209. />
  210. )}
  211. </section>
  212. );
  213. };
  214. // Database tab pages
  215. const DatabasesTab = (props: {
  216. databaseName: string;
  217. tabClass: string; // tab class
  218. }) => {
  219. const { databaseName, tabClass } = props;
  220. const { t: collectionTrans } = useTranslation('collection');
  221. const dbTab: ITab[] = [
  222. {
  223. label: collectionTrans('collections'),
  224. component: <Collections />,
  225. path: `collections`,
  226. },
  227. ];
  228. const actionDbTab = dbTab.findIndex(t => t.path === databaseName);
  229. return (
  230. <RouteTabList
  231. tabs={dbTab}
  232. wrapperClass={tabClass}
  233. activeIndex={actionDbTab !== -1 ? actionDbTab : 0}
  234. />
  235. );
  236. };
  237. // Collection tab pages
  238. const CollectionTabs = (props: {
  239. collectionPage: string; // current collection page
  240. collectionName: string; // current collection name
  241. tabClass: string; // tab class
  242. collections: CollectionObject[]; // collections
  243. searchParams: SearchParams; // search params
  244. setSearchParams: (params: SearchParams) => void; // set search params
  245. }) => {
  246. // props
  247. const {
  248. collectionPage,
  249. collectionName,
  250. tabClass,
  251. collections,
  252. searchParams,
  253. setSearchParams,
  254. } = props;
  255. // context
  256. const { isManaged } = useContext(authContext);
  257. // i18n
  258. const { t: collectionTrans } = useTranslation('collection');
  259. const collection = collections.find(
  260. i => i.collection_name === collectionName
  261. ) as CollectionFullObject;
  262. // collection tabs
  263. const collectionTabs: ITab[] = [
  264. {
  265. label: collectionTrans('overviewTab'),
  266. component: <Overview />,
  267. path: `overview`,
  268. },
  269. {
  270. label: collectionTrans('searchTab'),
  271. component: (
  272. <Search
  273. collections={collections}
  274. collectionName={collectionName}
  275. searchParams={searchParams}
  276. setSearchParams={setSearchParams}
  277. />
  278. ),
  279. path: `search`,
  280. },
  281. {
  282. label: collectionTrans('dataTab'),
  283. component: (
  284. <Data collections={collections} collectionName={collectionName} />
  285. ),
  286. path: `data`,
  287. },
  288. {
  289. label: collectionTrans('partitionTab'),
  290. component: <Partitions />,
  291. path: `partitions`,
  292. },
  293. ];
  294. if (!isManaged) {
  295. collectionTabs.push(
  296. {
  297. label: collectionTrans('segmentsTab'),
  298. component: <Segments />,
  299. path: `segments`,
  300. },
  301. {
  302. label: collectionTrans('propertiesTab'),
  303. component: <Properties collection={collection} />,
  304. path: `properties`,
  305. }
  306. );
  307. }
  308. // get active collection tab
  309. const activeColTab = collectionTabs.findIndex(t => t.path === collectionPage);
  310. return (
  311. <RouteTabList
  312. tabs={collectionTabs}
  313. wrapperClass={tabClass}
  314. activeIndex={activeColTab !== -1 ? activeColTab : 0}
  315. />
  316. );
  317. };
  318. export default Databases;