nocobase/docs/en-US/api/database/repository.md

682 lines
14 KiB
Markdown
Raw Normal View History

# Repository
2023-01-21 08:55:09 +00:00
## Overview
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
On a given `Collection` object, you can get its `Repository` object to perform read and write operations on the data table.
2022-11-13 15:00:59 +00:00
```javascript
const { UserCollection } = require("./collections");
const UserRepository = UserCollection.repository;
const user = await UserRepository.findOne({
filter: {
id: 1
},
});
user.name = "new name";
await user.save();
```
2023-01-21 08:55:09 +00:00
### Query
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
#### Basic Query
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
On the `Repository` object, call the `find*` methods to perform query. The `filter` parameter is supported by all query methods to filter the data.
2022-11-13 15:00:59 +00:00
```javascript
// SELECT * FROM users WHERE id = 1
userRepository.find({
filter: {
id: 1
}
});
```
2023-01-21 08:55:09 +00:00
#### Operator
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
The `filter` parameter in the `Repository` also provides a variety of operators to perform more diverse queries.
2022-11-13 15:00:59 +00:00
```javascript
// SELECT * FROM users WHERE age > 18
userRepository.find({
filter: {
age: {
$gt: 18
}
}
});
// SELECT * FROM users WHERE age > 18 OR name LIKE '%张%'
userRepository.find({
filter: {
$or: [
{ age: { $gt: 18 } },
{ name: { $like: "%张%" } }
]
}
});
```
2023-01-21 08:55:09 +00:00
Refer to [Filter Operators](/api/database/operators) for more details on operators.
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
#### Field Control
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Control the output fields by the `fields`, `except`, and `appends` parameters when performing query.
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
* `fields`: Specify output fields
* `except`: Exclude output fields
* `appends`: Append output associated fields
2022-11-13 15:00:59 +00:00
```javascript
2023-01-21 08:55:09 +00:00
// The result contains only the <i>id</i> and <i>name</i> fields
2022-11-13 15:00:59 +00:00
userRepository.find({
fields: ["id", "name"],
});
2023-01-21 08:55:09 +00:00
// The result does not contain only the <i>password</i> field
2022-11-13 15:00:59 +00:00
userRepository.find({
except: ["password"],
});
2023-01-21 08:55:09 +00:00
// The result contains data associated with the <i>posts</i> object
2022-11-13 15:00:59 +00:00
userRepository.find({
appends: ["posts"],
});
```
2023-01-21 08:55:09 +00:00
#### Associated Field Query
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
The `filter` parameter supports filtering by associated fields, for example:
2022-11-13 15:00:59 +00:00
```javascript
2023-01-21 08:55:09 +00:00
// Find the <i>user</i> objects whose associated posts have title of "post title"
2022-11-13 15:00:59 +00:00
userRepository.find({
filter: {
"posts.title": "post title"
}
});
```
2023-01-21 08:55:09 +00:00
Associated fields can also be nested:
2022-11-13 15:00:59 +00:00
```javascript
2023-01-21 08:55:09 +00:00
// Find the <i>user</i> objects whose associated posts have comments containing "keywords"
2022-11-13 15:00:59 +00:00
await userRepository.find({
filter: {
"posts.comments.content": {
$like: "%keywords%"
}
}
});
```
2023-01-21 08:55:09 +00:00
#### Sort
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Sort query results by the `sort` parameter.
2022-11-13 15:00:59 +00:00
```javascript
// SELECT * FROM users ORDER BY age
await userRepository.find({
sort: 'age'
});
// SELECT * FROM users ORDER BY age DESC
await userRepository.find({
sort: '-age'
});
// SELECT * FROM users ORDER BY age DESC, name ASC
await userRepository.find({
sort: ['-age', "name"],
});
```
2023-01-21 08:55:09 +00:00
Sort by the field of the associated object is also supported:
2022-11-13 15:00:59 +00:00
```javascript
await userRepository.find({
sort: 'profile.createdAt'
});
```
2023-01-21 08:55:09 +00:00
### Create
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
#### Basic Create
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Create new data objects via `Repository`.
2022-11-13 15:00:59 +00:00
```javascript
await userRepository.create({
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
age: 18,
});
2023-01-21 08:55:09 +00:00
// INSERT INTO users (name, age) VALUES ('Mark', 18)
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
// Bulk creation
2022-11-13 15:00:59 +00:00
await userRepository.create([
{
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
age: 18,
},
{
2023-01-21 08:55:09 +00:00
name: "Alex",
2022-11-13 15:00:59 +00:00
age: 20,
},
])
```
2023-01-21 08:55:09 +00:00
#### Create Association
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Create associated objects at the same time of creating data. Similar to query, nested use of associated objects is also supported. For example:
2022-11-13 15:00:59 +00:00
```javascript
await userRepository.create({
name: "张三",
age: 18,
posts: [
{
title: "post title",
content: "post content",
tags: [
{
name: "tag1",
},
{
name: "tag2",
},
],
},
],
});
2023-01-21 08:55:09 +00:00
// When crearing a user, creat a post to associate with the user, and create tags to associate with the post
2022-11-13 15:00:59 +00:00
```
2023-01-21 08:55:09 +00:00
If the associated object is already in the database, you can pass its ID to create an association with it.
2022-11-13 15:00:59 +00:00
```javascript
const tag1 = await tagRepository.findOne({
filter: {
name: "tag1"
},
});
await userRepository.create({
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
age: 18,
posts: [
{
title: "post title",
content: "post content",
tags: [
{
2023-01-21 08:55:09 +00:00
id: tag1.id, // Create an association with an existing associated object
2022-11-13 15:00:59 +00:00
},
{
name: "tag2",
},
],
},
],
});
```
2023-01-21 08:55:09 +00:00
### Update
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
#### Basic Update
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
After getting the data object, you can modify the properties directly on the data object (`Model`), and then call the `save` method to save the changes.
2022-11-13 15:00:59 +00:00
```javascript
const user = await userRepository.findOne({
filter: {
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
},
});
user.age = 20;
await user.save();
```
2023-01-21 08:55:09 +00:00
The data object `Model` is inherited from Sequelize Model, refer to [Sequelize Model](https://sequelize.org/master/manual/model-basics.html) for the operations on `Model`.
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Or update data via `Repository`:
2022-11-13 15:00:59 +00:00
```javascript
2023-01-21 08:55:09 +00:00
// Update the records that meet the filtering condition
2022-11-13 15:00:59 +00:00
await userRepository.update({
filter: {
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
},
values: {
age: 20,
},
});
```
2023-01-21 08:55:09 +00:00
Control which fields to update by the `whitelist` and `blacklist` parameters, for example:
2022-11-13 15:00:59 +00:00
```javascript
await userRepository.update({
filter: {
2023-01-21 08:55:09 +00:00
name: "Mark",
2022-11-13 15:00:59 +00:00
},
values: {
age: 20,
2023-01-21 08:55:09 +00:00
name: "Alex",
2022-11-13 15:00:59 +00:00
},
2023-01-21 08:55:09 +00:00
whitelist: ["age"], // Only update the <i>age</i> field
2022-11-13 15:00:59 +00:00
});
````
2023-01-21 08:55:09 +00:00
#### Update Associated Field
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Associated objects can be set while updating, for example:
2022-11-13 15:00:59 +00:00
```javascript
const tag1 = tagRepository.findOne({
filter: {
id: 1
},
});
await postRepository.update({
filter: {
id: 1
},
values: {
title: "new post title",
tags: [
{
2023-01-21 08:55:09 +00:00
id: tag1.id // Associate with tag1
2022-11-13 15:00:59 +00:00
},
{
2023-01-21 08:55:09 +00:00
name: "tag2", // Create new tag and associate with it
2022-11-13 15:00:59 +00:00
},
],
},
});
await postRepository.update({
filter: {
id: 1
},
values: {
2023-01-21 08:55:09 +00:00
tags: null // Disassociate post from tags
2022-11-13 15:00:59 +00:00
},
})
```
2023-01-21 08:55:09 +00:00
### Delete
2022-11-13 15:00:59 +00:00
2023-01-21 08:55:09 +00:00
Call the `destroy()` method in `Repository` to perform the deletion operation. Filtering condition has to be specified to delete.
2022-11-13 15:00:59 +00:00
```javascript
await userRepository.destroy({
filter: {
status: "blocked",
},
});
```
2023-01-21 08:55:09 +00:00
## Constructor
2023-01-21 08:55:09 +00:00
It is usually not called directly by the developer, the instantiation is done mainly by specifying a coressponding repository type that is already registered in the parameter of `db.colletion()`. Repository type is registered through `db.registerRepositories()`.
2023-01-21 08:55:09 +00:00
**Signature**
* `constructor(collection: Collection)`
2023-01-21 08:55:09 +00:00
**Example**
```ts
import { Repository } from '@nocobase/database';
class MyRepository extends Repository {
async myQuery(sql) {
return this.database.sequelize.query(sql);
}
}
db.registerRepositories({
books: MyRepository
});
db.collection({
name: 'books',
// here link to the registered repository
repository: 'books'
});
await db.sync();
const books = db.getRepository('books') as MyRepository;
await books.myQuery('SELECT * FROM books;');
```
2023-01-21 08:55:09 +00:00
## Instance Member
### `database`
2023-01-26 13:14:23 +00:00
The database management instance where the context is located.
### `collection`
2023-01-26 13:14:23 +00:00
The corresponding data table management instance.
### `model`
2023-01-26 13:14:23 +00:00
The corresponding data model class.
2023-01-26 13:14:23 +00:00
## Instance Method
### `find()`
2023-01-26 13:14:23 +00:00
Find datasets from the database with the specified filtering conditions and sorting, etc.
2023-01-21 08:55:09 +00:00
**Signature**
* `async find(options?: FindOptions): Promise<Model[]>`
2023-01-26 13:14:23 +00:00
**Type**
```typescript
type Filter = FilterWithOperator | FilterWithValue | FilterAnd | FilterOr;
type Appends = string[];
type Except = string[];
type Fields = string[];
type Sort = string[] | string;
interface SequelizeFindOptions {
limit?: number;
offset?: number;
}
interface FilterByTk {
filterByTk?: TargetKey;
}
interface CommonFindOptions extends Transactionable {
filter?: Filter;
fields?: Fields;
appends?: Appends;
except?: Except;
sort?: Sort;
}
type FindOptions = SequelizeFindOptions & CommonFindOptions & FilterByTk;
```
2023-01-26 13:14:23 +00:00
**Detailed Information**
#### `filter: Filter`
2023-01-26 13:14:23 +00:00
Query conditions for filtering data results. In the query parameters that passed in, `key` is the name of the field, `value` is the corresponding value. Operators can be used in conjunction with other filtering conditions.
```typescript
2023-01-26 13:14:23 +00:00
// Find records with name "foo" and age above 18
repository.find({
filter: {
name: "foo",
age: {
$gt: 18,
},
}
})
```
2023-01-26 13:14:23 +00:00
Refer to [Operators](./operators.md) for more information.
#### `filterByTk: TargetKey`
2023-01-26 13:14:23 +00:00
Query data by `TargetKey`, this is shortcut for the `filter` parameter. The field of `TargetKey` can be [configured](./collection.md#filtertargetkey) in `Collection`, the default is `primaryKey`.
2023-01-26 13:14:23 +00:00
```typescript
// By default, find records with id 1
repository.find({
filterByTk: 1,
});
```
#### `fields: string[]`
2023-01-26 13:14:23 +00:00
Query columns. It is used to control which data fields to output. With this parameter, only the specified fields will be returned.
#### `except: string[]`
2023-01-26 13:14:23 +00:00
Exclude columns. It is used to control which data fields to output. With this parameter, the specified fields will not be returned.
#### `appends: string[]`
2023-01-26 13:14:23 +00:00
Append columns. It is used to load associated data. With this parameter, the specified associated fields will be returned together.
#### `sort: string[] | string`
2023-01-26 13:14:23 +00:00
Specify the sorting method of the query results. The input parameter is the name of the field, by default is to sort in the ascending order (`asc`); a `-` symbol needs to be added before the field name to sort in the descending order (`desc`). For example, `['-id', 'name']` means to sort by `id desc, name asc`.
#### `limit: number`
2023-01-26 13:14:23 +00:00
Limit the number of results, same as `limit` in `SQL`.
#### `offset: number`
2023-01-26 13:14:23 +00:00
The offset of the query, same as `offset` in `SQL`.
2023-01-21 08:55:09 +00:00
**Example**
```ts
const posts = db.getRepository('posts');
const results = await posts.find({
filter: {
createdAt: {
$gt: '2022-01-01T00:00:00.000Z',
}
},
fields: ['title'],
appends: ['user'],
});
```
### `findOne()`
2023-01-26 13:14:23 +00:00
Find a single piece of data from the database for specific conditions. Equivalent to `Model.findOne()` in Sequelize.
2023-01-21 08:55:09 +00:00
**Signature**
* `async findOne(options?: FindOneOptions): Promise<Model | null>`
<embed src="./shared/find-one.md"></embed>
2023-01-21 08:55:09 +00:00
**Example**
```ts
const posts = db.getRepository('posts');
const result = await posts.findOne({
filterByTk: 1,
});
```
### `count()`
2023-01-26 13:14:23 +00:00
Query a certain amount of data from the database for specific conditions. Equivalent to `Model.count()` in Sequelize.
2023-01-21 08:55:09 +00:00
**Signature**
* `count(options?: CountOptions): Promise<number>`
2023-01-26 13:14:23 +00:00
**Type**
```typescript
interface CountOptions extends Omit<SequelizeCountOptions, 'distinct' | 'where' | 'include'>, Transactionable {
filter?: Filter;
}
```
2023-01-21 08:55:09 +00:00
**Example**
```ts
const books = db.getRepository('books');
const count = await books.count({
filter: {
2023-01-26 13:14:23 +00:00
title: 'Three character classic'
}
});
```
### `findAndCount()`
2023-01-26 13:14:23 +00:00
Find datasets from the database with the specified filtering conditions and return the number of results. Equivalent to `Model.findAndCountAll()` in Sequelize.
2023-01-21 08:55:09 +00:00
**Signature**
* `async findAndCount(options?: FindAndCountOptions): Promise<[Model[], number]>`
2023-01-26 13:14:23 +00:00
**Type**
```typescript
type FindAndCountOptions = Omit<SequelizeAndCountOptions, 'where' | 'include' | 'order'> & CommonFindOptions;
```
2023-01-26 13:14:23 +00:00
**Detailed Information**
2023-01-26 13:14:23 +00:00
The query parameters are the same as `find()`. An array is returned with the first element of the query results, and the second element of the total number of results.
### `create()`
2023-01-26 13:14:23 +00:00
Inserts a newly created data into the data table. Equivalent to `Model.create()` in Sequelize. When the data object to be created carries any assiciated field, the corresponding assiciated data record is created or updated along with it.
2023-01-21 08:55:09 +00:00
**Signature**
* `async create<M extends Model>(options: CreateOptions): Promise<M>`
<embed src="./shared/create-options.md"></embed>
2023-01-21 08:55:09 +00:00
**Example**
```ts
const posts = db.getRepository('posts');
const result = await posts.create({
values: {
2023-01-26 13:14:23 +00:00
title: 'NocoBase 1.0 Release Notes',
tags: [
2023-01-26 13:14:23 +00:00
// Update data when there is a primary key and value of the associated table
{ id: 1 },
2023-01-26 13:14:23 +00:00
// Create data when there is no primary key and value
{ name: 'NocoBase' },
]
},
});
```
### `createMany()`
2023-01-26 13:14:23 +00:00
Inserts multiple newly created data into the data table. This is equivalent to calling the `create()` method multiple times.
2023-01-21 08:55:09 +00:00
**Signature**
* `createMany(options: CreateManyOptions): Promise<Model[]>`
2023-01-26 13:14:23 +00:00
**Type**
```typescript
interface CreateManyOptions extends BulkCreateOptions {
records: Values[];
}
```
2023-01-26 13:14:23 +00:00
**Detailed Information**
2023-01-26 13:14:23 +00:00
* `records`: An array of data objects to be created.
* `transaction`: Transaction object. If no transaction parameter is passed, the method will automatically create an internal transaction.
2023-01-21 08:55:09 +00:00
**Example**
```ts
const posts = db.getRepository('posts');
const results = await posts.createMany({
records: [
{
2023-01-26 13:14:23 +00:00
title: 'NocoBase 1.0 Release Notes',
tags: [
2023-01-26 13:14:23 +00:00
// Update data when there is a primary key and value of the associated table
{ id: 1 },
2023-01-26 13:14:23 +00:00
// Create data when there is no primary key and value
{ name: 'NocoBase' },
]
},
{
2023-01-26 13:14:23 +00:00
title: 'NocoBase 1.1 Release Notes',
tags: [
{ id: 1 }
]
},
],
});
```
### `update()`
2023-01-26 13:14:23 +00:00
Update data in the data table. Equivalent to `Model.update()` in Sequelize. When the data object to be updated carries any associated field, the corresponding associated data record is created or updated along with it.
2023-01-21 08:55:09 +00:00
**Signature**
* `async update<M extends Model>(options: UpdateOptions): Promise<M>`
<embed src="./shared/update-options.md"></embed>
2023-01-21 08:55:09 +00:00
**Example**
```ts
const posts = db.getRepository('posts');
const result = await posts.update({
filterByTk: 1,
values: {
2023-01-26 13:14:23 +00:00
title: 'NocoBase 1.0 Release Notes',
tags: [
2023-01-26 13:14:23 +00:00
// Update data when there is a primary key and value of the associated table
{ id: 1 },
2023-01-26 13:14:23 +00:00
// Create data when there is no primary key and value
{ name: 'NocoBase' },
]
},
});
```
### `destory()`
2023-01-26 13:14:23 +00:00
Delete data from the data table. Equivalent to `Model.destroy()` in Sequelize.
2023-01-21 08:55:09 +00:00
**Signature**
* `async destory(options?: TargetKey | TargetKey[] | DestoryOptions): Promise<number>`
2023-01-26 13:14:23 +00:00
**Type**
```typescript
interface DestroyOptions extends SequelizeDestroyOptions {
filter?: Filter;
filterByTk?: TargetKey | TargetKey[];
truncate?: boolean;
context?: any;
}
```
2023-01-26 13:14:23 +00:00
**Detailed Information**
2023-01-26 13:14:23 +00:00
* `filter`Specify the filtering conditions of the records to be deleted. Refer to the [`find()`](#find) method for the detailed usage of the filter.
* `filterByTk`Specify the filtering conditions by TargetKey.
* `truncate`: Whether to empty the table data, this parameter is valid if no `filter` or `filterByTk` parameter is passed.
* `transaction`: Transaction object. If no transaction parameter is passed, the method will automatically create an internal transaction.