UBER-803: Fix slow filter (#3634)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-08-25 17:15:41 +07:00 committed by GitHub
parent 97abd2fc12
commit 72c026d505
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 28 deletions

View File

@ -224,14 +224,14 @@ export class LiveQuery {
const unsub = liveQuery.query(
_class,
piplineQuery.query ?? query,
query,
(result) => {
// If we have one more request after this one, no need to do something.
if (id === this.reqId) {
callback(result)
}
},
piplineQuery.options ?? options
options
)
this.unsubscribe = () => {
unsub()

View File

@ -17,7 +17,7 @@
import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
import attachment from '@hcengineering/attachment'
import chunter from '@hcengineering/chunter'
import contact, { Employee, PersonAccount, getName } from '@hcengineering/contact'
import contact, { Person, PersonAccount, getName } from '@hcengineering/contact'
import core, { AnyAttribute, Class, Doc, Ref, TxCUD, getCurrentAccount } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
@ -56,7 +56,7 @@
let viewlet: TxDisplayViewlet | undefined
let props: any
let account: PersonAccount | undefined
let employee: Employee | undefined
let person: Person | undefined
let model: AttributeModel[] = []
let modelIcon: Asset | undefined = undefined
let iconComponent: AnyComponent | undefined = undefined
@ -67,7 +67,7 @@
$: if (tx.tx._id !== ptx?.tx._id) {
if (tx.tx.modifiedBy !== account?._id) {
account = undefined
employee = undefined
person = undefined
}
viewlet = undefined
props = undefined
@ -107,10 +107,10 @@
$: account &&
employeeQuery.query(
contact.mixin.Employee,
{ _id: account.person as Ref<Employee> },
contact.class.Person,
{ _id: account.person },
(res) => {
;[employee] = res
;[person] = res
},
{ limit: 1 }
)
@ -191,7 +191,7 @@
{#if showIcon}
{#if withAvatar}
<div class="msgactivity-avatar">
<Component is={contact.component.Avatar} props={{ avatar: employee?.avatar, size: 'medium' }} />
<Component is={contact.component.Avatar} props={{ avatar: person?.avatar, size: 'medium' }} />
</div>
{:else}
<div class="msgactivity-icon">
@ -212,8 +212,8 @@
<div class="msgactivity-content__header">
<div class="msgactivity-content__title labels-row">
<span class={withAvatar ? 'bold' : 'strong'}>
{#if employee}
{getName(client.getHierarchy(), employee)}
{#if person}
{getName(client.getHierarchy(), person)}
{:else}
<Label label={core.string.System} />
{/if}

View File

@ -96,8 +96,8 @@ export class AggregationMiddleware extends BasePresentationMiddleware implements
const statusFields: Array<Attribute<Doc>> = []
const allAttrs = h.getAllAttributes(_class)
const updatedQuery: DocumentQuery<T> = { ...(ret.query ?? query) }
const finalOptions = { ...(ret.options ?? options ?? {}) }
const updatedQuery: DocumentQuery<T> = h.clone(ret.query ?? query)
const finalOptions = h.clone(ret.options ?? options ?? {})
await this.updateQueryOptions<T>(allAttrs, h, statusFields, updatedQuery, finalOptions)
@ -139,11 +139,13 @@ export class AggregationMiddleware extends BasePresentationMiddleware implements
const docFields: Array<Attribute<Doc>> = []
const h = this.client.getHierarchy()
const allAttrs = h.getAllAttributes(_class)
const finalOptions = options ?? {}
const finalOptions = h.clone(options ?? {})
await this.updateQueryOptions<T>(allAttrs, h, docFields, query, finalOptions)
const fquery = h.clone(query ?? {})
const result = await this.provideFindAll(_class, query, finalOptions)
await this.updateQueryOptions<T>(allAttrs, h, docFields, fquery, finalOptions)
const result = await this.provideFindAll(_class, fquery, finalOptions)
// We need to add $
if (docFields.length > 0) {
// We need to update $lookup for doc fields and provide $doc group fields.

View File

@ -24,16 +24,19 @@ import core, {
DocumentQuery,
FindOptions,
Hierarchy,
IdMap,
Ref,
SortingOrder,
SortingRules,
Space,
Status,
StatusCategory,
StatusManager,
StatusValue,
Tx,
WithLookup,
matchQuery
matchQuery,
toIdMap
} from '@hcengineering/core'
import { LiveQuery } from '@hcengineering/query'
import { AggregationManager, GrouppingManager } from '@hcengineering/view'
@ -47,8 +50,15 @@ export const statusStore = writable<StatusManager>(new StatusManager([]))
*/
export class StatusAggregationManager implements AggregationManager {
docs: Doc[] | undefined
docsByName: Map<string, Status[]> = new Map<string, Status[]>()
mgr: StatusManager | Promise<StatusManager> | undefined
statusCategory: IdMap<StatusCategory> = new Map()
query: (() => void) | undefined
categoryQuery: (() => void) | undefined
categoryPromise!: Promise<void>
lq: LiveQuery
lqCallback: () => void
@ -68,14 +78,27 @@ export class StatusAggregationManager implements AggregationManager {
}
return this.mgr
}
this.categoryPromise = new Promise((resolve) => {
this.categoryQuery = this.lq.query(core.class.StatusCategory, {}, (res) => {
this.statusCategory = toIdMap(res)
resolve()
})
})
this.mgr = new Promise<StatusManager>((resolve) => {
this.query = this.lq.query(
core.class.Status,
{},
(res) => {
const first = this.docs === undefined
this.docs = res
const newMap = new Map<string, Status[]>()
for (const d of this.docs as Array<WithLookup<Status>>) {
const n = d.name.toLowerCase().trim()
newMap.set(n, [...(newMap.get(n) ?? []), d])
}
this.mgr = new StatusManager(res)
this.docsByName = newMap
statusStore.set(this.mgr)
if (!first) {
this.lqCallback()
@ -83,9 +106,6 @@ export class StatusAggregationManager implements AggregationManager {
resolve(this.mgr)
},
{
lookup: {
category: core.class.StatusCategory
},
sort: {
rank: SortingOrder.Ascending
}
@ -98,6 +118,7 @@ export class StatusAggregationManager implements AggregationManager {
close (): void {
this.query?.()
this.categoryQuery?.()
}
async notifyTx (tx: Tx): Promise<void> {
@ -110,15 +131,13 @@ export class StatusAggregationManager implements AggregationManager {
async categorize (target: Array<Ref<Doc>>, attr: AnyAttribute): Promise<Array<Ref<Doc>>> {
const mgr = await this.getManager()
const idMap = mgr.getIdMap()
for (const sid of [...target]) {
const s = mgr.getIdMap().get(sid as Ref<Status>) as WithLookup<Status>
const s = idMap.get(sid as Ref<Status>) as WithLookup<Status>
if (s !== undefined) {
let statuses = mgr.getDocs()
statuses = statuses.filter(
(it) =>
it.ofAttribute === attr._id &&
it.name.toLowerCase().trim() === s.name.toLowerCase().trim() &&
it._id !== s._id
const statuses = (this.docsByName.get(s.name.toLowerCase().trim()) ?? []).filter(
(it) => it.ofAttribute === attr._id && it._id !== s._id
)
target.push(...statuses.map((it) => it._id))
}
@ -140,10 +159,11 @@ export class StatusAggregationManager implements AggregationManager {
// Fill custom sorting.
let statuses = (await this.getManager()).getDocs()
statuses = statuses.filter((it) => it.ofAttribute === attr._id)
await this.categoryPromise
statuses.sort((a, b) => {
let ret = 0
if (a.category !== undefined && b.category !== undefined) {
ret = (a.$lookup?.category?.order ?? 0) - (b.$lookup?.category?.order ?? 0)
ret = (this.statusCategory.get(a.category)?.order ?? 0) - (this.statusCategory.get(b.category)?.order ?? 0)
}
if (ret === 0) {
if (a.name.toLowerCase().trim() === b.name.toLowerCase().trim()) {