From 0dcdc190216e1ca45cb01d749185b4b5d99720e3 Mon Sep 17 00:00:00 2001 From: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> Date: Sat, 16 Apr 2022 09:00:45 +0600 Subject: [PATCH] add gt lt query (#1415) Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com> --- packages/core/src/__tests__/memdb.test.ts | 92 ++++++++++++++++++----- packages/core/src/predicate.ts | 21 +++++- packages/core/src/storage.ts | 12 ++- 3 files changed, 104 insertions(+), 21 deletions(-) diff --git a/packages/core/src/__tests__/memdb.test.ts b/packages/core/src/__tests__/memdb.test.ts index 1a05ba8473..954f13b59a 100644 --- a/packages/core/src/__tests__/memdb.test.ts +++ b/packages/core/src/__tests__/memdb.test.ts @@ -36,13 +36,15 @@ class ClientModel extends ModelDb implements Client { return this } - async findOne<T extends Doc>(_class: Ref<Class<T>>, query: DocumentQuery<T>, options?: FindOptions<T>): Promise<WithLookup<T> | undefined> { + async findOne<T extends Doc>( + _class: Ref<Class<T>>, + query: DocumentQuery<T>, + options?: FindOptions<T> + ): Promise<WithLookup<T> | undefined> { return (await this.findAll(_class, query, options)).shift() } - async close (): Promise<void> { - - } + async close (): Promise<void> {} } async function createModel (): Promise<{ model: ClientModel, hierarchy: Hierarchy, txDb: TxDb }> { @@ -84,7 +86,13 @@ describe('memdb', () => { const result2 = await client.findAll(core.class.Space, {}) expect(result2).toHaveLength(3) - await client.createDoc(core.class.Space, core.space.Model, { private: false, name: 'NewSpace', description: '', members: [], archived: false }) + await client.createDoc(core.class.Space, core.space.Model, { + private: false, + name: 'NewSpace', + description: '', + members: [], + archived: false + }) const result3 = await client.findAll(core.class.Space, {}) expect(result3).toHaveLength(4) }) @@ -92,7 +100,7 @@ describe('memdb', () => { it('should query model', async () => { const { model } = await createModel() const result = await model.findAll(core.class.Class, {}) - const names = result.map(d => d._id) + const names = result.map((d) => d._id) expect(names.includes(core.class.Class)).toBe(true) const result2 = await model.findAll(core.class.Class, { _id: undefined }) expect(result2.length).toBe(0) @@ -108,7 +116,9 @@ describe('memdb', () => { const { model } = await createModel() const ops = new TxOperations(model, core.account.System) - await ops.createMixin<Doc, TestMixin>(core.class.Obj, core.class.Class, core.space.Model, test.mixin.TestMixin, { arr: ['hello'] }) + await ops.createMixin<Doc, TestMixin>(core.class.Obj, core.class.Class, core.space.Model, test.mixin.TestMixin, { + arr: ['hello'] + }) const objClass = (await model.findAll(core.class.Class, { _id: core.class.Obj }))[0] as any expect(objClass['test:mixin:TestMixin'].arr).toEqual(expect.arrayContaining(['hello'])) @@ -160,6 +170,24 @@ describe('memdb', () => { space: { $in: [core.space.Model, core.space.Tx] } }) expect(multipleParam.length).toBeGreaterThan(5) + + const classes = await model.findAll(core.class.Class, {}) + const gt = await model.findAll(core.class.Class, { + kind: { $gt: 1 } + }) + expect(gt.length).toBe(classes.filter((p) => p.kind > 1).length) + const gte = await model.findAll(core.class.Class, { + kind: { $gte: 1 } + }) + expect(gte.length).toBe(classes.filter((p) => p.kind >= 1).length) + const lt = await model.findAll(core.class.Class, { + kind: { $lt: 1 } + }) + expect(lt.length).toBe(classes.filter((p) => p.kind < 1).length) + const lte = await model.findAll(core.class.Class, { + kind: { $lt: 1 } + }) + expect(lte.length).toBe(classes.filter((p) => p.kind <= 1).length) }) it('should query model like params', async () => { @@ -260,25 +288,51 @@ describe('memdb', () => { const spaces = await client.findAll(core.class.Space, {}) expect(spaces).toHaveLength(2) - const first = await client.addCollection(test.class.TestComment, core.space.Model, spaces[0]._id, spaces[0]._class, 'comments', { - message: 'msg' - }) + const first = await client.addCollection( + test.class.TestComment, + core.space.Model, + spaces[0]._id, + spaces[0]._class, + 'comments', + { + message: 'msg' + } + ) - const second = await client.addCollection(test.class.TestComment, core.space.Model, first, test.class.TestComment, 'comments', { - message: 'msg2' - }) + const second = await client.addCollection( + test.class.TestComment, + core.space.Model, + first, + test.class.TestComment, + 'comments', + { + message: 'msg2' + } + ) await client.addCollection(test.class.TestComment, core.space.Model, spaces[0]._id, spaces[0]._class, 'comments', { message: 'msg3' }) - const simple = await client.findAll(test.class.TestComment, { _id: first }, { lookup: { attachedTo: spaces[0]._class } }) + const simple = await client.findAll( + test.class.TestComment, + { _id: first }, + { lookup: { attachedTo: spaces[0]._class } } + ) expect(simple[0].$lookup?.attachedTo).toEqual(spaces[0]) - const nested = await client.findAll(test.class.TestComment, { _id: second }, { lookup: { attachedTo: [test.class.TestComment, { attachedTo: spaces[0]._class } as any] } }) + const nested = await client.findAll( + test.class.TestComment, + { _id: second }, + { lookup: { attachedTo: [test.class.TestComment, { attachedTo: spaces[0]._class } as any] } } + ) expect((nested[0].$lookup?.attachedTo as any).$lookup?.attachedTo).toEqual(spaces[0]) - const reverse = await client.findAll(spaces[0]._class, { _id: spaces[0]._id }, { lookup: { _id: { comments: test.class.TestComment } } }) + const reverse = await client.findAll( + spaces[0]._class, + { _id: spaces[0]._id }, + { lookup: { _id: { comments: test.class.TestComment } } } + ) expect((reverse[0].$lookup as any).comments).toHaveLength(2) }) @@ -306,7 +360,11 @@ describe('memdb', () => { text: 'qwe2' }) - const results = await client.findAll(test.class.TestMixinTodo, {}, { lookup: { attachedTo: test.mixin.TaskMixinTodos } }) + const results = await client.findAll( + test.class.TestMixinTodo, + {}, + { lookup: { attachedTo: test.mixin.TaskMixinTodos } } + ) expect(results.length).toEqual(2) const attached = results[0].$lookup?.attachedTo expect(attached).toBeDefined() diff --git a/packages/core/src/predicate.ts b/packages/core/src/predicate.ts index 41bd8f5a10..aa360664c1 100644 --- a/packages/core/src/predicate.ts +++ b/packages/core/src/predicate.ts @@ -49,7 +49,10 @@ const predicates: Record<string, PredicateFactory> = { }, $like: (query: string, propertyKey: string): Predicate => { - const searchString = query.split('%').map(it => escapeLikeForRegexp(it)).join('.*') + const searchString = query + .split('%') + .map((it) => escapeLikeForRegexp(it)) + .join('.*') const regex = RegExp(`^${searchString}$`, 'i') return (docs) => execPredicate(docs, propertyKey, (value) => regex.test(value)) @@ -58,11 +61,25 @@ const predicates: Record<string, PredicateFactory> = { $regex: (o: { $regex: string, $options: string }, propertyKey: string): Predicate => { const re = new RegExp(o.$regex, o.$options) return (docs) => execPredicate(docs, propertyKey, (value) => value.match(re) !== null) + }, + $gt: (o, propertyKey) => { + return (docs) => execPredicate(docs, propertyKey, (value) => value > o) + }, + $gte: (o, propertyKey) => { + return (docs) => execPredicate(docs, propertyKey, (value) => value >= o) + }, + $lt: (o, propertyKey) => { + return (docs) => execPredicate(docs, propertyKey, (value) => value < o) + }, + $lte: (o, propertyKey) => { + return (docs) => execPredicate(docs, propertyKey, (value) => value <= o) } } export function isPredicate (o: Record<string, any>): boolean { - if (o === null || typeof o !== 'object') { return false } + if (o === null || typeof o !== 'object') { + return false + } const keys = Object.keys(o) return keys.length > 0 && keys.every((key) => key.startsWith('$')) } diff --git a/packages/core/src/storage.ts b/packages/core/src/storage.ts index 58ea8d17a8..6237a34b0f 100644 --- a/packages/core/src/storage.ts +++ b/packages/core/src/storage.ts @@ -24,6 +24,10 @@ import type { Tx } from './tx' export type QuerySelector<T> = { $in?: T[] $nin?: T[] + $gt?: T extends number ? number : never + $gte?: T extends number ? number : never + $lt?: T extends number ? number : never + $lte?: T extends number ? number : never $like?: string $regex?: string $options?: string @@ -49,11 +53,15 @@ export type DocumentQuery<T extends Doc> = { /** * @public */ -export type ToClassRefT<T extends object, P extends keyof T> = T[P] extends Ref<infer X> | null | undefined ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] : never +export type ToClassRefT<T extends object, P extends keyof T> = T[P] extends Ref<infer X> | null | undefined + ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] + : never /** * @public */ -export type ToClassRefTA<T extends object, P extends keyof T> = T[P] extends Array<Ref<infer X>> | null | undefined ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] : never +export type ToClassRefTA<T extends object, P extends keyof T> = T[P] extends Array<Ref<infer X>> | null | undefined + ? Ref<Class<X>> | [Ref<Class<X>>, Lookup<X>] + : never /** * @public */