fontcolor_theme
Deepkit ORM

Query

Eine Query ist ein Objekt, das beschreibt, wie Daten aus der Datenbank abgerufen oder verändert werden. Sie hat mehrere Methoden, um die Query zu beschreiben, und Abschlussmethoden, die sie ausführen. Der Database Adapter kann die Query-API auf viele Arten erweitern, um datenbankspezifische Features zu unterstützen.

Eine Query kann mit Database.query(T) oder Session.query(T) erstellt werden. Wir empfehlen Sessions, da dies die Performance verbessert.

@entity.name('user')
class User {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date;
    birthdate?: Date;
    visits: number = 0;

    constructor(public username: string) {
    }
}

const database = new Database(...);

//[ { username: 'User1' }, { username: 'User2' }, { username: 'User2' } ]
const users = await database.query(User).select('username').find();

Filter

Ein Filter kann angewendet werden, um die Ergebnismenge einzuschränken.

//einfache Filter
const users = await database.query(User).filter({name: 'User1'}).find();

//mehrere Filter, alle AND
const users = await database.query(User).filter({name: 'User1', id: 2}).find();

//Bereichsfilter: $gt, $lt, $gte, $lte (größer als, kleiner als, ...)
//entspricht WHERE created < NOW()
const users = await database.query(User).filter({created: {$lt: new Date}}).find();
//entspricht WHERE id > 500
const users = await database.query(User).filter({id: {$gt: 500}}).find();
//entspricht WHERE id >= 500
const users = await database.query(User).filter({id: {$gte: 500}}).find();

//Set-Filter: $in, $nin (in, not in)
//entspricht WHERE id IN (1, 2, 3)
const users = await database.query(User).filter({id: {$in: [1, 2, 3]}}).find();

//Regex-Filter
const users = await database.query(User).filter({username: {$regex: /User[0-9]+/}}).find();

//Gruppierung: $and, $nor, $or
//entspricht WHERE (username = 'User1') OR (username = 'User2')
const users = await database.query(User).filter({
    $or: [{username: 'User1'}, {username: 'User2'}]
}).find();


//verschachtelte Gruppierung
//entspricht WHERE username = 'User1' OR (username = 'User2' and id > 0)
const users = await database.query(User).filter({
    $or: [{username: 'User1'}, {username: 'User2', id: {$gt: 0}}]
}).find();


//verschachtelte Gruppierung
//entspricht WHERE username = 'User1' AND (created < NOW() OR id > 0)
const users = await database.query(User).filter({
    $and: [{username: 'User1'}, {$or: [{created: {$lt: new Date}, id: {$gt: 0}}]}]
}).find();

Equal

Greater / Smaller

RegExp

Grouping AND/OR

In

Select

Um die Felder einzugrenzen, die aus der Datenbank abgerufen werden, kann select('field1') verwendet werden.

const user = await database.query(User).select('username').findOne();
const user = await database.query(User).select('id', 'username').findOne();

Wichtig ist: Sobald die Felder mit select eingegrenzt werden, sind die Ergebnisse keine Instanzen der Entity mehr, sondern nur noch Object-Literals.

const user = await database.query(User).select('username').findOne();
user instanceof User; //false

Order

Mit orderBy(field, order) kann die Reihenfolge der Einträge geändert werden. orderBy kann mehrfach ausgeführt werden, um die Sortierung immer weiter zu verfeinern.

const users = await session.query(User).orderBy('created', 'desc').find();
const users = await session.query(User).orderBy('created', 'asc').find();

Pagination

Die Methoden itemsPerPage() und page() können verwendet werden, um die Ergebnisse zu paginieren. Die Page beginnt bei 1.

const users = await session.query(User).itemsPerPage(50).page(1).find();

Mit den alternativen Methoden limit und skip kann man manuell paginieren.

const users = await session.query(User).limit(5).skip(10).find();

