mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-28 12:35:02 +02:00
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
import {
|
|
Hierarchy,
|
|
matchQuery,
|
|
toFindResult,
|
|
type Class,
|
|
type Doc,
|
|
type DocumentQuery,
|
|
type FindOptions,
|
|
type FindResult,
|
|
type Ref,
|
|
type Timestamp
|
|
} from '@hcengineering/core'
|
|
import type { Query, QueryId } from './types'
|
|
|
|
export interface DocumentRef {
|
|
doc: Doc
|
|
queries: QueryId[]
|
|
lastUsed: Timestamp
|
|
}
|
|
|
|
export class Refs {
|
|
// A map of _class to documents.
|
|
private readonly documentRefs = new Map<string, Map<Ref<Doc>, DocumentRef>>()
|
|
|
|
constructor (readonly getHierarchy: () => Hierarchy) {}
|
|
|
|
public updateDocuments (q: Query, docs: Doc[], clean: boolean = false): void {
|
|
if (q.options?.projection !== undefined) {
|
|
return
|
|
}
|
|
const params = ':' + JSON.stringify(q.options?.lookup ?? {}) + ':' + JSON.stringify(q.options?.associations ?? {})
|
|
for (const d of docs) {
|
|
const classKey = Hierarchy.mixinOrClass(d) + params
|
|
|
|
let docMap = this.documentRefs.get(classKey)
|
|
if (docMap === undefined) {
|
|
if (clean) {
|
|
continue
|
|
}
|
|
docMap = new Map()
|
|
this.documentRefs.set(classKey, docMap)
|
|
}
|
|
const queries = (docMap.get(d._id)?.queries ?? []).filter((it) => it !== q.id)
|
|
if (!clean) {
|
|
queries.push(q.id)
|
|
}
|
|
if (queries.length === 0) {
|
|
docMap.delete(d._id)
|
|
} else {
|
|
const q = docMap.get(d._id)
|
|
if ((q?.lastUsed ?? 0) < d.modifiedOn) {
|
|
docMap.set(d._id, { ...(q ?? {}), doc: d, queries, lastUsed: d.modifiedOn })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public findFromDocs<T extends Doc>(
|
|
_class: Ref<Class<Doc>>,
|
|
query: DocumentQuery<Doc>,
|
|
options?: FindOptions<T>
|
|
): FindResult<T> | null {
|
|
if (typeof query._id === 'string') {
|
|
const desc = this.getHierarchy().getDescendants(_class)
|
|
for (const des of desc) {
|
|
const classKey =
|
|
des + ':' + JSON.stringify(options?.lookup ?? {}) + ':' + JSON.stringify(options?.associations ?? {})
|
|
// One document query
|
|
const doc = this.documentRefs.get(classKey)?.get(query._id)?.doc
|
|
if (doc !== undefined) {
|
|
const q = matchQuery([doc], query, _class, this.getHierarchy())
|
|
if (q.length > 0) {
|
|
return toFindResult(this.getHierarchy().clone([doc]), 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (
|
|
options?.limit === 1 &&
|
|
options.total !== true &&
|
|
options?.sort === undefined &&
|
|
options?.projection === undefined
|
|
) {
|
|
const classKey =
|
|
_class + ':' + JSON.stringify(options?.lookup ?? {}) + ':' + JSON.stringify(options?.associations ?? {})
|
|
const docs = this.documentRefs.get(classKey)
|
|
if (docs !== undefined) {
|
|
const _docs = Array.from(docs.values()).map((it) => it.doc)
|
|
|
|
const q = matchQuery(_docs, query, _class, this.getHierarchy())
|
|
if (q.length > 0) {
|
|
return toFindResult(this.getHierarchy().clone([q[0]]), 1)
|
|
}
|
|
}
|
|
if (options.lookup === undefined && options.associations === undefined) {
|
|
const keys = Array.from(this.documentRefs.keys())
|
|
for (const key of keys) {
|
|
if (key.startsWith(_class + ':')) {
|
|
const docs = this.documentRefs.get(key)
|
|
if (docs !== undefined) {
|
|
const _docs = Array.from(docs.values()).map((it) => it.doc)
|
|
|
|
const q = matchQuery(_docs, query, _class, this.getHierarchy())
|
|
if (q.length > 0) {
|
|
const clonedDoc = this.getHierarchy().clone(q[0])
|
|
const { $lookup, $associations, ...clean } = clonedDoc
|
|
if (this.getHierarchy().isMixin(_class)) {
|
|
return toFindResult([this.getHierarchy().as(clean, _class)] as T[], 1)
|
|
}
|
|
return toFindResult([clean], 1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
}
|