diff --git a/package-lock.json b/package-lock.json
index f4d31f6e7..aeb6dedea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3598,9 +3598,9 @@
}
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
diff --git a/src/components/Collections/SearchQuality/Instruction.mdx b/src/components/Collections/SearchQuality/Instruction.mdx
new file mode 100644
index 000000000..a284503ac
--- /dev/null
+++ b/src/components/Collections/SearchQuality/Instruction.mdx
@@ -0,0 +1,44 @@
+### Example Output:
+`
+[18/10/2024, 01:51:38] Point ID 1(1/100) precision@10: 0.8
+(search time exact: 30ms, regular: 5ms)
+`
+
+### Explanation
+
+This output provides a comparison between:
+- **Exact Search** (full kNN)
+- **Approximate Search** (using ANN - Approximate Nearest Neighbor)
+
+**precision@10: 0.8**
+- Out of the top 10 results returned by the exact search, 8 were also found in the ANN search.
+
+**Search Time Comparison**
+- Exact search: 30ms
+- ANN search: 5ms (faster but with minor accuracy loss)
+
+---
+
+### Tuning the HNSW Algorithm (Advanced Mode)
+
+- **"hnsw_ef" parameter**: Controls how many neighbors to consider during a search.
+ - Increasing **hnsw_ef** improves precision but may slow down the search.
+
+---
+
+### Practical Use
+
+The ANN search (with HNSW) is significantly faster (5ms vs. 30ms) but may have slight accuracy trade-offs.
+**Tip**: Adjust **hnsw_ef** in advanced mode to balance speed and accuracy.
+
+---
+
+### Additional Tuning Parameters (set in collection configuration)
+
+1. **"m" Parameter**
+ - Defines the number of edges per node in the graph.
+ - A higher **m** value improves accuracy but increases memory usage.
+
+2. **"ef_construct" Parameter**
+ - Sets the number of neighbors considered during index creation.
+ - Higher values increase precision but lengthen indexing time.
\ No newline at end of file
diff --git a/src/components/Collections/SearchQuality/SearchQuality.jsx b/src/components/Collections/SearchQuality/SearchQuality.jsx
index 2ba44ca69..e512e6820 100644
--- a/src/components/Collections/SearchQuality/SearchQuality.jsx
+++ b/src/components/Collections/SearchQuality/SearchQuality.jsx
@@ -4,44 +4,41 @@ import { getSnackbarOptions } from '../../Common/utils/snackbarOptions';
import { useClient } from '../../../context/client-context';
import SearchQualityPanel from './SearchQualityPanel';
import { useSnackbar } from 'notistack';
-import { Box, Card, CardHeader } from '@mui/material';
+import { Box, Card, CardContent, CardHeader, Dialog, Grid, IconButton, Tooltip } from '@mui/material';
import { CopyButton } from '../../Common/CopyButton';
import { bigIntJSON } from '../../../common/bigIntJSON';
import EditorCommon from '../../EditorCommon';
import _ from 'lodash';
+import * as Instruction from './Instruction.mdx';
+import { mdxComponents } from '../../InteractiveTutorial/MdxComponents/MdxComponents';
+import { Close, OpenInFull } from '@mui/icons-material';
const SearchQuality = ({ collectionName }) => {
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const { client } = useClient();
const [collection, setCollection] = React.useState(null);
const [log, setLog] = React.useState('');
+ const [open, setOpen] = React.useState(false);
const handleLogUpdate = (newLog) => {
const date = new Date().toLocaleString();
newLog = `[${date}] ${newLog}`;
- setLog((prevLog) => {
- return newLog + '\n' + prevLog;
- });
+ setLog((prevLog) => newLog + '\n' + prevLog);
};
const clearLogs = () => {
- setLog('');
+ setLog(' ');
};
useEffect(() => {
client
.getCollection(collectionName)
- .then((res) => {
- setCollection(() => {
- return { ...res };
- });
- })
+ .then((res) => setCollection({ ...res }))
.catch((err) => {
enqueueSnackbar(err.message, getSnackbarOptions('error', closeSnackbar));
});
}, []);
- // Check that collection.config.params.vectors?.size exists and integer
const isNamedVectors = !collection?.config?.params.vectors?.size && _.isObject(collection?.config?.params?.vectors);
let vectors = {};
if (collection) {
@@ -49,44 +46,142 @@ const SearchQuality = ({ collectionName }) => {
}
return (
- <>
- {collection?.config?.params?.vectors && (
-
- )}
+
+
+ {collection?.config?.params?.vectors && (
+
+ )}
+
+
+ {
+ setOpen(true);
+ }}
+ >
+
+
+
+ }
+ />
+
+
+
+
+
-
-
+ }
- />
-
-
+ } />
+
+
+
+
+
+
+
);
};
diff --git a/src/components/Collections/SearchQuality/SearchQualityPanel.jsx b/src/components/Collections/SearchQuality/SearchQualityPanel.jsx
index c643afbc6..7e88ffcd8 100644
--- a/src/components/Collections/SearchQuality/SearchQualityPanel.jsx
+++ b/src/components/Collections/SearchQuality/SearchQualityPanel.jsx
@@ -9,11 +9,13 @@ import {
TableHead,
TableRow,
Tooltip,
- IconButton,
+ Button,
FormControlLabel,
Switch,
CardContent,
LinearProgress,
+ Box,
+ IconButton,
} from '@mui/material';
import { CopyButton } from '../../Common/CopyButton';
import { bigIntJSON } from '../../../common/bigIntJSON';
@@ -42,23 +44,35 @@ const VectorTableRow = ({ vectorObj, name, onCheckIndexQuality, precision, isInP
- {isInProgress && }
- {!isInProgress && (
- <>
+ {isInProgress === name && }
+ {isInProgress !== name && (
+
- {precision ? `${precision * 100}%` : '—'}
+ {precision ? `${precision * 100}%` : null}
-
-
-
+ {precision ? (
+
+
+
+ ) : (
+ }
+ onClick={onCheckIndexQuality}
+ data-testid="index-quality-check-button"
+ >
+ Check Quality
+
+ )}
- >
+
)}
@@ -70,7 +84,7 @@ VectorTableRow.propTypes = {
name: PropTypes.string,
onCheckIndexQuality: PropTypes.func,
precision: PropTypes.number,
- isInProgress: PropTypes.bool,
+ isInProgress: PropTypes.string,
};
const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo, ...other }) => {
@@ -87,7 +101,7 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
});
const [advancedMod, setAdvancedMod] = useState(false);
- const [inProgress, setInProgress] = useState(false);
+ const [inProgress, setInProgress] = useState(null);
const [code, setCode] = useState(`
// Run this code to estimate search quality versus exact search
@@ -168,10 +182,14 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
return <>No vectors>;
}
- const onCheckIndexQuality = async ({ using = '', limit = 10, params = null, filter = null, timeout }) => {
- setInProgress(true);
-
+ const onCheckIndexQuality = async ({ using = '', limit = 10, params = null, filter = null, timeout }, controller) => {
+ setInProgress(using);
clearLogsFoo && clearLogsFoo();
+ if (vectorsNames && !vectorsNames.includes(using)) {
+ loggingFoo && loggingFoo('Vector field name not found\n');
+ setInProgress(null);
+ return;
+ }
const precisions = [];
try {
const scrollResult = await client.scroll(collectionName, {
@@ -188,6 +206,10 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
loggingFoo && loggingFoo('Starting measuring quality on ' + total + ' requests for ' + using || '---');
for (let idx = 0; idx < total; idx++) {
+ if (controller.signal.aborted) {
+ loggingFoo && loggingFoo('Previous operation cancelled \n');
+ break;
+ }
const pointId = pointIds[idx];
const precision = await checkIndexPrecision(
client,
@@ -215,7 +237,7 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
Math.sqrt(precisions.reduce((x, val) => x + (val - avgPrecision) ** 2, 0) / precisions.length)
);
- loggingFoo('Mean precision@' + limit + ' for collection: ' + avgPrecision + ' ± ' + stdDev);
+ loggingFoo('Mean precision@' + limit + ' for collection: ' + avgPrecision + ' ± ' + stdDev + '\n');
setPrecision((prev) => {
return {
@@ -224,24 +246,32 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
};
});
- setInProgress(false);
+ setInProgress(null);
} catch (e) {
- setInProgress(false);
+ setInProgress(null);
console.error(e);
loggingFoo && loggingFoo(JSON.stringify(e));
}
};
const handleRunCode = async (qulityCheckParams) => {
- onCheckIndexQuality(qulityCheckParams);
+ if (window.currentController) {
+ window.currentController.abort();
+ }
+ const controller = new AbortController();
+ window.currentController = controller;
+
+ onCheckIndexQuality(qulityCheckParams, controller);
};
return (
- Search Quality
+
+
+ Search Quality
+
setAdvancedMod(!advancedMod)} size="small" />}
@@ -251,58 +281,64 @@ const SearchQualityPanel = ({ collectionName, vectors, loggingFoo, clearLogsFoo,
}
/>
- >
+
}
variant="heading"
sx={{
flexGrow: 1,
}}
- action={
- <>
-
- >
- }
+ action={}
/>
+
{!advancedMod && (
-
-
-
-
-
- Vector Name
-
-
-
-
- Size
-
-
-
-
- Distance
-
-
-
-
- Precision
-
-
-
-
+
+
+
+
+
+
+ Vector Name
+
+
+
+
+ Size
+
+
+
+
+ Distance
+
+
+
+
+ Precision
+
+
+
+
-
- {Object.keys(vectors).map((vectorName) => (
- onCheckIndexQuality({ using: vectorName })}
- precision={precision ? precision[vectorName] : null}
- key={vectorName}
- isInProgress={inProgress}
- />
- ))}
-
-
+
+ {Object.keys(vectors).map((vectorName) => (
+ {
+ if (window.currentController) {
+ window.currentController.abort();
+ }
+ const controller = new AbortController();
+ window.currentController = controller;
+ onCheckIndexQuality({ using: vectorName }, controller);
+ }}
+ precision={precision ? precision[vectorName] : null}
+ key={vectorName}
+ isInProgress={inProgress}
+ />
+ ))}
+
+
+
)}
{advancedMod && (
diff --git a/src/components/Collections/SearchQuality/check-index-precision.js b/src/components/Collections/SearchQuality/check-index-precision.js
index 2208269d0..86d60cf54 100644
--- a/src/components/Collections/SearchQuality/check-index-precision.js
+++ b/src/components/Collections/SearchQuality/check-index-precision.js
@@ -61,11 +61,11 @@ export const checkIndexPrecision = async (
limit +
': ' +
precision +
- ' (search time exact: ' +
+ '\n (search time exact: ' +
exactSearchElapsed +
'ms, regular: ' +
searchElapsed +
- 'ms)'
+ 'ms)\n'
);
return precision;
diff --git a/src/components/EditorCommon/index.jsx b/src/components/EditorCommon/index.jsx
index 4df8d1eb9..7445a79b1 100644
--- a/src/components/EditorCommon/index.jsx
+++ b/src/components/EditorCommon/index.jsx
@@ -22,7 +22,7 @@ window.MonacoEnvironment = {
loader.config({ monaco });
-const EditorCommon = ({ beforeMount, customHeight, ...props }) => {
+const EditorCommon = ({ beforeMount, customHeight, paddingBottom = 0, ...props }) => {
const monacoRef = useRef(null);
const editorWrapper = useRef(null);
const theme = useTheme();
@@ -55,7 +55,7 @@ const EditorCommon = ({ beforeMount, customHeight, ...props }) => {
if (customHeight) {
return;
}
- setEditorHeight(height - editorWrapper.current?.offsetTop);
+ setEditorHeight(height - editorWrapper.current?.offsetTop - paddingBottom);
}, [height, editorWrapper]);
return (
@@ -74,6 +74,7 @@ EditorCommon.propTypes = {
height: PropTypes.string,
beforeMount: PropTypes.func,
customHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ paddingBottom: PropTypes.number,
...Editor.propTypes,
};