update es index

Signed-off-by: Andrey Platov <andrey@hardcoreeng.com>
This commit is contained in:
Andrey Platov 2021-09-14 16:21:05 +02:00
parent 5439f55397
commit 4b0e2ceaa7
No known key found for this signature in database
GPG Key ID: C8787EFEB4B64AF0
6 changed files with 79 additions and 19 deletions

View File

@ -40,6 +40,9 @@ class NullFullTextAdapter implements FullTextAdapter {
console.log('noop full text indexer: ', doc) console.log('noop full text indexer: ', doc)
} }
async update (id: Ref<Doc>, update: Record<string, any>): Promise<void> {
}
async search (query: any): Promise<IndexedDoc[]> { async search (query: any): Promise<IndexedDoc[]> {
return [] return []
} }

View File

@ -15,6 +15,7 @@
// //
import { DOMAIN_TX } from '@anticrm/core' import { DOMAIN_TX } from '@anticrm/core'
import type { Ref, Doc } from '@anticrm/core'
import { start as startJsonRpc } from '@anticrm/server-ws' import { start as startJsonRpc } from '@anticrm/server-ws'
import { createInMemoryAdapter, createInMemoryTxAdapter } from '@anticrm/dev-storage' import { createInMemoryAdapter, createInMemoryTxAdapter } from '@anticrm/dev-storage'
import { createServerStorage, FullTextAdapter, IndexedDoc } from '@anticrm/server-core' import { createServerStorage, FullTextAdapter, IndexedDoc } from '@anticrm/server-core'
@ -28,6 +29,9 @@ class NullFullTextAdapter implements FullTextAdapter {
console.log('noop full text indexer: ', doc) console.log('noop full text indexer: ', doc)
} }
async update (id: Ref<Doc>, update: Record<string, any>): Promise<void> {
}
async search (query: any): Promise<IndexedDoc[]> { async search (query: any): Promise<IndexedDoc[]> {
return [] return []
} }

View File

@ -14,26 +14,14 @@
// limitations under the License. // limitations under the License.
// //
import core, { TxCreateDoc, Doc, Ref, Class, Obj, Hierarchy, AnyAttribute, Storage, DocumentQuery, FindOptions, FindResult, TxProcessor } from '@anticrm/core' import core, { Hierarchy, AnyAttribute, Storage, DocumentQuery, FindOptions, FindResult, TxProcessor } from '@anticrm/core'
import type { AttachedDoc } from '@anticrm/core' import type { AttachedDoc, TxUpdateDoc, TxCreateDoc, Doc, Ref, Class, Obj } from '@anticrm/core'
import type { IndexedDoc, FullTextAdapter, WithFind } from './types' import type { IndexedDoc, FullTextAdapter, WithFind } from './types'
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const NO_INDEX = [] as AnyAttribute[] const NO_INDEX = [] as AnyAttribute[]
function buildContent (doc: any, attributes: AnyAttribute[]): string {
let result = ''
for (const attr of attributes) {
const value = doc[attr.name]
if (value !== undefined) {
result += value as string
result += ' '
}
}
return result
}
export class FullTextIndex extends TxProcessor implements Storage { export class FullTextIndex extends TxProcessor implements Storage {
private readonly indexes = new Map<Ref<Class<Obj>>, AnyAttribute[]>() private readonly indexes = new Map<Ref<Class<Obj>>, AnyAttribute[]>()
@ -78,7 +66,7 @@ export class FullTextIndex extends TxProcessor implements Storage {
const attributes = this.getFullTextAttributes(tx.objectClass) const attributes = this.getFullTextAttributes(tx.objectClass)
if (attributes === undefined) return if (attributes === undefined) return
const doc = TxProcessor.createDoc2Doc(tx) const doc = TxProcessor.createDoc2Doc(tx)
const content = buildContent(doc, attributes) // (doc as any)[attribute.name] const content = attributes.map(attr => (doc as any)[attr.name]) // buildContent(doc, attributes) // (doc as any)[attribute.name]
const indexedDoc: IndexedDoc = { const indexedDoc: IndexedDoc = {
id: doc._id, id: doc._id,
_class: doc._class, _class: doc._class,
@ -86,8 +74,36 @@ export class FullTextIndex extends TxProcessor implements Storage {
modifiedOn: doc.modifiedOn, modifiedOn: doc.modifiedOn,
space: doc.space, space: doc.space,
attachedTo: (doc as AttachedDoc).attachedTo, attachedTo: (doc as AttachedDoc).attachedTo,
content content0: content[0],
content1: content[1],
content2: content[2],
content3: content[3],
content4: content[4],
content5: content[5],
content6: content[6],
content7: content[7],
content8: content[8],
content9: content[9]
} }
return await this.adapter.index(indexedDoc) return await this.adapter.index(indexedDoc)
} }
protected override async txUpdateDoc (tx: TxUpdateDoc<Doc>): Promise<void> {
const attributes = this.getFullTextAttributes(tx.objectClass)
if (attributes === undefined) return
const ops: any = tx.operations
const update: any = {}
let i = 0
let shouldUpdate = false
for (const attr of attributes) {
if (ops[attr.name] !== undefined) {
update[`content${i}`] = ops[attr.name]
shouldUpdate = true
i++
}
}
if (shouldUpdate) {
return await this.adapter.update(tx.objectId, update)
}
}
} }

