Skip to content

Commit

Permalink
feat(orm): new selector API, still work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed May 21, 2024
1 parent 6ad04d3 commit 1108062
Show file tree
Hide file tree
Showing 51 changed files with 2,552 additions and 541 deletions.
20 changes: 20 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This docker compose sets up the full environment for all tests (or at least that's the goal)

services:
postgres:
image: pgvector/pgvector:pg16
ports:
- "5432:5432"
environment:
- POSTGRES_HOST_AUTH_METHOD=trust

mongo:
image: mongo:5.0
ports:
- "27017:27017"

mongo_replica:
image: mongo:5.0
ports:
- "27018:27017"
command: mongod --replSet rs0
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,9 @@
"packages/topsort"
]
},
"packageManager": "[email protected]"
"packageManager": "[email protected]",
"dependencies": {
"mitata": "^0.1.11",
"tsx": "^4.7.1"
}
}
2 changes: 2 additions & 0 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,3 +889,5 @@ export function assertDefined<T>(value: T): asserts value is NonNullable<T> {
throw new Error(`Value is not defined`);
}
}

export const dynamicImport = new Function('modulePath', 'return import(modulePath)') as (modulePath: string) => Promise<any>;
14 changes: 7 additions & 7 deletions packages/event/src/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
*/

import { ClassType, CompilerContext, CustomError, isClass, isFunction } from '@deepkit/core';
import { injectedFunction } from '@deepkit/injector';
import { InjectorContext, InjectorModule } from '@deepkit/injector';
import { injectedFunction, InjectorContext, InjectorModule } from '@deepkit/injector';
import {
ClassDecoratorResult,
createClassDecoratorContext,
createPropertyDecoratorContext,
PropertyDecoratorResult,
ReflectionClass
ReflectionClass,
} from '@deepkit/type';

