mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
UBERF-8499: Optimize indexer operation (#6959)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
51679bf82c
commit
f89df5921a
@ -955,7 +955,8 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Allow to use fuzzy search for mixins
|
// Allow to use fuzzy search for mixins
|
||||||
builder.mixin(contact.class.Contact, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: contact.class.Contact,
|
||||||
fullTextSummary: true
|
fullTextSummary: true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -549,7 +549,8 @@ export function createModel (builder: Builder): void {
|
|||||||
func: documents.function.GetAllDocumentStates
|
func: documents.function.GetAllDocumentStates
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(documents.class.Document, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: documents.class.Document,
|
||||||
fullTextSummary: true,
|
fullTextSummary: true,
|
||||||
childProcessingAllowed: true
|
childProcessingAllowed: true
|
||||||
})
|
})
|
||||||
@ -886,11 +887,13 @@ export function defineNotifications (builder: Builder): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function defineSearch (builder: Builder): void {
|
export function defineSearch (builder: Builder): void {
|
||||||
builder.mixin(documents.class.Document, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: documents.class.Document,
|
||||||
parentPropagate: true
|
parentPropagate: true
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(documents.class.DocumentMeta, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: documents.class.DocumentMeta,
|
||||||
fullTextSummary: true,
|
fullTextSummary: true,
|
||||||
childProcessingAllowed: true,
|
childProcessingAllowed: true,
|
||||||
propagate: []
|
propagate: []
|
||||||
|
@ -361,8 +361,10 @@ export class TDocIndexState extends TDoc implements DocIndexState {
|
|||||||
generationId?: string
|
generationId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@MMixin(core.mixin.FullTextSearchContext, core.class.Class)
|
@Model(core.class.FullTextSearchContext, core.class.Doc, DOMAIN_MODEL)
|
||||||
export class TFullTextSearchContext extends TClass implements FullTextSearchContext {}
|
export class TFullTextSearchContext extends TDoc implements FullTextSearchContext {
|
||||||
|
toClass!: Ref<Class<Doc<Space>>>
|
||||||
|
}
|
||||||
|
|
||||||
@MMixin(core.mixin.ConfigurationElement, core.class.Class)
|
@MMixin(core.mixin.ConfigurationElement, core.class.Class)
|
||||||
export class TConfigurationElement extends TClass implements ConfigurationElement {
|
export class TConfigurationElement extends TClass implements ConfigurationElement {
|
||||||
|
@ -309,7 +309,8 @@ export function createModel (builder: Builder): void {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(core.class.Space, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: core.class.Space,
|
||||||
childProcessingAllowed: false
|
childProcessingAllowed: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -262,7 +262,11 @@ export const coreOperation: MigrateOperation = {
|
|||||||
async migrate (client: MigrationClient): Promise<void> {
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
// We need to delete all documents in doc index state for missing classes
|
// We need to delete all documents in doc index state for missing classes
|
||||||
const allClasses = client.hierarchy.getDescendants(core.class.Doc)
|
const allClasses = client.hierarchy.getDescendants(core.class.Doc)
|
||||||
const allIndexed = allClasses.filter((it) => isClassIndexable(client.hierarchy, it))
|
const contexts = new Map(
|
||||||
|
client.model.findAllSync(core.class.FullTextSearchContext, {}).map((it) => [it.toClass, it])
|
||||||
|
)
|
||||||
|
|
||||||
|
const allIndexed = allClasses.filter((it) => isClassIndexable(client.hierarchy, it, contexts))
|
||||||
|
|
||||||
// Next remove all non indexed classes and missing classes as well.
|
// Next remove all non indexed classes and missing classes as well.
|
||||||
await client.update(
|
await client.update(
|
||||||
|
@ -214,7 +214,8 @@ export function createModel (builder: Builder): void {
|
|||||||
gmail.action.WriteEmail
|
gmail.action.WriteEmail
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.mixin(gmail.class.Message, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: gmail.class.Message,
|
||||||
parentPropagate: false
|
parentPropagate: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1438,13 +1438,15 @@ export function createModel (builder: Builder): void {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Allow to use fuzzy search for mixins
|
// Allow to use fuzzy search for mixins
|
||||||
builder.mixin(recruit.class.Vacancy, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: recruit.class.Vacancy,
|
||||||
fullTextSummary: true,
|
fullTextSummary: true,
|
||||||
childProcessingAllowed: true,
|
childProcessingAllowed: true,
|
||||||
propagate: []
|
propagate: []
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(recruit.mixin.Candidate, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: recruit.mixin.Candidate,
|
||||||
fullTextSummary: true,
|
fullTextSummary: true,
|
||||||
propagate: [recruit.class.Applicant],
|
propagate: [recruit.class.Applicant],
|
||||||
childProcessingAllowed: true,
|
childProcessingAllowed: true,
|
||||||
@ -1457,7 +1459,8 @@ export function createModel (builder: Builder): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Allow to use fuzzy search for mixins
|
// Allow to use fuzzy search for mixins
|
||||||
builder.mixin(recruit.class.Applicant, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: recruit.class.Applicant,
|
||||||
fullTextSummary: true,
|
fullTextSummary: true,
|
||||||
forceIndex: true,
|
forceIndex: true,
|
||||||
childProcessingAllowed: true,
|
childProcessingAllowed: true,
|
||||||
|
@ -179,7 +179,8 @@ export function createModel (builder: Builder): void {
|
|||||||
telegram.ids.TelegramMessageSharedActivityViewlet
|
telegram.ids.TelegramMessageSharedActivityViewlet
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.mixin(telegram.class.Message, core.class.Class, core.mixin.FullTextSearchContext, {
|
builder.createDoc(core.class.FullTextSearchContext, core.space.Model, {
|
||||||
|
toClass: telegram.class.Message,
|
||||||
parentPropagate: false,
|
parentPropagate: false,
|
||||||
childProcessingAllowed: true
|
childProcessingAllowed: true
|
||||||
})
|
})
|
||||||
|
@ -578,7 +578,8 @@ export interface BlobLookup extends Blob {
|
|||||||
*
|
*
|
||||||
* If defined for class, this class will be enabled for embedding search like openai.
|
* If defined for class, this class will be enabled for embedding search like openai.
|
||||||
*/
|
*/
|
||||||
export interface FullTextSearchContext extends Class<Doc> {
|
export interface FullTextSearchContext extends Doc {
|
||||||
|
toClass: Ref<Class<Doc>>
|
||||||
fullTextSummary?: boolean
|
fullTextSummary?: boolean
|
||||||
forceIndex?: boolean
|
forceIndex?: boolean
|
||||||
|
|
||||||
|
@ -142,10 +142,10 @@ export default plugin(coreId, {
|
|||||||
StatusCategory: '' as Ref<Class<StatusCategory>>,
|
StatusCategory: '' as Ref<Class<StatusCategory>>,
|
||||||
MigrationState: '' as Ref<Class<MigrationState>>,
|
MigrationState: '' as Ref<Class<MigrationState>>,
|
||||||
|
|
||||||
BenchmarkDoc: '' as Ref<Class<BenchmarkDoc>>
|
BenchmarkDoc: '' as Ref<Class<BenchmarkDoc>>,
|
||||||
|
FullTextSearchContext: '' as Ref<Mixin<FullTextSearchContext>>
|
||||||
},
|
},
|
||||||
mixin: {
|
mixin: {
|
||||||
FullTextSearchContext: '' as Ref<Mixin<FullTextSearchContext>>,
|
|
||||||
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
|
ConfigurationElement: '' as Ref<Mixin<ConfigurationElement>>,
|
||||||
IndexConfiguration: '' as Ref<Mixin<IndexingConfiguration<Doc>>>,
|
IndexConfiguration: '' as Ref<Mixin<IndexingConfiguration<Doc>>>,
|
||||||
SpacesTypeData: '' as Ref<Mixin<Space>>
|
SpacesTypeData: '' as Ref<Mixin<Space>>
|
||||||
|
@ -46,9 +46,9 @@ import core from './component'
|
|||||||
import { Hierarchy } from './hierarchy'
|
import { Hierarchy } from './hierarchy'
|
||||||
import { TxOperations } from './operations'
|
import { TxOperations } from './operations'
|
||||||
import { isPredicate } from './predicate'
|
import { isPredicate } from './predicate'
|
||||||
|
import { Branding, BrandingMap } from './server'
|
||||||
import { DocumentQuery, FindResult } from './storage'
|
import { DocumentQuery, FindResult } from './storage'
|
||||||
import { DOMAIN_TX } from './tx'
|
import { DOMAIN_TX } from './tx'
|
||||||
import { Branding, BrandingMap } from './server'
|
|
||||||
|
|
||||||
function toHex (value: number, chars: number): string {
|
function toHex (value: number, chars: number): string {
|
||||||
const result = value.toString(16)
|
const result = value.toString(16)
|
||||||
@ -686,39 +686,48 @@ export function getFullTextIndexableAttributes (
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ctxKey = 'indexer_ftc'
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function getFullTextContext (
|
export function getFullTextContext (
|
||||||
hierarchy: Hierarchy,
|
hierarchy: Hierarchy,
|
||||||
objectClass: Ref<Class<Doc>>
|
objectClass: Ref<Class<Doc>>,
|
||||||
|
contexts: Map<Ref<Class<Doc>>, FullTextSearchContext>
|
||||||
): Omit<FullTextSearchContext, keyof Class<Doc>> {
|
): Omit<FullTextSearchContext, keyof Class<Doc>> {
|
||||||
let objClass = hierarchy.getClass(objectClass)
|
let ctx: Omit<FullTextSearchContext, keyof Class<Doc>> | undefined = hierarchy.getClassifierProp(objectClass, ctxKey)
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (hierarchy.hasMixin(objClass, core.mixin.FullTextSearchContext)) {
|
|
||||||
const ctx = hierarchy.as<Class<Doc>, FullTextSearchContext>(objClass, core.mixin.FullTextSearchContext)
|
|
||||||
if (ctx !== undefined) {
|
if (ctx !== undefined) {
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
if (typeof ctx !== 'string') {
|
||||||
|
const anc = hierarchy.getAncestors(objectClass)
|
||||||
|
for (const oc of anc) {
|
||||||
|
const ctx = contexts.get(oc)
|
||||||
|
if (ctx !== undefined) {
|
||||||
|
hierarchy.setClassifierProp(objectClass, ctxKey, ctx)
|
||||||
|
return ctx
|
||||||
}
|
}
|
||||||
if (objClass.extends === undefined) {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
objClass = hierarchy.getClass(objClass.extends)
|
|
||||||
}
|
}
|
||||||
return {
|
ctx = {
|
||||||
|
toClass: objectClass,
|
||||||
fullTextSummary: false,
|
fullTextSummary: false,
|
||||||
forceIndex: false,
|
forceIndex: false,
|
||||||
propagate: [],
|
propagate: [],
|
||||||
childProcessingAllowed: true
|
childProcessingAllowed: true
|
||||||
}
|
}
|
||||||
|
hierarchy.setClassifierProp(objectClass, ctxKey, ctx)
|
||||||
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function isClassIndexable (hierarchy: Hierarchy, c: Ref<Class<Doc>>): boolean {
|
export function isClassIndexable (
|
||||||
|
hierarchy: Hierarchy,
|
||||||
|
c: Ref<Class<Doc>>,
|
||||||
|
contexts: Map<Ref<Class<Doc>>, FullTextSearchContext>
|
||||||
|
): boolean {
|
||||||
const indexed = hierarchy.getClassifierProp(c, 'class_indexed')
|
const indexed = hierarchy.getClassifierProp(c, 'class_indexed')
|
||||||
if (indexed !== undefined) {
|
if (indexed !== undefined) {
|
||||||
return indexed as boolean
|
return indexed as boolean
|
||||||
@ -756,13 +765,13 @@ export function isClassIndexable (hierarchy: Hierarchy, c: Ref<Class<Doc>>): boo
|
|||||||
|
|
||||||
let result = true
|
let result = true
|
||||||
|
|
||||||
if (attrs.length === 0 && !(getFullTextContext(hierarchy, c)?.forceIndex ?? false)) {
|
if (attrs.length === 0 && !(getFullTextContext(hierarchy, c, contexts)?.forceIndex ?? false)) {
|
||||||
result = false
|
result = false
|
||||||
// We need check if document has collections with indexable fields.
|
// We need check if document has collections with indexable fields.
|
||||||
const attrs = hierarchy.getAllAttributes(c).values()
|
const attrs = hierarchy.getAllAttributes(c).values()
|
||||||
for (const attr of attrs) {
|
for (const attr of attrs) {
|
||||||
if (attr.type._class === core.class.Collection) {
|
if (attr.type._class === core.class.Collection) {
|
||||||
if (isClassIndexable(hierarchy, (attr.type as Collection<AttachedDoc>).of)) {
|
if (isClassIndexable(hierarchy, (attr.type as Collection<AttachedDoc>).of, contexts)) {
|
||||||
result = true
|
result = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"template": "@hcengineering/node-package",
|
"template": "@hcengineering/node-package",
|
||||||
"license": "EPL-2.0",
|
"license": "EPL-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret OPERATION_PROFILING=false MODEL_JSON=../../models/all/bundle/model.json node bundle/bundle.js",
|
"start": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 DB_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret OPERATION_PROFILING=false MODEL_JSON=../../models/all/bundle/model.json node --inspect bundle/bundle.js",
|
||||||
"start-u": "rush bundle --to @hcengineering/pod-server && ./bundle/ && cross-env NODE_ENV=production SERVER_PROVIDER=uweb ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json node bundle/bundle.js",
|
"start-u": "rush bundle --to @hcengineering/pod-server && ./bundle/ && cross-env NODE_ENV=production SERVER_PROVIDER=uweb ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json node bundle/bundle.js",
|
||||||
"start-flame": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json clinic flame --dest ./out -- node --nolazy -r ts-node/register --enable-source-maps src/__start.ts",
|
"start-flame": "rush bundle --to @hcengineering/pod-server && cross-env NODE_ENV=production ELASTIC_INDEX_NAME=local_storage_index MODEL_VERSION=$(node ../../common/scripts/show_version.js) ACCOUNTS_URL=http://localhost:3000 REKONI_URL=http://localhost:4004 MONGO_URL=mongodb://localhost:27017 ELASTIC_URL=http://localhost:9200 FRONT_URL=http://localhost:8087 UPLOAD_URL=/upload MINIO_ENDPOINT=localhost MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin METRICS_CONSOLE=true SERVER_SECRET=secret MODEL_JSON=../../models/all/bundle/model.json clinic flame --dest ./out -- node --nolazy -r ts-node/register --enable-source-maps src/__start.ts",
|
||||||
"build": "compile",
|
"build": "compile",
|
||||||
|
@ -522,7 +522,7 @@ class ElasticAdapter implements FullTextAdapter {
|
|||||||
async updateMany (docs: IndexedDoc[]): Promise<TxResult[]> {
|
async updateMany (docs: IndexedDoc[]): Promise<TxResult[]> {
|
||||||
const parts = Array.from(docs)
|
const parts = Array.from(docs)
|
||||||
while (parts.length > 0) {
|
while (parts.length > 0) {
|
||||||
const part = parts.splice(0, 1000)
|
const part = parts.splice(0, 500)
|
||||||
|
|
||||||
const operations = part.flatMap((doc) => {
|
const operations = part.flatMap((doc) => {
|
||||||
const wsDoc = { workspaceId: this.workspaceString, ...doc }
|
const wsDoc = { workspaceId: this.workspaceString, ...doc }
|
||||||
|
@ -45,9 +45,9 @@ import core, {
|
|||||||
toFindResult
|
toFindResult
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import type { FullTextAdapter, IndexedDoc, SessionFindAll, StorageAdapter, WithFind } from '@hcengineering/server-core'
|
import type { FullTextAdapter, IndexedDoc, SessionFindAll, StorageAdapter, WithFind } from '@hcengineering/server-core'
|
||||||
import { getScoringConfig, mapSearchResultDoc } from './mapper'
|
|
||||||
import { type FullTextIndexPipeline } from './indexer'
|
import { type FullTextIndexPipeline } from './indexer'
|
||||||
import { createStateDoc } from './indexer/utils'
|
import { createStateDoc } from './indexer/utils'
|
||||||
|
import { getScoringConfig, mapSearchResultDoc } from './mapper'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -90,7 +90,7 @@ export class FullTextIndex implements WithFind {
|
|||||||
if (TxProcessor.isExtendsCUD(tx._class)) {
|
if (TxProcessor.isExtendsCUD(tx._class)) {
|
||||||
const cud = tx as TxCUD<Doc>
|
const cud = tx as TxCUD<Doc>
|
||||||
|
|
||||||
if (!isClassIndexable(this.hierarchy, cud.objectClass)) {
|
if (!isClassIndexable(this.hierarchy, cud.objectClass, this.indexer.contexts)) {
|
||||||
// No need, since no indixable fields or attachments.
|
// No need, since no indixable fields or attachments.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ export class IndexedFieldStage implements FullTextPipelineStage {
|
|||||||
updateFields: DocUpdateHandler[] = []
|
updateFields: DocUpdateHandler[] = []
|
||||||
|
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
constructor (private readonly dbStorageFindAll: SessionFindAll) {}
|
constructor (private readonly dbStorageFindAll: SessionFindAll) {}
|
||||||
|
|
||||||
async initialize (ctx: MeasureContext, storage: DbAdapter, pipeline: FullTextPipeline): Promise<void> {}
|
async initialize (ctx: MeasureContext, storage: DbAdapter, pipeline: FullTextPipeline): Promise<void> {}
|
||||||
@ -150,7 +151,7 @@ export class IndexedFieldStage implements FullTextPipelineStage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (docState.attachedTo != null && changes > 0) {
|
if (docState.attachedTo != null && changes > 0) {
|
||||||
const ctx = getFullTextContext(pipeline.hierarchy, objClass)
|
const ctx = getFullTextContext(pipeline.hierarchy, objClass, pipeline.contexts)
|
||||||
if (ctx.parentPropagate ?? true) {
|
if (ctx.parentPropagate ?? true) {
|
||||||
// We need to clear field stage from parent, so it will be re indexed.
|
// We need to clear field stage from parent, so it will be re indexed.
|
||||||
await pipeline.update(docState.attachedTo as Ref<DocIndexState>, false, {})
|
await pipeline.update(docState.attachedTo as Ref<DocIndexState>, false, {})
|
||||||
@ -173,17 +174,13 @@ export class IndexedFieldStage implements FullTextPipelineStage {
|
|||||||
{
|
{
|
||||||
attachedTo: ids.length === 1 ? ids[0] : { $in: ids }
|
attachedTo: ids.length === 1 ? ids[0] : { $in: ids }
|
||||||
},
|
},
|
||||||
{ limit: ids.length }
|
{ limit: ids.length, skipSpace: true, skipClass: true }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const childs = allChildDocs.filter((it) => it.attachedTo === docState._id)
|
const childs = allChildDocs.filter((it) => it.attachedTo === docState._id)
|
||||||
for (const u of childs) {
|
// Marck childs to be indexed on next step
|
||||||
if (propagate.some((it) => pipeline.hierarchy.isDerived(u.objectClass, it))) {
|
await pipeline.queue(metrics, new Map(childs.map((it) => [it._id, { updated: true, removed: false }])))
|
||||||
pipeline.add(u)
|
|
||||||
await pipeline.update(u._id, false, {})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await pipeline.update(docState._id, true, docUpdate)
|
await pipeline.update(docState._id, true, docUpdate)
|
||||||
|
@ -30,12 +30,13 @@ import core, {
|
|||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
type Ref,
|
type Ref,
|
||||||
|
SortingOrder,
|
||||||
toIdMap,
|
toIdMap,
|
||||||
type WorkspaceId
|
type WorkspaceId
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { type DbAdapter, type FullTextAdapter, type IndexedDoc, type SessionFindAll } from '@hcengineering/server-core'
|
import { type DbAdapter, type FullTextAdapter, type IndexedDoc, type SessionFindAll } from '@hcengineering/server-core'
|
||||||
import { updateDocWithPresenter } from '../mapper'
|
|
||||||
import { jsonToText, markupToJSON } from '@hcengineering/text'
|
import { jsonToText, markupToJSON } from '@hcengineering/text'
|
||||||
|
import { updateDocWithPresenter } from '../mapper'
|
||||||
import {
|
import {
|
||||||
contentStageId,
|
contentStageId,
|
||||||
type DocUpdateHandler,
|
type DocUpdateHandler,
|
||||||
@ -118,7 +119,7 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
|
|
||||||
const childIds = toIndexPart
|
const childIds = toIndexPart
|
||||||
.filter((it) => {
|
.filter((it) => {
|
||||||
const fctx = getFullTextContext(pipeline.hierarchy, it.objectClass)
|
const fctx = getFullTextContext(pipeline.hierarchy, it.objectClass, pipeline.contexts)
|
||||||
return fctx.childProcessingAllowed ?? true
|
return fctx.childProcessingAllowed ?? true
|
||||||
})
|
})
|
||||||
.map((it) => it._id)
|
.map((it) => it._id)
|
||||||
@ -127,9 +128,17 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
'find-child',
|
'find-child',
|
||||||
{},
|
{},
|
||||||
async (ctx) =>
|
async (ctx) =>
|
||||||
await this.dbStorageFindAll(ctx, core.class.DocIndexState, {
|
await this.dbStorageFindAll(
|
||||||
|
ctx,
|
||||||
|
core.class.DocIndexState,
|
||||||
|
{
|
||||||
attachedTo: childIds.length === 1 ? childIds[0] : { $in: childIds }
|
attachedTo: childIds.length === 1 ? childIds[0] : { $in: childIds }
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
skipClass: true,
|
||||||
|
skipSpace: true
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// spaces
|
// spaces
|
||||||
@ -138,14 +147,22 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
'find-spaces',
|
'find-spaces',
|
||||||
{},
|
{},
|
||||||
async (ctx) =>
|
async (ctx) =>
|
||||||
await this.dbStorageFindAll(ctx, core.class.DocIndexState, {
|
await this.dbStorageFindAll(
|
||||||
|
ctx,
|
||||||
|
core.class.DocIndexState,
|
||||||
|
{
|
||||||
_id: {
|
_id: {
|
||||||
$in: toIndexPart.map(
|
$in: toIndexPart.map(
|
||||||
(doc) =>
|
(doc) =>
|
||||||
(doc.attributes[docKey('space', { _class: doc.objectClass })] ?? doc.space) as Ref<DocIndexState>
|
(doc.attributes[docKey('space', { _class: doc.objectClass })] ?? doc.space) as Ref<DocIndexState>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
{
|
||||||
|
skipClass: true,
|
||||||
|
skipSpace: true
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -163,7 +180,7 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
const childDocs = allChildDocs.filter((it) => it.attachedTo === doc._id)
|
const childDocs = allChildDocs.filter((it) => it.attachedTo === doc._id)
|
||||||
if (childDocs.length > 0) {
|
if (childDocs.length > 0) {
|
||||||
for (const c of childDocs) {
|
for (const c of childDocs) {
|
||||||
const fctx = getFullTextContext(pipeline.hierarchy, c.objectClass)
|
const fctx = getFullTextContext(pipeline.hierarchy, c.objectClass, pipeline.contexts)
|
||||||
if (fctx.parentPropagate ?? true) {
|
if (fctx.parentPropagate ?? true) {
|
||||||
ctx.withSync('updateDoc2Elastic', {}, (ctx) => {
|
ctx.withSync('updateDoc2Elastic', {}, (ctx) => {
|
||||||
updateDoc2Elastic(
|
updateDoc2Elastic(
|
||||||
@ -195,7 +212,11 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
{
|
{
|
||||||
_id: doc.attachedTo as Ref<DocIndexState>
|
_id: doc.attachedTo as Ref<DocIndexState>
|
||||||
},
|
},
|
||||||
{ limit: 1 }
|
{
|
||||||
|
limit: 1,
|
||||||
|
skipClass: true,
|
||||||
|
skipSpace: true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
).shift()
|
).shift()
|
||||||
))
|
))
|
||||||
@ -217,10 +238,29 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
|
|
||||||
const collectClasses = collectPropagateClasses(pipeline, parentDoc.objectClass)
|
const collectClasses = collectPropagateClasses(pipeline, parentDoc.objectClass)
|
||||||
if (collectClasses.length > 0) {
|
if (collectClasses.length > 0) {
|
||||||
const collections = await this.dbStorageFindAll<DocIndexState>(ctx, core.class.DocIndexState, {
|
let last: number = 0
|
||||||
|
while (true) {
|
||||||
|
const collections = await this.dbStorageFindAll<DocIndexState>(
|
||||||
|
ctx,
|
||||||
|
core.class.DocIndexState,
|
||||||
|
{
|
||||||
attachedTo: parentDoc._id,
|
attachedTo: parentDoc._id,
|
||||||
objectClass: { $in: collectClasses }
|
objectClass: { $in: collectClasses },
|
||||||
})
|
modifiedOn: { $gt: last }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sort: {
|
||||||
|
modifiedOn: SortingOrder.Ascending
|
||||||
|
},
|
||||||
|
skipClass: true,
|
||||||
|
skipSpace: true,
|
||||||
|
limit: 500
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (collections.length === 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
last = collections[collections.length - 1].modifiedOn
|
||||||
for (const c of collections) {
|
for (const c of collections) {
|
||||||
ctx.withSync('updateDoc2Elastic', {}, (ctx) => {
|
ctx.withSync('updateDoc2Elastic', {}, (ctx) => {
|
||||||
updateDoc2Elastic(
|
updateDoc2Elastic(
|
||||||
@ -239,6 +279,7 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const spaceDoc = spaceDocs.get(
|
const spaceDoc = spaceDocs.get(
|
||||||
(doc.attributes[docKey('space', { _class: doc.objectClass })] ?? doc.space) as Ref<DocIndexState>
|
(doc.attributes[docKey('space', { _class: doc.objectClass })] ?? doc.space) as Ref<DocIndexState>
|
||||||
)
|
)
|
||||||
@ -262,7 +303,7 @@ export class FullTextPushStage implements FullTextPipelineStage {
|
|||||||
}
|
}
|
||||||
// Perform bulk update to elastic
|
// Perform bulk update to elastic
|
||||||
|
|
||||||
void pushQueue.add(async () => {
|
await pushQueue.exec(async () => {
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
await ctx.with('push-elastic', {}, async () => {
|
await ctx.with('push-elastic', {}, async () => {
|
||||||
|
@ -21,6 +21,7 @@ import core, {
|
|||||||
type DocIndexState,
|
type DocIndexState,
|
||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
type DocumentUpdate,
|
type DocumentUpdate,
|
||||||
|
type FullTextSearchContext,
|
||||||
type Hierarchy,
|
type Hierarchy,
|
||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
type ModelDb,
|
type ModelDb,
|
||||||
@ -79,6 +80,10 @@ export class FullTextIndexPipeline implements FullTextPipeline {
|
|||||||
|
|
||||||
uploadOps: DocIndexState[] = []
|
uploadOps: DocIndexState[] = []
|
||||||
|
|
||||||
|
contexts: Map<Ref<Class<Doc>>, FullTextSearchContext>
|
||||||
|
propogage = new Map<Ref<Class<Doc>>, Ref<Class<Doc>>[]>()
|
||||||
|
propogageClasses = new Map<Ref<Class<Doc>>, Ref<Class<Doc>>[]>()
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private readonly storage: DbAdapter,
|
private readonly storage: DbAdapter,
|
||||||
private readonly stages: FullTextPipelineStage[],
|
private readonly stages: FullTextPipelineStage[],
|
||||||
@ -90,6 +95,7 @@ export class FullTextIndexPipeline implements FullTextPipeline {
|
|||||||
) {
|
) {
|
||||||
this.readyStages = stages.map((it) => it.stageId)
|
this.readyStages = stages.map((it) => it.stageId)
|
||||||
this.readyStages.sort()
|
this.readyStages.sort()
|
||||||
|
this.contexts = new Map(model.findAllSync(core.class.FullTextSearchContext, {}).map((it) => [it.toClass, it]))
|
||||||
}
|
}
|
||||||
|
|
||||||
async cancel (): Promise<void> {
|
async cancel (): Promise<void> {
|
||||||
@ -386,8 +392,6 @@ export class FullTextIndexPipeline implements FullTextPipeline {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Also update doc index state queries.
|
// Also update doc index state queries.
|
||||||
_classes.push(core.class.DocIndexState)
|
|
||||||
|
|
||||||
_classes.forEach((it) => this.broadcastClasses.add(it))
|
_classes.forEach((it) => this.broadcastClasses.add(it))
|
||||||
|
|
||||||
if (this.triggerCounts > 0) {
|
if (this.triggerCounts > 0) {
|
||||||
@ -410,10 +414,16 @@ export class FullTextIndexPipeline implements FullTextPipeline {
|
|||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000)
|
||||||
|
|
||||||
|
let notified = false
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
this.triggerIndexing = () => {
|
this.triggerIndexing = () => {
|
||||||
this.triggerCounts++
|
this.triggerCounts++
|
||||||
|
if (!notified) {
|
||||||
|
notified = true
|
||||||
|
setTimeout(() => {
|
||||||
resolve(null)
|
resolve(null)
|
||||||
|
}, 500) // Start indexing only after cooldown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -435,14 +445,18 @@ export class FullTextIndexPipeline implements FullTextPipeline {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let result: DocIndexState[] | undefined = await ctx.with('get-indexable', {}, async () => {
|
let result: DocIndexState[] | undefined = await ctx.with('get-indexable', {}, async () => {
|
||||||
const q: DocumentQuery<DocIndexState> = {
|
return await this.storage.findAll(
|
||||||
|
ctx,
|
||||||
|
core.class.DocIndexState,
|
||||||
|
{
|
||||||
needIndex: true
|
needIndex: true
|
||||||
}
|
},
|
||||||
return await this.storage.findAll(ctx, core.class.DocIndexState, q, {
|
{
|
||||||
limit: globalIndexer.processingSize,
|
limit: globalIndexer.processingSize,
|
||||||
skipClass: true,
|
skipClass: true,
|
||||||
skipSpace: true
|
skipSpace: true
|
||||||
})
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
// No more results
|
// No more results
|
||||||
|
@ -114,7 +114,7 @@ export class FullSummaryStage implements FullTextPipelineStage {
|
|||||||
const childDocs = allChildDocs.filter((it) => it.attachedTo === doc._id)
|
const childDocs = allChildDocs.filter((it) => it.attachedTo === doc._id)
|
||||||
if (childDocs.length > 0) {
|
if (childDocs.length > 0) {
|
||||||
for (const c of childDocs) {
|
for (const c of childDocs) {
|
||||||
const ctx = getFullTextContext(pipeline.hierarchy, c.objectClass)
|
const ctx = getFullTextContext(pipeline.hierarchy, c.objectClass, pipeline.contexts)
|
||||||
if (ctx.parentPropagate ?? true) {
|
if (ctx.parentPropagate ?? true) {
|
||||||
if (embeddingText.length > this.summaryLimit) {
|
if (embeddingText.length > this.summaryLimit) {
|
||||||
break
|
break
|
||||||
@ -137,15 +137,35 @@ export class FullSummaryStage implements FullTextPipelineStage {
|
|||||||
metrics,
|
metrics,
|
||||||
core.class.DocIndexState,
|
core.class.DocIndexState,
|
||||||
{ _id: doc.attachedTo as Ref<DocIndexState> },
|
{ _id: doc.attachedTo as Ref<DocIndexState> },
|
||||||
{ limit: 1 }
|
{
|
||||||
|
limit: 1,
|
||||||
|
skipSpace: true,
|
||||||
|
skipClass: true
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if (parentDoc !== undefined) {
|
if (parentDoc !== undefined) {
|
||||||
const ctx = collectPropagateClasses(pipeline, parentDoc.objectClass)
|
const ctx = collectPropagateClasses(pipeline, parentDoc.objectClass)
|
||||||
if (ctx.length > 0) {
|
if (ctx.length > 0) {
|
||||||
const collections = await this.dbStorageFindAll(metrics, core.class.DocIndexState, {
|
let last = 0
|
||||||
|
while (true) {
|
||||||
|
const collections = await this.dbStorageFindAll(
|
||||||
|
metrics,
|
||||||
|
core.class.DocIndexState,
|
||||||
|
{
|
||||||
attachedTo: parentDoc._id,
|
attachedTo: parentDoc._id,
|
||||||
objectClass: ctx.length === 1 ? ctx[0] : { $in: ctx }
|
objectClass: ctx.length === 1 ? ctx[0] : { $in: ctx },
|
||||||
})
|
modifiedOn: { $gt: last }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
limit: 250,
|
||||||
|
skipClass: true,
|
||||||
|
skipSpace: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (collections.length === 0) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
last = collections[collections.length - 1].modifiedOn
|
||||||
for (const c of collections) {
|
for (const c of collections) {
|
||||||
embeddingText +=
|
embeddingText +=
|
||||||
'\n' +
|
'\n' +
|
||||||
@ -155,6 +175,7 @@ export class FullSummaryStage implements FullTextPipelineStage {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (embeddingText.length > this.summaryLimit) {
|
if (embeddingText.length > this.summaryLimit) {
|
||||||
break
|
break
|
||||||
@ -188,7 +209,7 @@ export class FullSummaryStage implements FullTextPipelineStage {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function isIndexingRequired (pipeline: FullTextPipeline, doc: DocIndexState): boolean {
|
export function isIndexingRequired (pipeline: FullTextPipeline, doc: DocIndexState): boolean {
|
||||||
return getFullTextContext(pipeline.hierarchy, doc.objectClass).fullTextSummary ?? false
|
return getFullTextContext(pipeline.hierarchy, doc.objectClass, pipeline.contexts).fullTextSummary ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
type DocIndexState,
|
type DocIndexState,
|
||||||
type DocumentQuery,
|
type DocumentQuery,
|
||||||
type DocumentUpdate,
|
type DocumentUpdate,
|
||||||
|
type FullTextSearchContext,
|
||||||
type Hierarchy,
|
type Hierarchy,
|
||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
type ModelDb,
|
type ModelDb,
|
||||||
@ -32,6 +33,12 @@ import type { DbAdapter, IndexedDoc } from '@hcengineering/server-core'
|
|||||||
export interface FullTextPipeline {
|
export interface FullTextPipeline {
|
||||||
hierarchy: Hierarchy
|
hierarchy: Hierarchy
|
||||||
model: ModelDb
|
model: ModelDb
|
||||||
|
|
||||||
|
contexts: Map<Ref<Class<Doc>>, FullTextSearchContext>
|
||||||
|
|
||||||
|
propogage: Map<Ref<Class<Doc>>, Ref<Class<Doc>>[]>
|
||||||
|
propogageClasses: Map<Ref<Class<Doc>>, Ref<Class<Doc>>[]>
|
||||||
|
|
||||||
update: (
|
update: (
|
||||||
docId: Ref<DocIndexState>,
|
docId: Ref<DocIndexState>,
|
||||||
mark: boolean,
|
mark: boolean,
|
||||||
@ -49,6 +56,11 @@ export interface FullTextPipeline {
|
|||||||
from?: number
|
from?: number
|
||||||
) => Promise<{ docs: IndexedDoc[], pass: boolean }>
|
) => Promise<{ docs: IndexedDoc[], pass: boolean }>
|
||||||
|
|
||||||
|
queue: (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
updates: Map<Ref<DocIndexState>, { create?: DocIndexState, updated: boolean, removed: boolean }>
|
||||||
|
) => Promise<void>
|
||||||
|
|
||||||
cancelling: boolean
|
cancelling: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,58 +82,55 @@ export function traverseFullTextContexts (
|
|||||||
pipeline: FullTextPipeline,
|
pipeline: FullTextPipeline,
|
||||||
objectClass: Ref<Class<Doc>>,
|
objectClass: Ref<Class<Doc>>,
|
||||||
op: (ftc: Omit<FullTextSearchContext, keyof Class<Doc>>) => void
|
op: (ftc: Omit<FullTextSearchContext, keyof Class<Doc>>) => void
|
||||||
): Ref<Class<Doc>>[] {
|
): void {
|
||||||
const desc = new Set(pipeline.hierarchy.getDescendants(objectClass))
|
const cl = pipeline.hierarchy.getBaseClass(objectClass)
|
||||||
const propagate = new Set<Ref<Class<Doc>>>()
|
const ftContext = getFullTextContext(pipeline.hierarchy, cl, pipeline.contexts)
|
||||||
|
|
||||||
const ftContext = getFullTextContext(pipeline.hierarchy, objectClass)
|
|
||||||
if (ftContext !== undefined) {
|
if (ftContext !== undefined) {
|
||||||
op(ftContext)
|
op(ftContext)
|
||||||
}
|
}
|
||||||
|
const dsca = pipeline.hierarchy.getDescendants(cl)
|
||||||
// Add all parent mixins as well
|
|
||||||
for (const a of pipeline.hierarchy.getAncestors(objectClass)) {
|
|
||||||
const ftContext = getFullTextContext(pipeline.hierarchy, a)
|
|
||||||
if (ftContext !== undefined) {
|
|
||||||
op(ftContext)
|
|
||||||
}
|
|
||||||
const dsca = pipeline.hierarchy.getDescendants(a)
|
|
||||||
for (const dd of dsca) {
|
for (const dd of dsca) {
|
||||||
if (pipeline.hierarchy.isMixin(dd)) {
|
const mContext = getFullTextContext(pipeline.hierarchy, dd, pipeline.contexts)
|
||||||
desc.add(dd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const d of desc) {
|
|
||||||
if (pipeline.hierarchy.isMixin(d)) {
|
|
||||||
const mContext = getFullTextContext(pipeline.hierarchy, d)
|
|
||||||
if (mContext !== undefined) {
|
if (mContext !== undefined) {
|
||||||
op(mContext)
|
op(mContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return Array.from(propagate.values())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function collectPropagate (pipeline: FullTextPipeline, objectClass: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
export function collectPropagate (pipeline: FullTextPipeline, objectClass: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
||||||
const propagate = new Set<Ref<Class<Doc>>>()
|
let propagate = pipeline.propogage.get(objectClass)
|
||||||
traverseFullTextContexts(pipeline, objectClass, (fts) => fts?.propagate?.forEach((it) => propagate.add(it)))
|
if (propagate !== undefined) {
|
||||||
|
return propagate
|
||||||
|
}
|
||||||
|
const set = new Set<Ref<Class<Doc>>>()
|
||||||
|
traverseFullTextContexts(pipeline, objectClass, (fts) => {
|
||||||
|
fts?.propagate?.forEach((it) => {
|
||||||
|
set.add(it)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return Array.from(propagate.values())
|
propagate = Array.from(set.values())
|
||||||
|
pipeline.propogage.set(objectClass, propagate)
|
||||||
|
return propagate
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function collectPropagateClasses (pipeline: FullTextPipeline, objectClass: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
export function collectPropagateClasses (pipeline: FullTextPipeline, objectClass: Ref<Class<Doc>>): Ref<Class<Doc>>[] {
|
||||||
const propagate = new Set<Ref<Class<Doc>>>()
|
let propagate = pipeline.propogageClasses.get(objectClass)
|
||||||
traverseFullTextContexts(pipeline, objectClass, (fts) => fts?.propagateClasses?.forEach((it) => propagate.add(it)))
|
if (propagate !== undefined) {
|
||||||
|
return propagate
|
||||||
|
}
|
||||||
|
const set = new Set<Ref<Class<Doc>>>()
|
||||||
|
traverseFullTextContexts(pipeline, objectClass, (fts) => fts?.propagateClasses?.forEach((it) => set.add(it)))
|
||||||
|
|
||||||
return Array.from(propagate.values())
|
propagate = Array.from(set.values())
|
||||||
|
pipeline.propogageClasses.set(objectClass, propagate)
|
||||||
|
return propagate
|
||||||
}
|
}
|
||||||
|
|
||||||
const CUSTOM_ATTR_KEY = 'customAttributes'
|
const CUSTOM_ATTR_KEY = 'customAttributes'
|
||||||
|
Loading…
Reference in New Issue
Block a user