Fix mixin query (#2239)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-07-16 10:04:24 +06:00 committed by GitHub
parent 4a9740e64d
commit e7c0740b62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -253,6 +253,73 @@ export class LiveQuery extends TxProcessor implements Client {
return {} return {}
} }
private async checkSearch (q: Query, pos: number, _id: Ref<Doc>): Promise<boolean> {
if (q.result instanceof Promise) {
q.result = await q.result
}
const match = await this.findOne(q._class, { $search: q.query.$search, _id }, q.options)
if (match === undefined) {
if (q.options?.limit === q.result.length) {
await this.refresh(q)
return true
} else {
q.result.splice(pos, 1)
q.total--
}
} else {
q.result[pos] = match
}
return false
}
private async getCurrentDoc (q: Query, pos: number, _id: Ref<Doc>): Promise<boolean> {
if (q.result instanceof Promise) {
q.result = await q.result
}
const current = await this.findOne(q._class, { _id }, q.options)
if (current !== undefined && this.match(q, current)) {
q.result[pos] = current
} else {
if (q.options?.limit === q.result.length) {
await this.refresh(q)
return true
} else {
q.result.splice(pos, 1)
q.total--
}
}
return false
}
private async __updateMixinDoc (q: Query, updatedDoc: WithLookup<Doc>, tx: TxMixin<Doc, Doc>): Promise<void> {
updatedDoc = TxProcessor.updateMixin4Doc(updatedDoc, tx)
const ops = {
...tx.attributes,
modifiedBy: tx.modifiedBy,
modifiedOn: tx.modifiedOn
}
await this.__updateLookup(q, updatedDoc, ops)
}
private async checkUpdatedDocMatch (q: Query, pos: number, updatedDoc: WithLookup<Doc>): Promise<boolean> {
if (q.result instanceof Promise) {
q.result = await q.result
}
if (!this.match(q, updatedDoc)) {
if (q.options?.limit === q.result.length) {
await this.refresh(q)
return true
} else {
q.result.splice(pos, 1)
q.total--
}
} else {
q.result[pos] = updatedDoc
}
return false
}
protected override async txMixin (tx: TxMixin<Doc, Doc>): Promise<TxResult> { protected override async txMixin (tx: TxMixin<Doc, Doc>): Promise<TxResult> {
const hierarchy = this.client.getHierarchy() const hierarchy = this.client.getHierarchy()
for (const queries of this.queries) { for (const queries of this.queries) {
@ -267,12 +334,25 @@ export class LiveQuery extends TxProcessor implements Client {
if (q.result instanceof Promise) { if (q.result instanceof Promise) {
q.result = await q.result q.result = await q.result
} }
let updatedDoc = q.result.find((p) => p._id === tx.objectId) const pos = q.result.findIndex((p) => p._id === tx.objectId)
if (updatedDoc !== undefined) { if (pos !== -1) {
// Create or apply mixin value // If query contains search we must check use fulltext
updatedDoc = TxProcessor.updateMixin4Doc(updatedDoc, tx) if (q.query.$search != null && q.query.$search.length > 0) {
await this.__updateLookup(q, updatedDoc, tx.attributes) const searchRefresh = await this.checkSearch(q, pos, tx.objectId)
await this.updatedDocCallback(updatedDoc, q) if (searchRefresh) return {}
} else {
const updatedDoc = q.result[pos]
if (updatedDoc.modifiedOn < tx.modifiedOn) {
await this.__updateMixinDoc(q, updatedDoc, tx)
const updateRefresh = await this.checkUpdatedDocMatch(q, pos, updatedDoc)
if (updateRefresh) return {}
} else {
const currentRefresh = await this.getCurrentDoc(q, pos, updatedDoc._id)
if (currentRefresh) return {}
}
}
this.sort(q, tx)
await this.updatedDocCallback(q.result[pos], q)
} else if (isMixin) { } else if (isMixin) {
// Mixin potentially added to object we doesn't have in out results // Mixin potentially added to object we doesn't have in out results
const doc = await this.findOne(q._class, { _id: tx.objectId }, q.options) const doc = await this.findOne(q._class, { _id: tx.objectId }, q.options)
@ -280,6 +360,7 @@ export class LiveQuery extends TxProcessor implements Client {
await this.handleDocAdd(q, doc, false) await this.handleDocAdd(q, doc, false)
} }
} }
await this.handleDocUpdateLookup(q, tx)
} }
} }
return {} return {}
@ -340,43 +421,17 @@ export class LiveQuery extends TxProcessor implements Client {
if (pos !== -1) { if (pos !== -1) {
// If query contains search we must check use fulltext // If query contains search we must check use fulltext
if (q.query.$search != null && q.query.$search.length > 0) { if (q.query.$search != null && q.query.$search.length > 0) {
const match = await this.findOne(q._class, { $search: q.query.$search, _id: tx.objectId }, q.options) const searchRefresh = await this.checkSearch(q, pos, tx.objectId)
if (match === undefined) { if (searchRefresh) return
if (q.options?.limit === q.result.length) {
return await this.refresh(q)
} else {
q.result.splice(pos, 1)
q.total--
}
} else {
q.result[pos] = match
}
} else { } else {
const updatedDoc = q.result[pos] const updatedDoc = q.result[pos]
if (updatedDoc.modifiedOn < tx.modifiedOn) { if (updatedDoc.modifiedOn < tx.modifiedOn) {
await this.__updateDoc(q, updatedDoc, tx) await this.__updateDoc(q, updatedDoc, tx)
if (!this.match(q, updatedDoc)) { const updateRefresh = await this.checkUpdatedDocMatch(q, pos, updatedDoc)
if (q.options?.limit === q.result.length) { if (updateRefresh) return
return await this.refresh(q)
} else { } else {
q.result.splice(pos, 1) const currentRefresh = await this.getCurrentDoc(q, pos, updatedDoc._id)
q.total-- if (currentRefresh) return
}
} else {
q.result[pos] = updatedDoc
}
} else {
const current = await this.findOne(q._class, { _id: updatedDoc._id }, q.options)
if (current !== undefined && this.match(q, current)) {
q.result[pos] = current
} else {
if (q.options?.limit === q.result.length) {
return await this.refresh(q)
} else {
q.result.splice(pos, 1)
q.total--
}
}
} }
} }
this.sort(q, tx) this.sort(q, tx)
@ -388,7 +443,7 @@ export class LiveQuery extends TxProcessor implements Client {
await this.handleDocUpdateLookup(q, tx) await this.handleDocUpdateLookup(q, tx)
} }
private async handleDocUpdateLookup (q: Query, tx: TxUpdateDoc<Doc>): Promise<void> { private async handleDocUpdateLookup (q: Query, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): Promise<void> {
if (q.options?.lookup === undefined) return if (q.options?.lookup === undefined) return
const lookup = q.options.lookup const lookup = q.options.lookup
if (q.result instanceof Promise) { if (q.result instanceof Promise) {
@ -405,7 +460,11 @@ export class LiveQuery extends TxProcessor implements Client {
} }
} }
private async proccesLookupUpdateDoc (docs: Doc[], lookup: Lookup<Doc>, tx: TxUpdateDoc<Doc>): Promise<boolean> { private async proccesLookupUpdateDoc (
docs: Doc[],
lookup: Lookup<Doc>,
tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>
): Promise<boolean> {
let needCallback = false let needCallback = false
const lookupWays = this.getLookupWays(lookup, tx.objectClass) const lookupWays = this.getLookupWays(lookup, tx.objectClass)
for (const lookupWay of lookupWays) { for (const lookupWay of lookupWays) {
@ -418,7 +477,11 @@ export class LiveQuery extends TxProcessor implements Client {
let index = value.findIndex((p) => p._id === tx.objectId) let index = value.findIndex((p) => p._id === tx.objectId)
if (this.client.getHierarchy().isDerived(tx.objectClass, core.class.AttachedDoc)) { if (this.client.getHierarchy().isDerived(tx.objectClass, core.class.AttachedDoc)) {
if (reverseLookupKey !== undefined) { if (reverseLookupKey !== undefined) {
const reverseLookupValue = (tx.operations as any)[reverseLookupKey] const reverseLookupValue = (
tx._class === core.class.TxMixin
? ((tx as TxMixin<Doc, Doc>).attributes as any)
: ((tx as TxUpdateDoc<Doc>).operations as any)
)[reverseLookupKey]
if (index !== -1 && reverseLookupValue !== undefined && reverseLookupValue !== obj._id) { if (index !== -1 && reverseLookupValue !== undefined && reverseLookupValue !== obj._id) {
value.splice(index, 1) value.splice(index, 1)
index = -1 index = -1
@ -432,13 +495,21 @@ export class LiveQuery extends TxProcessor implements Client {
} }
} }
if (index !== -1) { if (index !== -1) {
TxProcessor.updateDoc2Doc(value[index], tx) if (tx._class === core.class.TxMixin) {
TxProcessor.updateMixin4Doc(value[index], tx as TxMixin<Doc, Doc>)
} else {
TxProcessor.updateDoc2Doc(value[index], tx as TxUpdateDoc<Doc>)
}
needCallback = true needCallback = true
} }
} else { } else {
if (obj[key] === tx.objectId) { if (obj[key] === tx.objectId) {
if (obj.$lookup[key] !== undefined) { if (obj.$lookup[key] !== undefined) {
TxProcessor.updateDoc2Doc(obj.$lookup[key], tx) if (tx._class === core.class.TxMixin) {
TxProcessor.updateMixin4Doc(obj.$lookup[key], tx as TxMixin<Doc, Doc>)
} else {
TxProcessor.updateDoc2Doc(obj.$lookup[key], tx as TxUpdateDoc<Doc>)
}
needCallback = true needCallback = true
} }
} }
@ -875,7 +946,7 @@ export class LiveQuery extends TxProcessor implements Client {
await this.__updateLookup(q, updatedDoc, ops) await this.__updateLookup(q, updatedDoc, ops)
} }
private sort (q: Query, tx: TxUpdateDoc<Doc>): void { private sort (q: Query, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): void {
const sort = q.options?.sort const sort = q.options?.sort
if (sort === undefined) return if (sort === undefined) return
let needSort = sort.modifiedBy !== undefined || sort.modifiedOn !== undefined let needSort = sort.modifiedBy !== undefined || sort.modifiedOn !== undefined
@ -884,8 +955,11 @@ export class LiveQuery extends TxProcessor implements Client {
if (needSort) resultSort(q.result as Doc[], sort, q._class, this.getHierarchy()) if (needSort) resultSort(q.result as Doc[], sort, q._class, this.getHierarchy())
} }
private checkNeedSort (sort: SortingQuery<Doc>, tx: TxUpdateDoc<Doc>): boolean { private checkNeedSort (sort: SortingQuery<Doc>, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): boolean {
const ops = tx.operations as any const ops =
tx._class === core.class.TxMixin
? (tx as TxMixin<Doc, Doc>).attributes
: ((tx as TxUpdateDoc<Doc>).operations as any)
for (const key in ops) { for (const key in ops) {
if (key.startsWith('$')) { if (key.startsWith('$')) {
for (const opKey in ops[key]) { for (const opKey in ops[key]) {