[#database-join]

Join

Standardmäßig werden References aus der Entity weder in Queries berücksichtigt noch geladen. Um einen Join in die Query einzuschließen, ohne die Reference zu laden, verwenden Sie join() (Left Join) oder innerJoin(). Um einen Join in die Query einzuschließen und die Reference zu laden, verwenden Sie joinWith() oder innerJoinWith().

Alle folgenden Beispiele gehen von diesen Model-Schemas aus:

@entity.name('group')
class Group {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date;

    constructor(public username: string) {
    }
}

@entity.name('user')
class User {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date;

    group?: Group & Reference;

    constructor(public username: string) {
    }
}
//wählt nur Users mit einer zugewiesenen group (INNER JOIN)
const users = await session.query(User).innerJoin('group').find();
for (const user of users) {
    user.group; //Fehler, da die Referenz nicht geladen wurde
}
//wählt nur Users mit einer zugewiesenen group (INNER JOIN) und lädt die Relation
const users = await session.query(User).innerJoinWith('group').find();
for (const user of users) {
    user.group.name; //funktioniert
}

Um Join-Queries zu modifizieren, verwenden Sie dieselben Methoden, jedoch mit dem Prefix use: useJoin, useInnerJoin, useJoinWith oder useInnerJoinWith. Um die Join-Query-Modifikation zu beenden, verwenden Sie end(), um zur Parent-Query zurückzukehren.

//wählt nur Users mit einer group mit dem Namen 'admins' (INNER JOIN)
const users = await session.query(User)
    .useInnerJoinWith('group')
        .filter({name: 'admins'})
        .end()  // kehrt zur Parent-Query zurück
    .find();

for (const user of users) {
    user.group.name; //immer admin
}

Aggregation

Aggregation-Methoden ermöglichen es, Records zu zählen und Felder zu aggregieren.

Die folgenden Beispiele gehen von diesem Model-Schema aus:

@entity.name('file')
class File {
    id: number & PrimaryKey & AutoIncrement = 0;
    created: Date = new Date;

    downloads: number = 0;

    category: string = 'none';

    constructor(public path: string & Index) {
    }
}

groupBy ermöglicht es, das Ergebnis nach dem angegebenen Feld zu gruppieren.

await database.persist(
    cast<File>({path: 'file1', category: 'images'}),
    cast<File>({path: 'file2', category: 'images'}),
    cast<File>({path: 'file3', category: 'pdfs'})
);

//[ { category: 'images' }, { category: 'pdfs' } ]
await session.query(File).groupBy('category').find();

Es gibt mehrere Aggregation-Methoden: withSum, withAverage, withCount, withMin, withMax, withGroupConcat. Jede benötigt einen Feldnamen als erstes Argument und optional ein zweites Argument, um den Alias zu ändern.

//zuerst aktualisieren wir einige Records:
await database.query(File).filter({path: 'images/file1'}).patchOne({$inc: {downloads: 15}});
await database.query(File).filter({path: 'images/file2'}).patchOne({$inc: {downloads: 5}});

//[{ category: 'images', downloads: 20 },{ category: 'pdfs', downloads: 0 }]
await session.query(File).groupBy('category').withSum('downloads').find();

//[{ category: 'images', downloads: 10 },{ category: 'pdfs', downloads: 0 }]
await session.query(File).groupBy('category').withAverage('downloads').find();

//[ { category: 'images', amount: 2 }, { category: 'pdfs', amount: 1 } ]
await session.query(File).groupBy('category').withCount('id', 'amount').find();

Returning

Mit returning können bei Änderungen über patch und delete zusätzliche Felder angefordert werden.

Achtung: Nicht alle Database Adapter liefern Felder atomar zurück. Verwenden Sie Transactions, um Datenkonsistenz sicherzustellen.

await database.query(User).patchMany({visits: 0});

//{ modified: 1, returning: { visits: [ 5 ] }, primaryKeys: [ 1 ] }
const result = await database.query(User)
    .filter({username: 'User1'})
    .returning('username', 'visits')
    .patchOne({$inc: {visits: 5}});

Find

Gibt ein Array von Einträgen zurück, die dem angegebenen Filter entsprechen.

const users: User[] = await database.query(User).filter({username: 'Peter'}).find();

FindOne

Gibt einen Eintrag zurück, der dem angegebenen Filter entspricht. Wenn kein Eintrag gefunden wird, wird ein ItemNotFound Error ausgelöst.

const users: User = await database.query(User).filter({username: 'Peter'}).findOne();

FindOneOrUndefined

Gibt einen Eintrag zurück, der dem angegebenen Filter entspricht. Wenn kein Eintrag gefunden wird, wird undefined zurückgegeben.

const query = database.query(User).filter({username: 'Peter'});
const users: User|undefined = await query.findOneOrUndefined();

FindField

Gibt eine Liste eines Feldes zurück, die dem angegebenen Filter entspricht.

const usernames: string[] = await database.query(User).findField('username');

FindOneField

Gibt eine Liste eines Feldes zurück, die dem angegebenen Filter entspricht. Wenn kein Eintrag gefunden wird, wird ein ItemNotFound Error ausgelöst.

const username: string = await database.query(User).filter({id: 3}).findOneField('username');

Patch

Patch ist eine Change-Query, die die in der Query beschriebenen Records patcht. Die Methoden patchOne und patchMany beenden die Query und führen den Patch aus.

patchMany ändert alle Records in der Datenbank, die dem angegebenen Filter entsprechen. Wenn kein Filter gesetzt ist, wird die gesamte Tabelle geändert. Verwenden Sie patchOne, um jeweils nur einen Eintrag zu ändern.

await database.query(User).filter({username: 'Peter'}).patch({username: 'Peter2'});

await database.query(User).filter({username: 'User1'}).patchOne({birthdate: new Date});
await database.query(User).filter({username: 'User1'}).patchOne({$inc: {visits: 1}});

await database.query(User).patchMany({visits: 0});

Delete

deleteMany löscht alle Einträge in der Datenbank, die dem angegebenen Filter entsprechen. Wenn kein Filter gesetzt ist, wird die gesamte Tabelle gelöscht. Verwenden Sie deleteOne, um jeweils nur einen Eintrag zu löschen.

const result = await database.query(User)
    .filter({visits: 0})
    .deleteMany();

const result = await database.query(User).filter({id: 4}).deleteOne();

Has

Gibt zurück, ob mindestens ein Eintrag in der Datenbank existiert.

const userExists: boolean = await database.query(User).filter({username: 'Peter'}).has();

Count

Gibt die Anzahl der Einträge zurück.

const userCount: number = await database.query(User).count();

Lift

Eine Query zu liften bedeutet, ihr neue Funktionalität hinzuzufügen. Dies wird üblicherweise entweder von Plugins oder in komplexen Architekturen verwendet, um größere Query-Klassen in mehrere bequeme, wiederverwendbare Klassen aufzuteilen.

import { FilterQuery, Query } from '@deepkit/orm';

class UserQuery<T extends {birthdate?: Date}> extends Query<T>  {
    hasBirthday() {
        const start = new Date();
        start.setHours(0,0,0,0);
        const end = new Date();
        end.setHours(23,59,59,999);

        return this.filter({$and: [{birthdate: {$gte: start}}, {birthdate: {$lte: end}}]} as FilterQuery<T>);
    }
}

await session.query(User).lift(UserQuery).hasBirthday().find();
English中文 (Chinese)한국어 (Korean)日本語 (Japanese)Deutsch (German)