Skip to content

Commit

Permalink
fix(orm): support deep paths for index signature properties in query …
Browse files Browse the repository at this point in the history
…filter
  • Loading branch information
marcj committed Aug 30, 2023
1 parent 483699d commit d55ff22
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 21 deletions.
4 changes: 2 additions & 2 deletions packages/bson/src/bson-deserializer-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ export function deserializeObjectLiteral(type: TypeClass | TypeObjectLiteral, st

for (const signature of signatures) {
const check = isOptional(signature.type) ? `` : `elementType !== ${BSONType.UNDEFINED} &&`;
signatureLines.push(`else if (${check} ${getIndexCheck(state, i, signature.index)}) {
signatureLines.push(`else if (${check} ${getIndexCheck(state.compilerContext, i, signature.index)}) {
${executeTemplates(state.fork(`${object}[${i}]`).extendPath(new RuntimeCode(i)).forPropertyName(new RuntimeCode(i)), signature.type)}
continue;
}`);
Expand Down Expand Up @@ -931,7 +931,7 @@ export function bsonTypeGuardObjectLiteral(type: TypeClass | TypeObjectLiteral,
sortSignatures(signatures);

for (const signature of signatures) {
signatureLines.push(`else if (${getIndexCheck(state, i, signature.index)}) {
signatureLines.push(`else if (${getIndexCheck(state.compilerContext, i, signature.index)}) {
${executeTemplates(state.fork(valid).extendPath(new RuntimeCode(i)).forPropertyName(new RuntimeCode(i)), signature.type)}
if (!${valid}) break;
Expand Down
2 changes: 1 addition & 1 deletion packages/bson/src/bson-serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ function handleObjectLiteral(
? executeTemplates(propertyState.fork().forPropertyName(new RuntimeCode(i)), { kind: ReflectionKind.undefined })
: isNullable(signature.type) ? executeTemplates(propertyState.fork().forPropertyName(new RuntimeCode(i)), { kind: ReflectionKind.null }) : '';

signatureLines.push(`else if (${getIndexCheck(state, i, signature.index)}) {
signatureLines.push(`else if (${getIndexCheck(state.compilerContext, i, signature.index)}) {
if (${accessor} === undefined) {
${setUndefined}
} else {
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
// @ts-ignore
import { indent } from './indent.js';
import { hasProperty } from './core.js';

export class CompilerContext {
public readonly context = new Map<string, any>();
Expand Down Expand Up @@ -45,6 +46,15 @@ export class CompilerContext {
throw new Error(`Too many context variables (max ${this.maxReservedVariable})`);
}

set(values: { [name: string]: any }) {
for (const i in values) {
if (!hasProperty(values, i)) {
continue;
}
this.context.set(i, values[i]);
}
}

/**
* Returns always the same variable name for the same value.
* The variable name should not be set afterwards.
Expand Down
25 changes: 24 additions & 1 deletion packages/postgres/tests/postgres.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AutoIncrement, entity, PrimaryKey } from '@deepkit/type';
import { AutoIncrement, cast, entity, PrimaryKey } from '@deepkit/type';
import { expect, test } from '@jest/globals';
import pg from 'pg';
import { databaseFactory } from './factory.js';
Expand Down Expand Up @@ -158,3 +158,26 @@ test('for update/share', async () => {
const items = await database.query(Model).forUpdate().find();
expect(items).toHaveLength(2);
});

test('json field and query', async () => {
@entity.name('product').collection('products')
class Product {
id: number & PrimaryKey & AutoIncrement = 0;
raw?: { [key: string]: any };
}

const database = await databaseFactory([Product]);

await database.persist(cast<Product>({ raw: { productId: 1, name: 'first' } }));
await database.persist(cast<Product>({ raw: { productId: 2, name: 'second' } }));

{
const res = await database.query(Product).filter({ 'raw.productId': 1 }).find();
expect(res).toMatchObject([{ id: 1, raw: { productId: 1, name: 'first' } }]);
}

{
const res = await database.query(Product).filter({ 'raw.productId': 2 }).find();
expect(res).toMatchObject([{ id: 2, raw: { productId: 2, name: 'second' } }]);
}
});
2 changes: 1 addition & 1 deletion packages/type/src/change-detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ function createJITChangeDetectorForSnapshot(schema: ReflectionClass<any>, stateI
const itemAccessor = new ContainerAccessor('item', i);

for (const signature of signatures) {
signatureLines.push(`else if (${getIndexCheck(state, i, signature.index)}) {
signatureLines.push(`else if (${getIndexCheck(state.compilerContext, i, signature.index)}) {
${getComparator(signature.type, lastAccessor, currentAccessor, itemAccessor, i, '', state)}
}`);
}
Expand Down
18 changes: 16 additions & 2 deletions packages/type/src/path.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getTypeJitContainer, ReflectionKind, Type } from './reflection/type.js';
import { CompilerContext, toFastProperties } from '@deepkit/core';
import { ReceiveType, resolveReceiveType } from './reflection/reflection.js';
import { JitStack } from './serializer.js';
import { getIndexCheck, JitStack } from './serializer.js';

export type Resolver = (path: string) => Type | undefined;

Expand Down Expand Up @@ -31,7 +31,9 @@ function pathResolverCode(type: Type, compilerContext: CompilerContext, jitStack
}

export function resolvePath<T>(path: string, type?: ReceiveType<T>): Type {
const t = pathResolver(resolveReceiveType(type))(path);
const resolver = pathResolver(resolveReceiveType(type));
debugger;
const t = resolver(path);
if (!t) throw new Error(`No type found for path ${path}`);
return t;
}
Expand All @@ -44,6 +46,7 @@ export function pathResolver<T>(type?: ReceiveType<T>, jitStack: JitStack = new
if (type.kind === ReflectionKind.objectLiteral || type.kind === ReflectionKind.class) {
const compilerContext = new CompilerContext();
const lines: string[] = [];
const defaultCase: string[] = [];

for (const member of type.types) {
if (member.kind === ReflectionKind.propertySignature || member.kind === ReflectionKind.property) {
Expand All @@ -53,6 +56,14 @@ export function pathResolver<T>(type?: ReceiveType<T>, jitStack: JitStack = new
if (path === '') return ${compilerContext.reserveVariable('type', member)};
${pathResolverCode(member.type, compilerContext, jitStack)}
}`);
} else if (member.kind === ReflectionKind.indexSignature) {
const checkValid = compilerContext.reserveName('check');
defaultCase.push(`else if (${getIndexCheck(compilerContext, 'pathName', member.index)}) {
let ${checkValid} = false;
if (!${checkValid}) {
${pathResolverCode(member.type, compilerContext, jitStack)}
}
}`);
}
}

Expand All @@ -63,6 +74,9 @@ export function pathResolver<T>(type?: ReceiveType<T>, jitStack: JitStack = new
switch(pathName) {
${lines.join('\n')}
default: {
if (false) {} ${defaultCase.join('\n')}
}
}
`;

Expand Down
22 changes: 8 additions & 14 deletions packages/type/src/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,13 +510,7 @@ export class TemplateState {
}

setContext(values: { [name: string]: any }) {
for (const i in values) {
if (!hasProperty(values, i)) {
console.log('hasProperty is false: ', i, values[i], hasProperty(values, i));
continue;
}
this.compilerContext.context.set(i, values[i]);
}
this.compilerContext.set(values);
}

addCode(code: string) {
Expand Down Expand Up @@ -924,20 +918,20 @@ export function deserializeEmbedded(type: TypeClass | TypeObjectLiteral, state:
`;
}

export function getIndexCheck(state: TemplateState, i: string, type: Type): string {
export function getIndexCheck(context: CompilerContext, i: string, type: Type): string {
if (type.kind === ReflectionKind.number) {
state.setContext({ isNumeric: isNumeric });
context.set({ isNumeric: isNumeric });
return `isNumeric(${i})`;
} else if (type.kind === ReflectionKind.string || type.kind === ReflectionKind.any) {
return `'string' === typeof ${i}`;
} else if (type.kind === ReflectionKind.symbol) {
return `'symbol' === typeof ${i}`;
} else if (type.kind === ReflectionKind.templateLiteral) {
state.setContext({ extendTemplateLiteral: extendTemplateLiteral });
const typeVar = state.setVariable('type', type);
context.set({ extendTemplateLiteral: extendTemplateLiteral });
const typeVar = context.reserveVariable('type', type);
return `'string' === typeof ${i} && extendTemplateLiteral({kind: ${ReflectionKind.literal}, literal: ${i}}, ${typeVar})`;
} else if (type.kind === ReflectionKind.union) {
return '(' + type.types.map(v => getIndexCheck(state, i, v)).join(' || ') + ')';
return '(' + type.types.map(v => getIndexCheck(context, i, v)).join(' || ') + ')';
}
return '';
}
Expand Down Expand Up @@ -1154,7 +1148,7 @@ export function serializeObjectLiteral(type: TypeObjectLiteral | TypeClass, stat
sortSignatures(signatures);

for (const signature of signatures) {
signatureLines.push(`else if (${getIndexCheck(state, i, signature.index)} && ${groupFilter(signature.type)}) {
signatureLines.push(`else if (${getIndexCheck(state.compilerContext, i, signature.index)} && ${groupFilter(signature.type)}) {
${createConverterJSForMember(signature, state.fork(new ContainerAccessor(v, i), new ContainerAccessor(state.accessor, i)).extendPath(new RuntimeCode(i)))}
}`);
}
Expand Down Expand Up @@ -1321,7 +1315,7 @@ export function typeGuardObjectLiteral(type: TypeObjectLiteral | TypeClass, stat
for (const signature of signatures) {
const checkValid = state.compilerContext.reserveName('check');
const checkTemplate = executeTemplates(state.fork(checkValid, new ContainerAccessor(state.accessor, i)).extendPath(new RuntimeCode(i)), signature.type).trim();
signatureLines.push(`else if (${getIndexCheck(state, i, signature.index)}) {
signatureLines.push(`else if (${getIndexCheck(state.compilerContext, i, signature.index)}) {
let ${checkValid} = false;
${checkTemplate || `// no template found for signature.type.kind=${signature.type.kind}`}
if (!${checkValid}) ${state.setter} = false;
Expand Down

0 comments on commit d55ff22

Please sign in to comment.