mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-09 09:20:54 +00:00
Enum sorting (#2808)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
746f2a809f
commit
27a3264f9b
@ -27,7 +27,7 @@ import { toFindResult } from './utils'
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export abstract class MemDb extends TxProcessor {
|
export abstract class MemDb extends TxProcessor implements Storage {
|
||||||
private readonly objectsByClass = new Map<Ref<Class<Doc>>, Doc[]>()
|
private readonly objectsByClass = new Map<Ref<Class<Doc>>, Doc[]>()
|
||||||
private readonly objectById = new Map<Ref<Doc>, Doc>()
|
private readonly objectById = new Map<Ref<Doc>, Doc>()
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ export abstract class MemDb extends TxProcessor {
|
|||||||
result = matchQuery(result, query, _class, this.hierarchy)
|
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
|
const total = result.length
|
||||||
result = result.slice(0, options?.limit)
|
result = result.slice(0, options?.limit)
|
||||||
const tresult = this.hierarchy.clone(result) as WithLookup<T>[]
|
const tresult = this.hierarchy.clone(result) as WithLookup<T>[]
|
||||||
@ -194,7 +194,7 @@ export abstract class MemDb extends TxProcessor {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class TxDb extends MemDb implements Storage {
|
export class TxDb extends MemDb {
|
||||||
protected txCreateDoc (tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
protected txCreateDoc (tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
||||||
throw new Error('Method not implemented.')
|
throw new Error('Method not implemented.')
|
||||||
}
|
}
|
||||||
@ -222,7 +222,7 @@ export class TxDb extends MemDb implements Storage {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class ModelDb extends MemDb implements Storage {
|
export class ModelDb extends MemDb {
|
||||||
protected override async txCreateDoc (tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
protected override async txCreateDoc (tx: TxCreateDoc<Doc>): Promise<TxResult> {
|
||||||
this.addDoc(TxProcessor.createDoc2Doc(tx))
|
this.addDoc(TxProcessor.createDoc2Doc(tx))
|
||||||
return {}
|
return {}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { DocumentQuery } from '.'
|
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 { Hierarchy } from './hierarchy'
|
||||||
import { getObjectValue } from './objvalue'
|
import { getObjectValue } from './objvalue'
|
||||||
import { createPredicates, isPredicate } from './predicate'
|
import { createPredicates, isPredicate } from './predicate'
|
||||||
import { SortingOrder, SortingQuery } from './storage'
|
import { SortingOrder, SortingQuery, Storage } from './storage'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -30,19 +31,37 @@ function isArrayValueCheck<T, P> (val: T, value: P): boolean {
|
|||||||
return Array.isArray(val) && !Array.isArray(value) && val.includes(value)
|
return Array.isArray(val) && !Array.isArray(value) && val.includes(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEnumValue<T extends Doc> (
|
||||||
|
key: string,
|
||||||
|
_class: Ref<Class<T>>,
|
||||||
|
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
|
* @public
|
||||||
*/
|
*/
|
||||||
export function resultSort<T extends Doc> (
|
export async function resultSort<T extends Doc> (
|
||||||
result: T[],
|
result: T[],
|
||||||
sortOptions: SortingQuery<T>,
|
sortOptions: SortingQuery<T>,
|
||||||
_class: Ref<Class<T>>,
|
_class: Ref<Class<T>>,
|
||||||
hierarchy: Hierarchy
|
hierarchy: Hierarchy,
|
||||||
): void {
|
modelDb: Storage
|
||||||
|
): Promise<void> {
|
||||||
|
const enums = await getEnums(_class, sortOptions, hierarchy, modelDb)
|
||||||
const sortFunc = (a: any, b: any): number => {
|
const sortFunc = (a: any, b: any): number => {
|
||||||
for (const key in sortOptions) {
|
for (const key in sortOptions) {
|
||||||
const aValue = getValue(key, a, _class, hierarchy)
|
const _enum = enums[key]
|
||||||
const bValue = getValue(key, b, _class, hierarchy)
|
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])
|
const result = getSortingResult(aValue, bValue, sortOptions[key])
|
||||||
if (result !== 0) return result
|
if (result !== 0) return result
|
||||||
}
|
}
|
||||||
@ -67,6 +86,28 @@ function getSortingResult (aValue: any, bValue: any, order: SortingOrder): numbe
|
|||||||
return res * order
|
return res * order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getEnums<T extends Doc> (
|
||||||
|
_class: Ref<Class<T>>,
|
||||||
|
sortOptions: SortingQuery<T>,
|
||||||
|
hierarchy: Hierarchy,
|
||||||
|
modelDb: Storage
|
||||||
|
): Promise<Record<string, Enum>> {
|
||||||
|
const res: Record<string, Enum> = {}
|
||||||
|
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<T extends Doc> (key: string, obj: any, _class: Ref<Class<T>>, hierarchy: Hierarchy): any {
|
function getValue<T extends Doc> (key: string, obj: any, _class: Ref<Class<T>>, hierarchy: Hierarchy): any {
|
||||||
const tkey = checkMixinKey(key, _class, hierarchy)
|
const tkey = checkMixinKey(key, _class, hierarchy)
|
||||||
let value = getObjectValue(tkey, obj)
|
let value = getObjectValue(tkey, obj)
|
||||||
|
@ -394,7 +394,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
|||||||
if (currentRefresh) return {}
|
if (currentRefresh) return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sort(q, tx)
|
await this.sort(q, tx)
|
||||||
const udoc = q.result.find((p) => p._id === tx.objectId)
|
const udoc = q.result.find((p) => p._id === tx.objectId)
|
||||||
await this.updatedDocCallback(udoc, q)
|
await this.updatedDocCallback(udoc, q)
|
||||||
} else if (isMixin) {
|
} else if (isMixin) {
|
||||||
@ -478,11 +478,11 @@ export class LiveQuery extends TxProcessor implements Client {
|
|||||||
if (currentRefresh) return
|
if (currentRefresh) return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sort(q, tx)
|
await this.sort(q, tx)
|
||||||
const udoc = q.result.find((p) => p._id === tx.objectId)
|
const udoc = q.result.find((p) => p._id === tx.objectId)
|
||||||
await this.updatedDocCallback(udoc, q)
|
await this.updatedDocCallback(udoc, q)
|
||||||
} else if (await this.matchQuery(q, tx)) {
|
} 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)
|
const udoc = q.result.find((p) => p._id === tx.objectId)
|
||||||
await this.updatedDocCallback(udoc, q)
|
await this.updatedDocCallback(udoc, q)
|
||||||
}
|
}
|
||||||
@ -500,7 +500,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
|||||||
|
|
||||||
if (needCallback) {
|
if (needCallback) {
|
||||||
if (q.options?.sort !== undefined) {
|
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)
|
await this.callback(q)
|
||||||
}
|
}
|
||||||
@ -725,7 +725,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
|||||||
q.total++
|
q.total++
|
||||||
|
|
||||||
if (q.options?.sort !== undefined) {
|
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) {
|
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 (needCallback) {
|
||||||
if (q.options?.sort !== undefined) {
|
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)
|
await this.callback(q)
|
||||||
}
|
}
|
||||||
@ -863,7 +863,7 @@ export class LiveQuery extends TxProcessor implements Client {
|
|||||||
}
|
}
|
||||||
if (needCallback) {
|
if (needCallback) {
|
||||||
if (q.options?.sort !== undefined) {
|
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)
|
await this.callback(q)
|
||||||
}
|
}
|
||||||
@ -998,13 +998,13 @@ 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> | TxMixin<Doc, Doc>): void {
|
private async sort (q: Query, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): Promise<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
|
||||||
if (!needSort) needSort = this.checkNeedSort(sort, tx)
|
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<Doc>, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): boolean {
|
private checkNeedSort (sort: SortingQuery<Doc>, tx: TxUpdateDoc<Doc> | TxMixin<Doc, Doc>): boolean {
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
let applications: Map<Ref<Vacancy>, ApplicationInfo> = new Map<Ref<Vacancy>, ApplicationInfo>()
|
let applications: Map<Ref<Vacancy>, ApplicationInfo> = new Map<Ref<Vacancy>, ApplicationInfo>()
|
||||||
|
|
||||||
const applicantQuery = createQuery()
|
const applicantQuery = createQuery()
|
||||||
$: applicantQuery.query(
|
applicantQuery.query(
|
||||||
recruit.class.Applicant,
|
recruit.class.Applicant,
|
||||||
{},
|
{},
|
||||||
(res) => {
|
(res) => {
|
||||||
|
@ -22,6 +22,8 @@ import core, {
|
|||||||
Domain,
|
Domain,
|
||||||
DOMAIN_MODEL,
|
DOMAIN_MODEL,
|
||||||
DOMAIN_TX,
|
DOMAIN_TX,
|
||||||
|
Enum,
|
||||||
|
EnumOf,
|
||||||
escapeLikeForRegexp,
|
escapeLikeForRegexp,
|
||||||
FindOptions,
|
FindOptions,
|
||||||
FindResult,
|
FindResult,
|
||||||
@ -331,6 +333,39 @@ abstract class MongoAdapterBase implements DbAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async fillSortPipeline<T extends Doc>(
|
||||||
|
clazz: Ref<Class<T>>,
|
||||||
|
options: FindOptions<T>,
|
||||||
|
pipeline: any[]
|
||||||
|
): Promise<void> {
|
||||||
|
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<T extends Doc>(
|
private async lookup<T extends Doc>(
|
||||||
clazz: Ref<Class<T>>,
|
clazz: Ref<Class<T>>,
|
||||||
query: DocumentQuery<T>,
|
query: DocumentQuery<T>,
|
||||||
@ -347,14 +382,7 @@ abstract class MongoAdapterBase implements DbAdapter {
|
|||||||
}
|
}
|
||||||
pipeline.push(match)
|
pipeline.push(match)
|
||||||
const resultPipeline: any[] = []
|
const resultPipeline: any[] = []
|
||||||
if (options.sort !== undefined) {
|
await this.fillSortPipeline(clazz, options, pipeline)
|
||||||
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 })
|
|
||||||
}
|
|
||||||
if (options.limit !== undefined) {
|
if (options.limit !== undefined) {
|
||||||
resultPipeline.push({ $limit: options.limit })
|
resultPipeline.push({ $limit: options.limit })
|
||||||
}
|
}
|
||||||
@ -442,6 +470,30 @@ abstract class MongoAdapterBase implements DbAdapter {
|
|||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async isEnumSortKey<T extends Doc>(_class: Ref<Class<T>>, key: string): Promise<Enum | undefined> {
|
||||||
|
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<T extends Doc>(_class: Ref<Class<T>>, options?: FindOptions<T>): 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<T extends Doc>(
|
async findAll<T extends Doc>(
|
||||||
_class: Ref<Class<T>>,
|
_class: Ref<Class<T>>,
|
||||||
query: DocumentQuery<T>,
|
query: DocumentQuery<T>,
|
||||||
@ -449,7 +501,7 @@ abstract class MongoAdapterBase implements DbAdapter {
|
|||||||
): Promise<FindResult<T>> {
|
): Promise<FindResult<T>> {
|
||||||
// TODO: rework this
|
// TODO: rework this
|
||||||
if (options !== null && options !== undefined) {
|
if (options !== null && options !== undefined) {
|
||||||
if (options.lookup !== undefined) {
|
if (options.lookup !== undefined || this.isEnumSort(_class, options)) {
|
||||||
return await this.lookup(_class, query, options)
|
return await this.lookup(_class, query, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user