From d340b55af8300345aa881394394f27dc21e9f9c6 Mon Sep 17 00:00:00 2001 From: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> Date: Wed, 15 Dec 2021 15:16:19 +0600 Subject: [PATCH] Index attributes of AttachedTo object with elastic (#628) Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> --- server/core/src/fulltext.ts | 82 +++++++++++++++++++++++++++++++++---- server/core/src/types.ts | 2 +- server/front/src/app.ts | 4 +- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/server/core/src/fulltext.ts b/server/core/src/fulltext.ts index 1a6dd7950c..0b82a4a064 100644 --- a/server/core/src/fulltext.ts +++ b/server/core/src/fulltext.ts @@ -14,7 +14,19 @@ // limitations under the License. // -import core, { Hierarchy, AnyAttribute, Storage, DocumentQuery, FindOptions, FindResult, TxProcessor, TxMixin, TxPutBag, TxRemoveDoc } from '@anticrm/core' +import core, { + Hierarchy, + AnyAttribute, + Storage, + DocumentQuery, + FindOptions, + FindResult, + TxProcessor, + TxMixin, + TxPutBag, + TxRemoveDoc, + Collection +} from '@anticrm/core' import type { AttachedDoc, TxUpdateDoc, TxCreateDoc, Doc, Ref, Class, Obj, TxResult } from '@anticrm/core' import type { IndexedDoc, FullTextAdapter, WithFind } from './types' @@ -47,11 +59,21 @@ export class FullTextIndex extends TxProcessor implements Storage { throw new Error('Method not implemented.') } - async findAll (_class: Ref>, query: DocumentQuery, options?: FindOptions): Promise> { + async findAll( + _class: Ref>, + query: DocumentQuery, + options?: FindOptions + ): Promise> { console.log('search', query) const docs = await this.adapter.search(query) console.log(docs) - const ids = docs.map(doc => (doc.attachedTo ?? doc.id) as Ref) + const ids: Ref[] = [] + for (const doc of docs) { + ids.push(doc.id) + if (doc.attachedTo !== undefined) { + ids.push(doc.attachedTo) + } + } return await this.dbStorage.findAll(_class, { _id: { $in: ids as any } }, options) // TODO: remove `as any` } @@ -78,9 +100,21 @@ export class FullTextIndex extends TxProcessor implements Storage { protected override async txCreateDoc (tx: TxCreateDoc): Promise { const attributes = this.getFullTextAttributes(tx.objectClass) - if (attributes === undefined) return {} const doc = TxProcessor.createDoc2Doc(tx) - const content = attributes.map(attr => ((doc as any)[attr.name] !== null && (doc as any)[attr.name] !== undefined) ? (doc as any)[attr.name].toString() : '') // temporary: getFullTextAttributes should provide string attrs only + let parentContent: any[] = [] + if (this.hierarchy.isDerived(doc._class, core.class.AttachedDoc)) { + const attachedDoc = doc as AttachedDoc + const parentDoc = ( + await this.dbStorage.findAll(attachedDoc.attachedToClass, { _id: attachedDoc.attachedTo }, { limit: 1 }) + )[0] + if (parentDoc !== undefined) { + const parentAttributes = this.getFullTextAttributes(parentDoc._class) + parentContent = this.getContent(parentAttributes, parentDoc) + } + } + if (attributes === undefined && parentContent.length === 0) return {} + let content = this.getContent(attributes, doc) + content = content.concat(parentContent) const indexedDoc: IndexedDoc = { id: doc._id, _class: doc._class, @@ -104,7 +138,8 @@ export class FullTextIndex extends TxProcessor implements Storage { protected override async txUpdateDoc (tx: TxUpdateDoc): Promise { const attributes = this.getFullTextAttributes(tx.objectClass) - if (attributes === undefined) return {} + let result = {} + if (attributes === undefined) return result const ops: any = tx.operations const update: any = {} let i = 0 @@ -117,8 +152,39 @@ export class FullTextIndex extends TxProcessor implements Storage { i++ } if (shouldUpdate) { - return await this.adapter.update(tx.objectId, update) + result = await this.adapter.update(tx.objectId, update) + await this.updateAttachedDocs(tx, update) + } + return result + } + + private getContent (attributes: AnyAttribute[] | undefined, doc: Doc): any[] { + if (attributes === undefined) return [] + return attributes.map((attr) => + (doc as any)[attr.name]?.toString() ?? '' + ) + } + + private async updateAttachedDocs (tx: TxUpdateDoc, update: any): Promise { + const doc = (await this.dbStorage.findAll(tx.objectClass, { _id: tx.objectId }, { limit: 1 }))[0] + if (doc === undefined) return + const attributes = this.hierarchy.getAllAttributes(doc._class) + for (const attribute of attributes.values()) { + if (this.hierarchy.isDerived(attribute.type._class, core.class.Collection)) { + const collection = attribute.type as Collection + const allAttached = await this.dbStorage.findAll(collection.of, { attachedTo: tx.objectId }) + if (allAttached.length === 0) continue + const attributes = this.getFullTextAttributes(tx.objectClass) + const shift = attributes?.length ?? 0 + const docUpdate: any = {} + for (const key in update) { + const index = Number.parseInt(key.replace('content', '')) + docUpdate[`content${index + shift}`] = update[key] + } + for (const attached of allAttached) { + await this.adapter.update(attached._id, docUpdate) + } + } } - return {} } } diff --git a/server/core/src/types.ts b/server/core/src/types.ts index 8cc2cd3c99..4d53e7e75e 100644 --- a/server/core/src/types.ts +++ b/server/core/src/types.ts @@ -39,7 +39,7 @@ export interface Trigger extends Doc { * @public */ export interface IndexedDoc { - id: string + id: Ref _class: Ref> space: Ref modifiedOn: Timestamp diff --git a/server/front/src/app.ts b/server/front/src/app.ts index 2d4904f408..404a039b08 100644 --- a/server/front/src/app.ts +++ b/server/front/src/app.ts @@ -178,7 +178,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string, const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace) const indexedDoc: IndexedDoc = { - id: generateId() + '/attachments/' + name, + id: generateId() + '/attachments/' + name as Ref, _class: attachment.class.Attachment, space, modifiedOn: Date.now(), @@ -249,7 +249,7 @@ export function start (config: { transactorEndpoint: string, elasticUrl: string, const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace) const indexedDoc: IndexedDoc = { - id: generateId() + '/attachments/' + 'Profile.pdf', + id: generateId() + '/attachments/' + 'Profile.pdf' as Ref, _class: attachment.class.Attachment, space, modifiedOn: Date.now(),