diff --git a/packages/core/src/memdb.ts b/packages/core/src/memdb.ts index 2de78a0cc2..3e9e07c909 100644 --- a/packages/core/src/memdb.ts +++ b/packages/core/src/memdb.ts @@ -27,7 +27,7 @@ import { toFindResult } from './utils' /** * @public */ -export abstract class MemDb extends TxProcessor { +export abstract class MemDb extends TxProcessor implements Storage { private readonly objectsByClass = new Map>, Doc[]>() private readonly objectById = new Map, Doc>() @@ -162,7 +162,7 @@ export abstract class MemDb extends TxProcessor { result = matchQuery(result, query, _class, this.hierarchy) } - if (options?.sort !== undefined) resultSort(result, options?.sort, _class, this.hierarchy) + if (options?.sort !== undefined) await resultSort(result, options?.sort, _class, this.hierarchy, this) const total = result.length result = result.slice(0, options?.limit) const tresult = this.hierarchy.clone(result) as WithLookup[] @@ -194,7 +194,7 @@ export abstract class MemDb extends TxProcessor { * * @public */ -export class TxDb extends MemDb implements Storage { +export class TxDb extends MemDb { protected txCreateDoc (tx: TxCreateDoc): Promise { throw new Error('Method not implemented.') } @@ -222,7 +222,7 @@ export class TxDb extends MemDb implements Storage { * * @public */ -export class ModelDb extends MemDb implements Storage { +export class ModelDb extends MemDb { protected override async txCreateDoc (tx: TxCreateDoc): Promise { this.addDoc(TxProcessor.createDoc2Doc(tx)) return {} diff --git a/packages/core/src/query.ts b/packages/core/src/query.ts index 190b7b40ad..8b468c831f 100644 --- a/packages/core/src/query.ts +++ b/packages/core/src/query.ts @@ -1,9 +1,10 @@ import { DocumentQuery } from '.' -import { Class, Doc, Ref } from './classes' +import { Class, Doc, Enum, EnumOf, Ref } from './classes' +import core from './component' import { Hierarchy } from './hierarchy' import { getObjectValue } from './objvalue' import { createPredicates, isPredicate } from './predicate' -import { SortingOrder, SortingQuery } from './storage' +import { SortingOrder, SortingQuery, Storage } from './storage' /** * @public @@ -30,19 +31,37 @@ function isArrayValueCheck (val: T, value: P): boolean { return Array.isArray(val) && !Array.isArray(value) && val.includes(value) } +function getEnumValue ( + key: string, + _class: Ref>, + hierarchy: Hierarchy, + obj: any, + _enum: Enum +): number { + const tkey = checkMixinKey(key, _class, hierarchy) + const value = getObjectValue(tkey, obj) + const index = _enum.enumValues.findIndex((p) => p === value) + return index === -1 ? _enum.enumValues.length : index +} + /** * @public */ -export function resultSort ( +export async function resultSort ( result: T[], sortOptions: SortingQuery, _class: Ref>, - hierarchy: Hierarchy -): void { + hierarchy: Hierarchy, + modelDb: Storage +): Promise { + const enums = await getEnums(_class, sortOptions, hierarchy, modelDb) const sortFunc = (a: any, b: any): number => { for (const key in sortOptions) { - const aValue = getValue(key, a, _class, hierarchy) - const bValue = getValue(key, b, _class, hierarchy) + const _enum = enums[key] + const aValue = + _enum !== undefined ? getEnumValue(key, _class, hierarchy, a, _enum) : getValue(key, a, _class, hierarchy) + const bValue = + _enum !== undefined ? getEnumValue(key, _class, hierarchy, b, _enum) : getValue(key, b, _class, hierarchy) const result = getSortingResult(aValue, bValue, sortOptions[key]) if (result !== 0) return result } @@ -67,6 +86,28 @@ function getSortingResult (aValue: any, bValue: any, order: SortingOrder): numbe return res * order } +async function getEnums ( + _class: Ref>, + sortOptions: SortingQuery, + hierarchy: Hierarchy, + modelDb: Storage +): Promise> { + const res: Record = {} + for (const key in sortOptions) { + const attr = hierarchy.findAttribute(_class, key) + if (attr !== undefined) { + if (attr !== undefined) { + if (attr.type._class === core.class.EnumOf) { + const ref = (attr.type as EnumOf).of + const enu = await modelDb.findAll(core.class.Enum, { _id: ref }) + res[key] = enu[0] + } + } + } + } + return res +} + function getValue (key: string, obj: any, _class: Ref>, hierarchy: Hierarchy): any { const tkey = checkMixinKey(key, _class, hierarchy) let value = getObjectValue(tkey, obj) diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index ee8fdf202d..5309ced0e0 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -394,7 +394,7 @@ export class LiveQuery extends TxProcessor implements Client { if (currentRefresh) return {} } } - this.sort(q, tx) + await this.sort(q, tx) const udoc = q.result.find((p) => p._id === tx.objectId) await this.updatedDocCallback(udoc, q) } else if (isMixin) { @@ -478,11 +478,11 @@ export class LiveQuery extends TxProcessor implements Client { if (currentRefresh) return } } - this.sort(q, tx) + await this.sort(q, tx) const udoc = q.result.find((p) => p._id === tx.objectId) await this.updatedDocCallback(udoc, q) } else if (await this.matchQuery(q, tx)) { - this.sort(q, tx) + await this.sort(q, tx) const udoc = q.result.find((p) => p._id === tx.objectId) await this.updatedDocCallback(udoc, q) } @@ -500,7 +500,7 @@ export class LiveQuery extends TxProcessor implements Client { if (needCallback) { if (q.options?.sort !== undefined) { - resultSort(q.result, q.options?.sort, q._class, this.getHierarchy()) + await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.client.getModel()) } await this.callback(q) } @@ -725,7 +725,7 @@ export class LiveQuery extends TxProcessor implements Client { q.total++ if (q.options?.sort !== undefined) { - resultSort(q.result, q.options?.sort, q._class, this.getHierarchy()) + await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.client.getModel()) } if (q.options?.limit !== undefined && q.result.length > q.options.limit) { @@ -761,7 +761,7 @@ export class LiveQuery extends TxProcessor implements Client { if (needCallback) { if (q.options?.sort !== undefined) { - resultSort(q.result, q.options?.sort, q._class, this.getHierarchy()) + await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.getModel()) } await this.callback(q) } @@ -863,7 +863,7 @@ export class LiveQuery extends TxProcessor implements Client { } if (needCallback) { if (q.options?.sort !== undefined) { - resultSort(q.result, q.options?.sort, q._class, this.getHierarchy()) + await resultSort(q.result, q.options?.sort, q._class, this.getHierarchy(), this.getModel()) } await this.callback(q) } @@ -998,13 +998,13 @@ export class LiveQuery extends TxProcessor implements Client { await this.__updateLookup(q, updatedDoc, ops) } - private sort (q: Query, tx: TxUpdateDoc | TxMixin): void { + private async sort (q: Query, tx: TxUpdateDoc | TxMixin): Promise { const sort = q.options?.sort if (sort === undefined) return let needSort = sort.modifiedBy !== undefined || sort.modifiedOn !== undefined if (!needSort) needSort = this.checkNeedSort(sort, tx) - if (needSort) resultSort(q.result as Doc[], sort, q._class, this.getHierarchy()) + if (needSort) await resultSort(q.result as Doc[], sort, q._class, this.getHierarchy(), this.client.getModel()) } private checkNeedSort (sort: SortingQuery, tx: TxUpdateDoc | TxMixin): boolean { diff --git a/plugins/recruit-resources/src/components/Vacancies.svelte b/plugins/recruit-resources/src/components/Vacancies.svelte index afeb5ac5b6..5c7dbd8c87 100644 --- a/plugins/recruit-resources/src/components/Vacancies.svelte +++ b/plugins/recruit-resources/src/components/Vacancies.svelte @@ -41,7 +41,7 @@ let applications: Map, ApplicationInfo> = new Map, ApplicationInfo>() const applicantQuery = createQuery() - $: applicantQuery.query( + applicantQuery.query( recruit.class.Applicant, {}, (res) => { diff --git a/server/mongo/src/storage.ts b/server/mongo/src/storage.ts index 80118979bd..be831712a8 100644 --- a/server/mongo/src/storage.ts +++ b/server/mongo/src/storage.ts @@ -22,6 +22,8 @@ import core, { Domain, DOMAIN_MODEL, DOMAIN_TX, + Enum, + EnumOf, escapeLikeForRegexp, FindOptions, FindResult, @@ -331,6 +333,39 @@ abstract class MongoAdapterBase implements DbAdapter { } } + private async fillSortPipeline( + clazz: Ref>, + options: FindOptions, + pipeline: any[] + ): Promise { + if (options.sort !== undefined) { + const sort = {} as any + for (const _key in options.sort) { + const key = this.translateKey(_key, clazz) + const enumOf = await this.isEnumSortKey(clazz, _key) + if (enumOf === undefined) { + sort[key] = options.sort[_key] === SortingOrder.Ascending ? 1 : -1 + } else { + const branches = enumOf.enumValues.map((value, index) => { + return { case: { $eq: [`$${key}`, value] }, then: index } + }) + pipeline.push({ + $addFields: { + [`sort_${key}`]: { + $switch: { + branches, + default: enumOf.enumValues.length + } + } + } + }) + sort[`sort_${key}`] = options.sort[_key] === SortingOrder.Ascending ? 1 : -1 + } + } + pipeline.push({ $sort: sort }) + } + } + private async lookup( clazz: Ref>, query: DocumentQuery, @@ -347,14 +382,7 @@ abstract class MongoAdapterBase implements DbAdapter { } pipeline.push(match) const resultPipeline: any[] = [] - if (options.sort !== undefined) { - const sort = {} as any - for (const _key in options.sort) { - const key: string = this.translateKey(_key, clazz) - sort[key] = options.sort[_key] === SortingOrder.Ascending ? 1 : -1 - } - pipeline.push({ $sort: sort }) - } + await this.fillSortPipeline(clazz, options, pipeline) if (options.limit !== undefined) { resultPipeline.push({ $limit: options.limit }) } @@ -442,6 +470,30 @@ abstract class MongoAdapterBase implements DbAdapter { return key } + private async isEnumSortKey(_class: Ref>, key: string): Promise { + const attr = this.hierarchy.findAttribute(_class, key) + if (attr !== undefined) { + if (attr.type._class === core.class.EnumOf) { + const ref = (attr.type as EnumOf).of + const res = await this.modelDb.findAll(core.class.Enum, { _id: ref }) + return res[0] + } + } + } + + private isEnumSort(_class: Ref>, options?: FindOptions): boolean { + if (options?.sort === undefined) return false + for (const key in options.sort) { + const attr = this.hierarchy.findAttribute(_class, key) + if (attr !== undefined) { + if (attr.type._class === core.class.EnumOf) { + return true + } + } + } + return false + } + async findAll( _class: Ref>, query: DocumentQuery, @@ -449,7 +501,7 @@ abstract class MongoAdapterBase implements DbAdapter { ): Promise> { // TODO: rework this if (options !== null && options !== undefined) { - if (options.lookup !== undefined) { + if (options.lookup !== undefined || this.isEnumSort(_class, options)) { return await this.lookup(_class, query, options) } }