From a1690d6c0fa96cf87437d025fb6d0c60a668b13e Mon Sep 17 00:00:00 2001 From: Vyacheslav Tumanov Date: Sat, 13 Jan 2024 13:56:43 +0500 Subject: [PATCH] Fix indexes, use DocIndexState for fields shown in search (#4341) * fix indexes, rework to use DocIndexState Signed-off-by: Vyacheslav Tumanov * fix icons for issues Signed-off-by: Vyacheslav Tumanov * remove commented lines Signed-off-by: Vyacheslav Tumanov --------- Signed-off-by: Vyacheslav Tumanov --- .../components/issues/IssueSearchIcon.svelte | 8 +-- server/core/src/indexer/fulltextPush.ts | 12 +++- server/core/src/indexer/types.ts | 2 +- server/core/src/mapper.ts | 61 +++++++++++++------ server/core/src/types.ts | 1 + 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/plugins/tracker-resources/src/components/issues/IssueSearchIcon.svelte b/plugins/tracker-resources/src/components/issues/IssueSearchIcon.svelte index 02a5ce97fe..93286bbb6e 100644 --- a/plugins/tracker-resources/src/components/issues/IssueSearchIcon.svelte +++ b/plugins/tracker-resources/src/components/issues/IssueSearchIcon.svelte @@ -20,11 +20,11 @@ import IssueStatusIcon from './IssueStatusIcon.svelte' - export let status: string - export let space: Ref + export let status: string | string[] + export let space: Ref | Ref[] - $: st = $statusStore.byId.get(status as Ref) - $: spaceProject = space as Ref + $: st = $statusStore.byId.get(Array.isArray(status) ? (status[0] as Ref) : (status as Ref)) + $: spaceProject = Array.isArray(space) ? (space[0] as Ref) : (space as Ref) {#if st} diff --git a/server/core/src/indexer/fulltextPush.ts b/server/core/src/indexer/fulltextPush.ts index 431665a7f1..37b7fad30e 100644 --- a/server/core/src/indexer/fulltextPush.ts +++ b/server/core/src/indexer/fulltextPush.ts @@ -178,7 +178,7 @@ export class FullTextPushStage implements FullTextPipelineStage { } } } - + const parentDoc: DocIndexState | undefined = undefined if (doc.attachedToClass != null && doc.attachedTo != null) { const propagate: Ref>[] = collectPropagate(pipeline, doc.attachedToClass) if (propagate.some((it) => pipeline.hierarchy.isDerived(doc.objectClass, it))) { @@ -210,13 +210,21 @@ export class FullTextPushStage implements FullTextPipelineStage { } } } + const [spaceDoc] = await metrics.with( + 'find-space', + {}, + async (ctx) => + await this.dbStorage.findAll(ctx, core.class.DocIndexState, { + _id: doc.space as any as Ref + }) + ) const allAttributes = pipeline.hierarchy.getAllAttributes(elasticDoc._class) // Include child ref attributes await this.indexRefAttributes(allAttributes, doc, elasticDoc, metrics) - await updateDocWithPresenter(pipeline.hierarchy, elasticDoc) + await updateDocWithPresenter(pipeline.hierarchy, doc, elasticDoc, { parentDoc, spaceDoc }) this.checkIntegrity(elasticDoc) bulk.push(elasticDoc) diff --git a/server/core/src/indexer/types.ts b/server/core/src/indexer/types.ts index bc1c930bcb..44ad301eda 100644 --- a/server/core/src/indexer/types.ts +++ b/server/core/src/indexer/types.ts @@ -107,4 +107,4 @@ export const fieldStateId = 'fld-v11' /** * @public */ -export const fullTextPushStageId = 'fts-v7' +export const fullTextPushStageId = 'fts-v8' diff --git a/server/core/src/mapper.ts b/server/core/src/mapper.ts index 38e1304626..9d777d91a0 100644 --- a/server/core/src/mapper.ts +++ b/server/core/src/mapper.ts @@ -1,8 +1,8 @@ -import { Hierarchy, Ref, RefTo, Class, Doc, SearchResultDoc, docKey } from '@hcengineering/core' +import { Class, Doc, DocIndexState, docKey, Hierarchy, Ref, RefTo, SearchResultDoc } from '@hcengineering/core' import { getResource } from '@hcengineering/platform' import plugin from './plugin' -import { IndexedDoc, SearchPresenter, ClassSearchConfigProps, SearchScoring } from './types' +import { ClassSearchConfigProps, IndexedDoc, SearchPresenter, SearchScoring } from './types' interface IndexedReader { get: (attribute: string) => any @@ -13,22 +13,26 @@ interface IndexedReader { function createIndexedReader ( _class: Ref>, hierarchy: Hierarchy, - doc: IndexedDoc, + doc: DocIndexState, + otherDocs?: Record, refAttribute?: string ): IndexedReader { return { get: (attr: string) => { const realAttr = hierarchy.findAttribute(_class, attr) if (realAttr !== undefined) { - return doc[docKey(attr, { refAttribute, _class: realAttr.attributeOf })] + return doc.attributes[docKey(attr, { refAttribute, _class: realAttr.attributeOf })] } return undefined }, getDoc: (attr: string) => { const realAttr = hierarchy.findAttribute(_class, attr) if (realAttr !== undefined) { - const refAtrr = realAttr.type as RefTo - return createIndexedReader(refAtrr.to, hierarchy, doc, docKey(attr, { _class })) + const anotherDoc = otherDocs?.[attr] + if (anotherDoc !== undefined) { + const refAtrr = realAttr.type as RefTo + return createIndexedReader(refAtrr.to, hierarchy, anotherDoc, otherDocs, docKey(attr, { _class })) + } } return undefined } @@ -66,13 +70,24 @@ function findSearchPresenter (hierarchy: Hierarchy, _class: Ref>): Se /** * @public */ -export async function updateDocWithPresenter (hierarchy: Hierarchy, doc: IndexedDoc): Promise { - const searchPresenter = findSearchPresenter(hierarchy, doc._class) +export async function updateDocWithPresenter ( + hierarchy: Hierarchy, + doc: DocIndexState, + elasticDoc: IndexedDoc, + refDocs: { + parentDoc: DocIndexState | undefined + spaceDoc: DocIndexState | undefined + } +): Promise { + const searchPresenter = findSearchPresenter(hierarchy, elasticDoc._class) if (searchPresenter === undefined) { return } - const reader = createIndexedReader(doc._class, hierarchy, doc) + const reader = createIndexedReader(elasticDoc._class, hierarchy, doc, { + space: refDocs.spaceDoc, + attachedTo: refDocs.parentDoc + }) const props = [ { @@ -80,7 +95,7 @@ export async function updateDocWithPresenter (hierarchy: Hierarchy, doc: Indexed config: searchPresenter.searchConfig.title, provider: searchPresenter.getSearchTitle } - ] + ] as any[] if (searchPresenter.searchConfig.shortTitle !== undefined) { props.push({ @@ -90,20 +105,29 @@ export async function updateDocWithPresenter (hierarchy: Hierarchy, doc: Indexed }) } + if (searchPresenter.searchConfig.iconConfig !== undefined) { + props.push({ + name: 'searchIcon', + config: searchPresenter.searchConfig.iconConfig + }) + } + for (const prop of props) { let value - if (typeof prop.config === 'string') { - value = reader.get(prop.config) - } else if (prop.config.tmpl !== undefined) { + if (prop.config.tmpl !== undefined) { const tmpl = prop.config.tmpl const renderProps = readAndMapProps(reader, prop.config.props) value = fillTemplate(tmpl, renderProps) + } else if (typeof prop.config === 'string') { + value = reader.get(prop.config) } else if (prop.provider !== undefined) { - const func = await getResource(prop.provider) + const func = (await getResource(prop.provider)) as any const renderProps = readAndMapProps(reader, prop.config.props) - value = func(hierarchy, { _class: doc._class, ...renderProps }) + value = func(hierarchy, { _class: elasticDoc._class, ...renderProps }) + } else if (prop.name === 'searchIcon') { + value = readAndMapProps(reader, prop.config.props) } - doc[prop.name] = value + elasticDoc[prop.name] = value } } @@ -126,6 +150,7 @@ export function mapSearchResultDoc (hierarchy: Hierarchy, raw: IndexedDoc): Sear id: raw.id, title: raw.searchTitle, shortTitle: raw.searchShortTitle, + iconProps: raw.searchIcon, doc: { _id: raw.id, _class: raw._class @@ -138,10 +163,6 @@ export function mapSearchResultDoc (hierarchy: Hierarchy, raw: IndexedDoc): Sear } if (searchPresenter?.searchConfig.iconConfig !== undefined) { doc.iconComponent = searchPresenter.searchConfig.iconConfig.component - doc.iconProps = readAndMapProps( - createIndexedReader(raw._class, hierarchy, raw), - searchPresenter.searchConfig.iconConfig.props - ) } return doc diff --git a/server/core/src/types.ts b/server/core/src/types.ts index b3cf6aa717..07c7a8a298 100644 --- a/server/core/src/types.ts +++ b/server/core/src/types.ts @@ -165,6 +165,7 @@ export interface IndexedDoc { attachedToClass?: Ref> searchTitle?: string searchShortTitle?: string + searchIcon?: any [key: string]: any }