Skip to content

Commit

Permalink
fix(search): speedup query, search result balancing relevance and rec…
Browse files Browse the repository at this point in the history
…ency
  • Loading branch information
thesophiaxu committed Mar 2, 2022
1 parent b06d2ac commit 8cd0855
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 40 deletions.
11 changes: 10 additions & 1 deletion packages/unigraph-dev-backend/src/dgraphClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getRandomInt } from 'unigraph-dev-common/lib/utils/utils';
import fetch from 'node-fetch';
import { getAsyncLock, withLock } from './asyncManager';
import { perfLogStartDbTransaction, perfLogAfterDbTransaction } from './logging';
import { makeSearchQuery } from './search';
import { makeNameQuery, makeSearchQuery } from './search';

export type UnigraphUpsert = {
queries: string[];
Expand Down Expand Up @@ -348,6 +348,15 @@ export default class DgraphClient {
return {results: ((searchOptions?.noPrimitives || searchOptions?.resultsOnly) ? [] : res[0]) as any[], entities: res[res.length - 1] as any[]};
}

async getSearchNameResults(query: string, hideHidden?: boolean) { // Only do keyword matches
const finalQuery = makeNameQuery(query, hideHidden)
// console.log(finalQuery)
perfLogStartDbTransaction ()
const res = (await this.queryDgraph(finalQuery));
perfLogAfterDbTransaction ()
return {entities: res[1] as any[], top: res[0] as any[]};
}

async getPackages() {
return this.queryData<any[]>(`
query findByName() {
Expand Down
3 changes: 3 additions & 0 deletions packages/unigraph-dev-backend/src/localUnigraphApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,9 @@ export function getLocalUnigraphAPI(
throw Error('Not available in server side');
},
getSearchResults: async (query, display: any, hops, searchOptions) => {
// eslint-disable-next-line no-return-await
if (display === 'name')
return (await client.getSearchNameResults(query[0].value, searchOptions?.hideHidden)) as any;
let res: { results: any[]; entities: any[] } = {
results: [],
entities: [],
Expand Down
45 changes: 40 additions & 5 deletions packages/unigraph-dev-backend/src/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,51 @@ const getQueryHead = (qual: string, options: string, filter: string, showHidden:
showHidden ? '' : 'AND (NOT eq(<_hide>, true))'
})`;

// TODO: move this to more flexible query
export const makeNameQuery = (name: string, hideHidden?: boolean) => `query {
uhops0 as var(func: alloftext(<_value.%>, "${name.replace(/"/g, '\\"')}"))
var(func: uid(uhops0)) {
<unigraph.origin> @filter(type(Entity)) {
uhops1 as uid
}
}
filteredEntity as var(func: uid(uhops1)) @filter((NOT type(Deleted)) AND NOT (uid(uhops0)) AND (NOT eq(<_propertyType>, "inheritance")) ${
hideHidden ? 'AND (NOT eq(<_hide>, true))' : ''
} AND has(_updatedAt)) {
incoming as count(<unigraph.origin>) @filter(type(Entity))
}
result_top(func: uid(filteredEntity), orderdesc: val(incoming)) @filter(gt(val(incoming), 5)) {uid _updatedAt
type {
unigraph.id
}
unigraph.id
unigraph.indexes {
expand(_userpredicate_) { uid expand(_userpredicate_) { uid expand(_userpredicate_) {
uid expand(_userpredicate_) { uid expand(_userpredicate_) { uid expand(_userpredicate_) } } } } }
}}
result(func: uid(filteredEntity), orderdesc: _updatedAt, first: 200) @filter(has(<unigraph.indexes>)) {
uid
_updatedAt
type {
unigraph.id
}
unigraph.indexes {
expand(_userpredicate_) { uid expand(_userpredicate_) { uid expand(_userpredicate_) {
uid expand(_userpredicate_) { uid expand(_userpredicate_) { uid expand(_userpredicate_) } } } } }
}
}
}`;

const resQueries = {
indexes: (qual: string, options: string, filter: string, showHidden: boolean, _: string) => `var${getQueryHead(
qual,
'',
filter,
showHidden,
)} {incoming as sum(val(incoming2))
<unigraph.origin> @filter(type(Entity)) {
incoming2 as count(<unigraph.origin>) @filter(type(Entity) AND uid(uhops0, uhops1))
}
)} {
incoming as sum(<unigraph.origin>) @filter(type(Entity))
}
${getQueryHead(
`${qual}, orderdesc: val(incoming), orderdesc: _updatedAt`,
Expand Down Expand Up @@ -126,7 +161,7 @@ export const makeSearchQuery = (
${qhops.join('\n')}
${resultQuery}
}`;
// console.log(fq);
console.log(fq);
return fq;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,43 @@ import { parseQuery } from './UnigraphSearch';
import { setSearchPopup } from '../../examples/notes/searchPopup';
import { SearchPopupState } from '../../global.d';

const ResultDisplay = ({ el }: any) => {
return (
<div style={{ display: 'inline-flex' }}>
<div
style={{
minHeight: '18px',
minWidth: '18px',
height: '18px',
width: '18px',
alignSelf: 'center',
marginRight: '3px',
opacity: 0.54,
backgroundImage: `url("data:image/svg+xml,${
window.unigraph.getNamespaceMap?.()?.[el.type]?._icon
}")`,
}}
/>
<Typography style={{ color: 'grey', marginLeft: '2px' }}>
{window.unigraph.getNamespaceMap?.()?.[el.type]?._name}
</Typography>
<Divider
variant="middle"
orientation="vertical"
style={{
height: '16px',
alignSelf: 'center',
marginLeft: '16px',
marginRight: '16px',
marginTop: '0px',
marginBottom: '0px',
}}
/>
<Typography variant="body1">{el.name}</Typography>
</div>
);
};

export function InlineSearch() {
const ctxMenuState: AppState<Partial<SearchPopupState>> = window.unigraph.getState('global/searchPopup');

Expand All @@ -17,7 +54,7 @@ export function InlineSearch() {
_.throttle((key: string) => {
if (key !== undefined && key.length > 1) {
window.unigraph
.getSearchResults(parseQuery(key as any) as any, 'indexes', 2, {
.getSearchResults(parseQuery(key as any) as any, 'name', 2, {
limit: 500,
noPrimitives: true,
hideHidden: ctxMenuState.value.hideHidden,
Expand All @@ -31,8 +68,17 @@ export function InlineSearch() {
type: el.type['unigraph.id'],
}))
.filter((el: any) => el.name);
const topResults = res.top
.filter((el: any) => el.type['unigraph.id'] !== '$/schema/embed_block')
.map((el: any) => ({
name: new UnigraphObject(el['unigraph.indexes']?.name || {}).as('primitive'),
uid: el.uid,
type: el.type['unigraph.id'],
}))
.filter((el: any) => el.name);
console.log(topResults);
if (window.unigraph.getState('global/searchPopup').value.search === key)
setSearchResults(results);
setSearchResults([...topResults, ...results]);
});
}
}, 500),
Expand All @@ -43,6 +89,7 @@ export function InlineSearch() {
React.useEffect(() => ctxMenuState.subscribe((v) => setState(v)), []);

const [searchResults, setSearchResults] = React.useState<any[]>([]);
const [topResults, setTopResults] = React.useState<any[]>([]);
React.useEffect(() => {
if (!state.show) setSearchResults([]);
else setCurrentAction(0);
Expand All @@ -64,38 +111,7 @@ export function InlineSearch() {
},
]),
...searchResults.map((el: any) => [
<div style={{ display: 'inline-flex' }}>
<div
style={{
minHeight: '18px',
minWidth: '18px',
height: '18px',
width: '18px',
alignSelf: 'center',
marginRight: '3px',
opacity: 0.54,
backgroundImage: `url("data:image/svg+xml,${
window.unigraph.getNamespaceMap?.()?.[el.type]?._icon
}")`,
}}
/>
<Typography style={{ color: 'grey', marginLeft: '2px' }}>
{window.unigraph.getNamespaceMap?.()?.[el.type]?._name}
</Typography>
<Divider
variant="middle"
orientation="vertical"
style={{
height: '16px',
alignSelf: 'center',
marginLeft: '16px',
marginRight: '16px',
marginTop: '0px',
marginBottom: '0px',
}}
/>
<Typography variant="body1">{el.name}</Typography>
</div>,
<ResultDisplay el={el} />,
(ev: any) => {
ev.preventDefault();
ev.stopPropagation();
Expand Down

0 comments on commit 8cd0855

Please sign in to comment.