View File

@ -40,7 +40,16 @@ export interface IndexedDoc {
modifiedOn: Timestamp modifiedOn: Timestamp
modifiedBy: Ref<Account> modifiedBy: Ref<Account>
attachedTo?: Ref<Doc> attachedTo?: Ref<Doc>
content?: string content0?: string
content1?: string
content2?: string
content3?: string
content4?: string
content5?: string
content6?: string
content7?: string
content8?: string
content9?: string
data?: string data?: string
} }
@ -54,6 +63,7 @@ export type SearchQuery = any // TODO: replace with DocumentQuery
*/ */
export interface FullTextAdapter { export interface FullTextAdapter {
index: (doc: IndexedDoc) => Promise<void> index: (doc: IndexedDoc) => Promise<void>
update: (id: Ref<Doc>, update: Record<string, any>) => Promise<void>
search: (query: SearchQuery) => Promise<IndexedDoc[]> search: (query: SearchQuery) => Promise<IndexedDoc[]>
} }

View File

@ -27,7 +27,7 @@ describe('client', () => {
modifiedBy: 'andrey' as Ref<Account>, modifiedBy: 'andrey' as Ref<Account>,
modifiedOn: 0, modifiedOn: 0,
space: 'space1' as Ref<Space>, space: 'space1' as Ref<Space>,
content: 'hey there!' content0: 'hey there!'
} }
await adapter.index(doc) await adapter.index(doc)
const hits = await adapter.search({}) const hits = await adapter.search({})

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import type { Doc, Ref } from '@anticrm/core'
import type { FullTextAdapter, IndexedDoc, SearchQuery } from '@anticrm/server-core' import type { FullTextAdapter, IndexedDoc, SearchQuery } from '@anticrm/server-core'
import { Client } from '@elastic/elasticsearch' import { Client } from '@elastic/elasticsearch'
@ -34,13 +35,26 @@ class ElasticAdapter implements FullTextAdapter {
query: { query: {
multi_match: { multi_match: {
query: query.$search, query: query.$search,
fields: ['content', 'attachment.content'] fields: [
'content0',
'content1',
'content2',
'content3',
'content4',
'content5',
'content6',
'content7',
'content8',
'content9',
'attachment.content'
]
} }
} }
} }
}) })
console.log(result) console.log(result)
const hits = result.body.hits.hits as any[] const hits = result.body.hits.hits as any[]
console.log('hits', hits)
return hits.map(hit => hit._source) return hits.map(hit => hit._source)
} }
@ -49,6 +63,7 @@ class ElasticAdapter implements FullTextAdapter {
if (doc.data === undefined) { if (doc.data === undefined) {
const resp = await this.client.index({ const resp = await this.client.index({
index: this.db, index: this.db,
id: doc.id,
type: '_doc', type: '_doc',
body: doc body: doc
}) })
@ -58,6 +73,7 @@ class ElasticAdapter implements FullTextAdapter {
console.log('attachment pipeline') console.log('attachment pipeline')
const resp = await this.client.index({ const resp = await this.client.index({
index: this.db, index: this.db,
id: doc.id,
type: '_doc', type: '_doc',
pipeline: 'attachment', pipeline: 'attachment',
body: doc body: doc
@ -66,6 +82,17 @@ class ElasticAdapter implements FullTextAdapter {
console.log('error', (resp.meta as any)?.body?.error) console.log('error', (resp.meta as any)?.body?.error)
} }
} }
async update (id: Ref<Doc>, update: Record<string, any>): Promise<void> {
const resp = await this.client.update({
index: this.db,
id,
body: {
doc: update
}
})
console.log('update', resp)
}
} }
/** /**