Query.tsx 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import { FC, useEffect, useState, useRef, useMemo, useContext } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { rootContext } from '../../context/Root';
  4. import EmptyCard from '../../components/cards/EmptyCard';
  5. import icons from '../../components/icons/Icons';
  6. import CustomButton from '../../components/customButton/CustomButton';
  7. import MilvusGrid from '../../components/grid/Grid';
  8. import { ToolBarConfig } from '../../components/grid/Types';
  9. import { getQueryStyles } from './Styles';
  10. import Filter from '../../components/advancedSearch';
  11. import { CollectionHttp } from '../../http/Collection';
  12. import { FieldHttp } from '../../http/Field';
  13. import { usePaginationHook } from '../../hooks/Pagination';
  14. import CopyButton from '../../components/advancedSearch/CopyButton';
  15. import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
  16. import CustomToolBar from '../../components/grid/ToolBar';
  17. const Query: FC<{
  18. collectionName: string;
  19. }> = ({ collectionName }) => {
  20. const [fields, setFields] = useState<any[]>([]);
  21. const [expression, setExpression] = useState('');
  22. const [tableLoading, setTableLoading] = useState<any>();
  23. const [queryResult, setQueryResult] = useState<any>();
  24. const [selectedDatas, setSelectedDatas] = useState<any[]>([]);
  25. const [primaryKey, setPrimaryKey] = useState<string>('');
  26. const { setDialog, handleCloseDialog, openSnackBar } =
  27. useContext(rootContext);
  28. const VectorSearchIcon = icons.vectorSearch;
  29. const ResetIcon = icons.refresh;
  30. const { t: dialogTrans } = useTranslation('dialog');
  31. const { t: successTrans } = useTranslation('success');
  32. const { t: searchTrans } = useTranslation('search');
  33. const { t: collectionTrans } = useTranslation('collection');
  34. const { t: btnTrans } = useTranslation('btn');
  35. const { t: commonTrans } = useTranslation();
  36. const copyTrans = commonTrans('copy');
  37. const classes = getQueryStyles();
  38. // Format result list
  39. const queryResultMemo = useMemo(
  40. () =>
  41. queryResult?.map((resultItem: { [key: string]: any }) => {
  42. // Iterate resultItem keys, then format vector(array) items.
  43. const tmp = Object.keys(resultItem).reduce(
  44. (prev: { [key: string]: any }, item: string) => {
  45. if (Array.isArray(resultItem[item])) {
  46. const list2Str = `[${resultItem[item]}]`;
  47. prev[item] = (
  48. <div className={classes.vectorTableCell}>
  49. <div>{list2Str}</div>
  50. <CopyButton
  51. label={copyTrans.label}
  52. value={list2Str}
  53. className={classes.copyBtn}
  54. />
  55. </div>
  56. );
  57. } else {
  58. prev[item] = resultItem[item];
  59. }
  60. return prev;
  61. },
  62. {}
  63. );
  64. return tmp;
  65. }),
  66. [queryResult, classes.vectorTableCell, classes.copyBtn, copyTrans.label]
  67. );
  68. const {
  69. pageSize,
  70. handlePageSize,
  71. currentPage,
  72. handleCurrentPage,
  73. total,
  74. data: result,
  75. order,
  76. orderBy,
  77. handleGridSort,
  78. } = usePaginationHook(queryResultMemo || []);
  79. const handlePageChange = (e: any, page: number) => {
  80. handleCurrentPage(page);
  81. };
  82. const getFields = async (collectionName: string) => {
  83. const schemaList = await FieldHttp.getFields(collectionName);
  84. const nameList = schemaList.map(v => ({
  85. name: v.name,
  86. type: v.data_type.includes('Int') ? 'int' : 'float',
  87. }));
  88. const primaryKey =
  89. schemaList.find(v => v._isPrimaryKey === true)?._fieldName || '';
  90. setPrimaryKey(primaryKey);
  91. setFields(nameList);
  92. };
  93. // Get fields at first or collection name changed.
  94. useEffect(() => {
  95. collectionName && getFields(collectionName);
  96. }, [collectionName]);
  97. const filterRef = useRef();
  98. const handleFilterReset = () => {
  99. const currentFilter: any = filterRef.current;
  100. currentFilter?.getReset();
  101. setExpression('');
  102. setTableLoading(null);
  103. setQueryResult(null);
  104. };
  105. const handleFilterSubmit = (expression: string) => {
  106. setExpression(expression);
  107. setQueryResult(null);
  108. };
  109. const handleQuery = async () => {
  110. setTableLoading(true);
  111. try {
  112. const res = await CollectionHttp.queryData(collectionName, {
  113. expr: expression,
  114. output_fields: fields.map(i => i.name),
  115. });
  116. const result = res.data;
  117. setQueryResult(result);
  118. } catch (err) {
  119. setQueryResult([]);
  120. } finally {
  121. setTableLoading(false);
  122. }
  123. };
  124. const handleSelectChange = (value: any) => {
  125. setSelectedDatas(value);
  126. };
  127. const handleDelete = async () => {
  128. await CollectionHttp.deleteEntities(collectionName, {
  129. expr: `${primaryKey} in [${selectedDatas.map(v => v.id).join(',')}]`,
  130. });
  131. handleCloseDialog();
  132. openSnackBar(successTrans('delete', { name: collectionTrans('entites') }));
  133. handleQuery();
  134. };
  135. const toolbarConfigs: ToolBarConfig[] = [
  136. {
  137. type: 'iconBtn',
  138. onClick: () => {
  139. setDialog({
  140. open: true,
  141. type: 'custom',
  142. params: {
  143. component: (
  144. <DeleteTemplate
  145. label={btnTrans('delete')}
  146. title={dialogTrans('deleteTitle', {
  147. type: collectionTrans('entites'),
  148. })}
  149. text={collectionTrans('deleteDataWarning')}
  150. handleDelete={handleDelete}
  151. />
  152. ),
  153. },
  154. });
  155. },
  156. label: collectionTrans('delete'),
  157. icon: 'delete',
  158. // tooltip: collectionTrans('deleteTooltip'),
  159. disabledTooltip: collectionTrans('deleteTooltip'),
  160. disabled: () => selectedDatas.length === 0,
  161. },
  162. ];
  163. return (
  164. <div className={classes.root}>
  165. <CustomToolBar toolbarConfigs={toolbarConfigs} />
  166. <div className={classes.toolbar}>
  167. <div className="left">
  168. <div>{`${expression || collectionTrans('exprPlaceHolder')}`}</div>
  169. <Filter
  170. ref={filterRef}
  171. title="Advanced Filter"
  172. fields={fields}
  173. filterDisabled={false}
  174. onSubmit={handleFilterSubmit}
  175. showTitle={false}
  176. showTooltip={false}
  177. />
  178. </div>
  179. <div className="right">
  180. <CustomButton className="btn" onClick={handleFilterReset}>
  181. <ResetIcon classes={{ root: 'icon' }} />
  182. {btnTrans('reset')}
  183. </CustomButton>
  184. <CustomButton
  185. variant="contained"
  186. disabled={!expression}
  187. onClick={() => handleQuery()}
  188. >
  189. {btnTrans('query')}
  190. </CustomButton>
  191. </div>
  192. </div>
  193. {tableLoading || queryResult?.length ? (
  194. <MilvusGrid
  195. toolbarConfigs={[]}
  196. colDefinitions={fields.map(i => ({
  197. id: i.name,
  198. align: 'left',
  199. disablePadding: false,
  200. label: i.name,
  201. }))}
  202. primaryKey={fields.find(i => i.is_primary_key)?.name}
  203. openCheckBox={true}
  204. isLoading={!!tableLoading}
  205. rows={result}
  206. rowCount={total}
  207. selected={selectedDatas}
  208. setSelected={handleSelectChange}
  209. page={currentPage}
  210. onChangePage={handlePageChange}
  211. rowsPerPage={pageSize}
  212. setRowsPerPage={handlePageSize}
  213. orderBy={orderBy}
  214. order={order}
  215. handleSort={handleGridSort}
  216. />
  217. ) : (
  218. <EmptyCard
  219. wrapperClass={`page-empty-card ${classes.emptyCard}`}
  220. icon={<VectorSearchIcon />}
  221. text={
  222. queryResult?.length === 0
  223. ? searchTrans('empty')
  224. : collectionTrans('startTip')
  225. }
  226. />
  227. )}
  228. </div>
  229. );
  230. };
  231. export default Query;