UBERF-4195: fix query after applying viewOptions (#3942)

Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
Vyacheslav Tumanov 2023-11-10 12:09:38 +05:00 committed by GitHub
parent 67a563d6c7
commit f8cdaba5f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 8 deletions

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import { Client } from '..'
import { Client, generateId, mergeQueries } from '..'
import type { Class, Doc, Obj, Ref } from '../classes'
import core from '../component'
import { Hierarchy } from '../hierarchy'
@ -396,4 +396,39 @@ describe('memdb', () => {
expect(e).toEqual(new Error('createDoc cannot be used for objects inherited from AttachedDoc'))
}
})
it('mergeQueries', () => {
const id1 = generateId()
const id2 = generateId()
const q1 = {
space: id1,
unique: 'item',
age: { $gt: 10 }
} as any
const q2 = {
space: { $in: [id1, id2] },
age: 30
} as any
const resCompare = {
space: id1,
unique: 'item',
age: { $gt: 30 }
} as any
expect(mergeQueries(q1, q2)).toEqual(resCompare)
expect(mergeQueries(q2, q1)).toEqual(resCompare)
const q3 = {
space: { $nin: [id1] },
age: 20
} as any
const resCompare2 = {
space: { $ne: id1 },
age: []
} as any
expect(mergeQueries(q2, q3)).toEqual(resCompare2)
expect(mergeQueries(q3, q2)).toEqual(resCompare2)
expect(mergeQueries({ age: { $lt: 20 } } as any, { age: { $gt: 25 } } as any)).toEqual({ age: { $gt: 25 } })
expect(mergeQueries({ age: { $gt: 25 } } as any, { age: { $lt: 20 } } as any)).toEqual({ age: { $lt: 20 } })
})
})

View File

@ -16,7 +16,8 @@
import { Account, AnyAttribute, Class, Doc, DocData, DocIndexState, IndexKind, Obj, Ref, Space } from './classes'
import core from './component'
import { Hierarchy } from './hierarchy'
import { FindResult } from './storage'
import { DocumentQuery, FindResult } from './storage'
import { isPredicate } from './predicate'
function toHex (value: number, chars: number): string {
const result = value.toString(16)
@ -331,3 +332,64 @@ export class RateLimitter {
await await Promise.race(this.processingQueue.values())
}
}
export function mergeQueries<T extends Doc> (query1: DocumentQuery<T>, query2: DocumentQuery<T>): DocumentQuery<T> {
const q = Object.assign({}, query1)
for (const k in query2) {
if (!Object.keys(query1).includes(k)) {
Object.assign(q, { [k]: query2[k] })
continue
}
Object.assign(q, { [k]: getInNiN(query1[k], query2[k]) })
if (isPredicate(query2[k]) || isPredicate(query1[k])) {
const toIterate = isPredicate(query2[k]) ? query2[k] : query1[k]
for (const x in toIterate) {
if (['$lt', '$gt'].includes(x)) {
const val1 = isPredicate(query1[k]) ? query1[k][x] : query1[k]
const val2 = isPredicate(query2[k]) ? query2[k][x] : query2[k]
if (x === '$lt') {
Object.assign(q, { [k]: { $lt: val1 < val2 ? val1 : val2 } })
continue
}
if (x === '$gt') {
Object.assign(q, { [k]: { $gt: val1 > val2 ? val1 : val2 } })
}
}
}
}
}
return q
}
function getInNiN (query1: any, query2: any): Object {
const aIn =
(typeof query1 === 'object' && '$in' in query1 ? query1.$in : undefined) ??
(typeof query1 !== 'object' && query1 !== undefined ? [query1] : [])
const aNIn =
(typeof query1 === 'object' && '$nin' in query1 ? query1.$nin : undefined) ??
(typeof query1 === 'object' && query1.$ne !== undefined ? [query1.$ne] : [])
const bIn =
(typeof query2 === 'object' && '$in' in query2 ? query2.$in : undefined) ??
(typeof query2 !== 'object' && query2 !== undefined ? [query2] : [])
const bNIn =
(typeof query2 === 'object' && '$nin' in query2 ? query2.$nin : undefined) ??
(typeof query2 === 'object' && query2.$ne !== undefined ? [query2.$ne] : [])
const finalIn =
aIn.length - bIn.length < 0 ? bIn.filter((c: any) => aIn.includes(c)) : aIn.filter((c: any) => bIn.includes(c))
const finalNin = Array.from(new Set([...aNIn, ...bNIn]))
if (finalIn.length === 1 && finalNin.length === 0) {
return aIn[0]
}
if (finalIn.length === 0 && finalNin.length === 1) {
return { $ne: finalNin[0] }
}
const res: any = {}
if (finalIn.length > 0) {
res.$in = finalIn
}
if (finalNin.length > 0) {
res.$nin = finalNin
}
if (aIn.length === 1 && bIn.length === 1) return []
return res
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Class, Doc, DocumentQuery, Ref, Space, WithLookup } from '@hcengineering/core'
import { Class, Doc, DocumentQuery, mergeQueries, Ref, Space, WithLookup } from '@hcengineering/core'
import { IntlString, getEmbeddedLabel } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { Project, ProjectType, ProjectTypeCategory } from '@hcengineering/task'
@ -65,7 +65,7 @@
$: spacesQ.query(task.class.Project, { type: mode as Ref<ProjectType> }, (result) => {
spaces = result
})
let resultQuery: DocumentQuery<Doc>
$: query = { ...(baseQuery ?? {}), ...(viewlet?.baseQuery ?? {}), space: { $in: spaces.map((it) => it._id) } }
$: searchQuery = search === '' ? query : { ...query, $search: search }
$: resultQuery = searchQuery
@ -154,7 +154,7 @@
query={searchQuery}
{viewOptions}
on:change={(e) => {
resultQuery = { ...query, ...e.detail }
resultQuery = mergeQueries(query, e.detail)
}}
/>
<Component

View File

@ -22,6 +22,7 @@
DocumentUpdate,
FindOptions,
generateId,
mergeQueries,
Ref
} from '@hcengineering/core'
import { Item, Kanban as KanbanUI } from '@hcengineering/kanban'
@ -87,7 +88,7 @@
}
let resultQuery: DocumentQuery<any> = { ...query }
$: getResultQuery(query, viewOptionsConfig, viewOptions).then((p) => (resultQuery = { ...p, ...query }))
$: getResultQuery(query, viewOptionsConfig, viewOptions).then((p) => (resultQuery = mergeQueries(p, query)))
const client = getClient()
const hierarchy = client.getHierarchy()

View File

@ -13,7 +13,16 @@
// limitations under the License.
-->
<script lang="ts">
import core, { Class, Doc, DocumentQuery, FindOptions, Ref, Space, RateLimitter } from '@hcengineering/core'
import core, {
Class,
Doc,
DocumentQuery,
FindOptions,
Ref,
Space,
RateLimitter,
mergeQueries
} from '@hcengineering/core'
import { IntlString, getResource } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { AnyComponent, AnySvelteComponent } from '@hcengineering/ui'
@ -59,7 +68,7 @@
let resultQuery: DocumentQuery<Doc> = query
$: getResultQuery(query, viewOptionsConfig, viewOptions).then((p) => {
resultQuery = { ...query, ...p }
resultQuery = mergeQueries(p, query) as DocumentQuery<Doc>
})
$: queryNoLookup = noLookup(resultQuery)