fontcolor_theme
Deepkit ORM

クエリ

クエリは、データベースからデータを取得または変更する方法を記述するオブジェクトです。クエリを記述するための複数の Method と、それらを実行する終端 Method を持ちます。データベースアダプターは、データベース固有の機能をサポートするために、クエリ API をさまざまな方法で拡張できます。

Database.query(T) または Session.query(T) を使用してクエリを作成できます。パフォーマンスが向上するため、Session の使用を推奨します。

@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();

フィルター

フィルターを適用して結果セットを絞り込むことができます。

//シンプルなフィルター
const users = await database.query(User).filter({name: 'User1'}).find();

//複数フィルター。すべて AND
const users = await database.query(User).filter({name: 'User1', id: 2}).find();

//範囲フィルター: $gt, $lt, $gte, $lte(より大きい、より小さい、...)
//WHERE created < NOW() と同等
const users = await database.query(User).filter({created: {$lt: new Date}}).find();
//WHERE id > 500 と同等
const users = await database.query(User).filter({id: {$gt: 500}}).find();
//WHERE id >= 500 と同等
const users = await database.query(User).filter({id: {$gte: 500}}).find();

//集合フィルター: $in, $nin(IN, NOT IN)
//WHERE id IN (1, 2, 3) と同等
const users = await database.query(User).filter({id: {$in: [1, 2, 3]}}).find();

//正規表現フィルター
const users = await database.query(User).filter({username: {$regex: /User[0-9]+/}}).find();

//グルーピング: $and, $nor, $or
//WHERE (username = 'User1') OR (username = 'User2') と同等
const users = await database.query(User).filter({
    $or: [{username: 'User1'}, {username: 'User2'}]
}).find();


//ネストされたグルーピング
//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();


//ネストされたグルーピング
//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();

等価

大なり/小なり

正規表現

グルーピング AND/OR

IN

Select

データベースから受け取るフィールドを絞り込むには、select('field1') を使用します。

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

select を使ってフィールドを絞り込んだ時点で、結果はエンティティのインスタンスではなく、オブジェクトリテラルのみになることに注意してください。

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

並び順

orderBy(field, order) でエントリの並び順を変更できます。 orderBy は複数回実行でき、順序を段階的に詳細化できます。

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

ページネーション

itemsPerPage()page() を使って、結果をページングできます。ページは 1 から開始します。

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

代替の limitskip を使えば手動でページングできます。

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

[#database-join]

結合

デフォルトでは、エンティティからの参照はクエリに含まれず、ロードもされません。参照をロードせずにクエリに結合を含めるには、join()(LEFT JOIN)または innerJoin() を使用します。参照をロードしつつクエリに結合を含めるには、joinWith() または innerJoinWith() を使用します。

以下の例はすべて、次のモデルスキーマを前提としています。

@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) {
    }
}
//グループが割り当てられたユーザーのみを選択(INNER JOIN)
const users = await session.query(User).innerJoin('group').find();
for (const user of users) {
    user.group; //参照がロードされていないため error
}
//グループが割り当てられたユーザーのみを選択(INNER JOIN)し、リレーションをロード
const users = await session.query(User).innerJoinWith('group').find();
for (const user of users) {
    user.group.name; //動作します
}

結合クエリを修正するには、同じ Method を use プレフィックス付きで使用します: useJoin, useInnerJoin, useJoinWith, useInnerJoinWith。結合クエリの修正を終了し、親クエリに戻るには end() を使用します。

//名前が 'admins' のグループが割り当てられているユーザーのみを選択(INNER JOIN)
const users = await session.query(User)
    .useInnerJoinWith('group')
        .filter({name: 'admins'})
        .end()  // 親クエリに戻る
    .find();

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

集計

集計 Method により、レコードのカウントやフィールドの集計が可能です。

次の例は、このモデルスキーマを前提としています。

@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 は指定したフィールドで結果をグルーピングします。

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();

利用できる集計 Method は複数あります: withSum, withAverage, withCount, withMin, withMax, withGroupConcat。いずれも第1引数にフィールド名を取り、別名を変更するための任意の第2引数を取ります。

// まず、いくつかのレコードを更新します:
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

patch および delete による変更時に、returning で追加のフィールドを要求できます。

注意: すべてのデータベースアダプターがフィールドをアトミックに返すわけではありません。データ整合性を確保するためにトランザクションを使用してください。

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

指定したフィルターに一致するエントリの配列を返します。

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

FindOne

指定したフィルターに一致するアイテムを返します。 アイテムが見つからない場合、ItemNotFound Error がスローされます。

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

FindOneOrUndefined

指定したフィルターに一致するエントリを返します。 エントリが見つからない場合は、undefined が返されます。

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

FindField

指定したフィルターに一致するフィールドのリストを返します。

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

FindOneField

指定したフィルターに一致するフィールドのリストを返します。 エントリが見つからない場合、ItemNotFound Error がスローされます。

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

Patch

Patch は、クエリで記述されたレコードをパッチする変更クエリです。patchOnepatchMany の Method がクエリを終了し、パッチを実行します。

patchMany は、指定したフィルターに一致するすべてのレコードをデータベースで変更します。フィルターが設定されていない場合は、テーブル全体が変更されます。1件のみ変更するには patchOne を使用します。

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 は、指定したフィルターに一致するすべてのエントリをデータベースから削除します。 フィルターが設定されていない場合は、テーブル全体が削除されます。1件のみ削除するには deleteOne を使用します。

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

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

Has

データベースに少なくとも1件のエントリが存在するかどうかを返します。

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

Count

エントリ数を返します。

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

Lift

クエリのリフトとは、クエリに新しい機能を追加することを意味します。これは通常、プラグインや複雑なアーキテクチャで、大きなクエリ Class を複数の使いやすく再利用可能な Class に分割するために使用されます。

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)