export type EventListenerCallback<T> = (event: T, ...args: any[]) => any | Promise<any>;
Expand Down Expand Up @@ -344,7 +343,7 @@ export class EventDispatcher implements EventDispatcherInterface {
* but cheap in creating event dispatchers.
*/
export class ForkedEventDispatcher implements EventDispatcherInterface {
protected listenerMap = new Map<EventToken<any>, { entries: EventListenerContainerEntry[], sorted: boolean }>();
protected listenerMap?: Map<EventToken<any>, { entries: EventListenerContainerEntry[], sorted: boolean }>;

constructor(protected parent: EventDispatcherInterface, protected injector: InjectorContext) {
}
Expand All @@ -353,7 +352,7 @@ export class ForkedEventDispatcher implements EventDispatcherInterface {
await this.parent.dispatch(eventToken, eventIn, injector);
const event = resolveEvent(eventToken, eventIn);

const listeners = this.listenerMap.get(eventToken);
const listeners = this.listenerMap?.get(eventToken);
if (!listeners) return;

if (!listeners.sorted) {
Expand Down Expand Up @@ -385,9 +384,10 @@ export class ForkedEventDispatcher implements EventDispatcherInterface {
}

public getListeners(eventToken: EventToken<any>): { entries: EventListenerContainerEntry[], sorted: boolean } {
let listeners = this.listenerMap.get(eventToken);
let listeners = this.listenerMap?.get(eventToken);
if (!listeners) {
listeners = { entries: [], sorted: true };
if (!this.listenerMap) this.listenerMap = new Map();
this.listenerMap.set(eventToken, listeners);
}
return listeners;
Expand All @@ -408,7 +408,7 @@ export class ForkedEventDispatcher implements EventDispatcherInterface {
}

hasListeners(eventToken: EventToken<any>): boolean {
if (this.listenerMap.has(eventToken)) return true;
if (this.listenerMap?.has(eventToken)) return true;
return this.parent.hasListeners(eventToken);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/framework/tests/service-container.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ test('database integration', async () => {
const app = new App({
providers: [MyDatabase],
listeners: [
Query.onFetch.listen(event => {
Query.onFind.listen(event => {
onFetch.push(event);
})
],
Expand Down
5 changes: 4 additions & 1 deletion packages/orm-integration/src/test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Database, DatabaseAdapter, DatabasePlugin } from '@deepkit/orm';
import { AbstractClassType } from '@deepkit/core';
import { AbstractClassType, formatError } from '@deepkit/core';
import { ReflectionClass, Type } from '@deepkit/type';

export type DatabaseFactory<T extends DatabaseAdapter = DatabaseAdapter> = (entities?: (Type | ReflectionClass<any> | AbstractClassType)[], plugins?: DatabasePlugin[]) => Promise<Database<T>>;
Expand All @@ -15,6 +15,9 @@ export async function executeTest(test: (factory: DatabaseFactory) => any, facto

try {
await test(collectedFactory);
} catch (error) {
console.log(formatError(error));
throw error;
} finally {
for (const db of databases) {
db.disconnect(true);
Expand Down
1 change: 1 addition & 0 deletions packages/orm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export * from './src/memory-db.js';
export * from './src/type.js';
export * from './src/logger.js';
export * from './src/event.js';
export * from './src/select.js';
export * from './src/reference.js';
export * from './src/plugin/plugin.js';
export * from './src/plugin/soft-delete-plugin.js';
Expand Down
25 changes: 23 additions & 2 deletions packages/orm/src/database-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,29 @@
*/

import { OrmEntity } from './type.js';
import { AbstractClassType, arrayRemoveItem, ClassType, getClassName, getClassTypeFromInstance, isClass, stringifyValueWithType } from '@deepkit/core';
import { is, isSameType, ItemChanges, PrimaryKeyFields, ReceiveType, ReflectionClass, ReflectionKind, stringifyType, Type } from '@deepkit/type';
import {
AbstractClassType,
arrayRemoveItem,
ClassType,
getClassName,
getClassTypeFromInstance,
isClass,
stringifyValueWithType,
} from '@deepkit/core';
import {
is,
isSameType,
ItemChanges,
PrimaryKeyFields,
ReceiveType,
ReflectionClass,
ReflectionKind,
stringifyType,
Type,
} from '@deepkit/type';
import { Query } from './query.js';
import { DatabaseSession, DatabaseTransaction } from './database-session.js';
import { Query2Resolver } from './select.js';

export abstract class DatabaseAdapterQueryFactory {
abstract createQuery<T extends OrmEntity>(type?: ReceiveType<T> | ClassType<T> | AbstractClassType<T> | ReflectionClass<T>): Query<T>;
Expand Down Expand Up @@ -93,6 +112,8 @@ export class MigrateOptions {
export abstract class DatabaseAdapter {
abstract queryFactory(session: DatabaseSession<this>): DatabaseAdapterQueryFactory;

createQuery2Resolver?(session: DatabaseSession<this>): Query2Resolver<any>;

rawFactory(session: DatabaseSession<this>): RawFactory<any> {
return new RawFactory();
};
Expand Down
17 changes: 9 additions & 8 deletions packages/orm/src/database-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import type { DatabaseAdapter, DatabasePersistence, DatabasePersistenceChangeSet } from './database-adapter.js';
import { DatabaseEntityRegistry } from './database-adapter.js';
import { DatabaseValidationError, OrmEntity } from './type.js';
import { AbstractClassType, ClassType, CustomError, forwardTypeArguments } from '@deepkit/core';
import { AbstractClassType, ClassType, CustomError } from '@deepkit/core';
import {
getPrimaryKeyExtractor,
isReferenceInstance,
Expand Down Expand Up @@ -346,12 +346,13 @@ export class DatabaseSession<ADAPTER extends DatabaseAdapter = DatabaseAdapter>
}

this.query = query as any;
// this.query = {} as any;

const factory = this.adapter.rawFactory(this);
this.raw = (...args: any[]) => {
forwardTypeArguments(this.raw, factory.create);
return factory.create(...args);
};
// const factory = this.adapter.rawFactory(this);
// this.raw = (...args: any[]) => {
// forwardTypeArguments(this.raw, factory.create);
// return factory.create(...args);
// };
}

/**
Expand Down Expand Up @@ -556,8 +557,8 @@ export class DatabaseSession<ADAPTER extends DatabaseAdapter = DatabaseAdapter>
//make sure all stuff in the identity-map is known
const round = this.getCurrentRound();
if (this.withIdentityMap) {
for (const map of this.identityMap.registry.values()) {
for (const item of map.values()) {
for (const map of Object.values(this.identityMap.registry)) {
for (const item of Object.values(map)) {
round.add(item.ref);
}
}
Expand Down
19 changes: 15 additions & 4 deletions packages/orm/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@
* You should have received a copy of the MIT License along with this program.
*/

import { AbstractClassType, ClassType, forwardTypeArguments, getClassName, getClassTypeFromInstance } from '@deepkit/core';
import {
AbstractClassType,
ClassType,
forwardTypeArguments,
getClassName,
getClassTypeFromInstance,
} from '@deepkit/core';
import {
entityAnnotation,
EntityOptions,
Expand All @@ -19,7 +25,7 @@ import {
ReflectionClass,
ReflectionKind,
resolveReceiveType,
Type
Type,
} from '@deepkit/type';
import { DatabaseAdapter, DatabaseEntityRegistry, MigrateOptions } from './database-adapter.js';
import { DatabaseSession } from './database-session.js';
Expand All @@ -32,6 +38,7 @@ import { Stopwatch } from '@deepkit/stopwatch';
import { getClassState, getInstanceState, getNormalizedPrimaryKey } from './identity-map.js';
import { EventDispatcher, EventDispatcherUnsubscribe, EventListenerCallback, EventToken } from '@deepkit/event';
import { DatabasePlugin, DatabasePluginRegistry } from './plugin/plugin.js';
import { Query2 } from './select.js';

/**
* Hydrates not completely populated item and makes it completely accessible.
Expand Down Expand Up @@ -173,6 +180,12 @@ export class Database<ADAPTER extends DatabaseAdapter = DatabaseAdapter> {
}
}

from<T extends OrmEntity>(type?: ReceiveType<T>) {
const session = this.createSession();
if (!this.adapter.createQuery2Resolver) throw new Error('Adapter has no createQuery2Resolver method');
return new Query2(ReflectionClass.fromType(resolveReceiveType(type)), session, this.adapter.createQuery2Resolver(session));
}

registerPlugin(...plugins: DatabasePlugin[]): void {
for (const plugin of plugins) {
this.pluginRegistry.add(plugin);
Expand All @@ -182,8 +195,6 @@ export class Database<ADAPTER extends DatabaseAdapter = DatabaseAdapter> {

static createClass<T extends DatabaseAdapter>(name: string, adapter: T, schemas: (ClassType | ReflectionClass<any>)[] = []): ClassType<Database<T>> {
class C extends Database<T> {
bla!: string;

constructor() {
super(adapter, schemas);
this.name = name;
Expand Down
23 changes: 16 additions & 7 deletions packages/orm/src/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import type { Changes } from '@deepkit/type';
import { PrimaryKeyType, ReflectionClass } from '@deepkit/type';
import type { DatabasePersistenceChangeSet } from './database-adapter.js';
import type { DatabaseSession } from './database-session.js';
import type { Query } from './query.js';
import type { DeleteResult, PatchResult } from './type.js';
import { OrmEntity } from './type.js';

Expand Down Expand Up @@ -79,7 +78,8 @@ export class QueryDatabaseEvent<T extends OrmEntity> extends DatabaseEvent {
constructor(
public readonly databaseSession: DatabaseSession<any>,
public readonly classSchema: ReflectionClass<T>,
public query: Query<T>,
// public query: Query2<T>,
public query: any, //TODO change back
) {
super();
}
Expand All @@ -94,7 +94,8 @@ export class DatabaseErrorEvent extends DatabaseEvent {
public readonly error: Error,
public readonly databaseSession: DatabaseSession<any>,
public readonly classSchema?: ReflectionClass<any>,
public readonly query?: Query<any>,
// public readonly query?: Query2<any>,
public readonly query?: any, //TODO change back
) {
super();
}
Expand Down Expand Up @@ -127,7 +128,8 @@ export class QueryDatabaseDeleteEvent<T extends OrmEntity> extends DatabaseEvent
constructor(
public readonly databaseSession: DatabaseSession<any>,
public readonly classSchema: ReflectionClass<T>,
public query: Query<T>,
// public query: Query2<T>,
public query: any, //TODO change back
public readonly deleteResult: DeleteResult<T>,
) {
super();
Expand All @@ -139,12 +141,11 @@ export class QueryDatabaseDeleteEvent<T extends OrmEntity> extends DatabaseEvent
}

export class QueryDatabasePatchEvent<T extends object> extends DatabaseEvent {
public returning: (keyof T & string)[] = [];

constructor(
public readonly databaseSession: DatabaseSession<any>,
public readonly classSchema: ReflectionClass<T>,
public query: Query<T>,
// public query: Query2<T>,
public query: any, //TODO change back
public readonly patch: Changes<T>,
public readonly patchResult: PatchResult<T>,
) {
Expand All @@ -155,3 +156,11 @@ export class QueryDatabasePatchEvent<T extends object> extends DatabaseEvent {
return this.classSchema.isSchemaOf(classType);
}
}

export const onFind: EventToken<QueryDatabaseEvent<any>> = new EventToken('orm.select.find');

export const onDeletePre: EventToken<QueryDatabaseDeleteEvent<any>> = new EventToken('orm.select.delete.pre');
export const onDeletePost: EventToken<QueryDatabaseDeleteEvent<any>> = new EventToken('orm.select.delete.post');

export const onPatchPre: EventToken<QueryDatabasePatchEvent<any>> = new EventToken('orm.select.patch.pre');
export const onPatchPost: EventToken<QueryDatabasePatchEvent<any>> = new EventToken('orm.select.patch.post');
4 changes: 2 additions & 2 deletions packages/orm/src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
Serializer,
typeSettings,
UnpopulatedCheck,
unpopulatedSymbol
unpopulatedSymbol,
} from '@deepkit/type';
import { DatabaseQueryModel } from './query.js';
import { capitalize, ClassType } from '@deepkit/core';
Expand Down Expand Up @@ -230,7 +230,7 @@ export class Formatter {
if (item) {
const fromDatabase = getInstanceState(classState, item).isFromDatabase();

//if its proxy a unhydrated proxy then we update property values
//if its proxy an unhydrated proxy then we update property values
if (fromDatabase && !isReferenceHydrated(item)) {
//we automatically hydrate proxy object once someone fetches them from the database.
//or we update a stale instance
Expand Down
Loading

0 comments on commit 1108062

Please sign in to comment.