mikro-orm
Advanced tools
Comparing version 0.7.0 to 0.7.1
{ | ||
"name": "mikro-orm", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"description": "Simple typescript mongo ORM for node.js based on data-mapper, unit-of-work and identity-map patterns", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
298
README.md
@@ -13,12 +13,42 @@ # mikro-orm | ||
## Installation & Usage | ||
## Defining entity | ||
Fist install the module via `yarn` or `npm`: | ||
`$ yarn add mikro-orm` | ||
or | ||
`$ npm i -s mikro-orm` | ||
Then call `MikroORM.init` as part of bootstrapping your app: | ||
```typescript | ||
import { BaseEntity, Entity, ManyToOne, Property } from 'mikro-orm'; | ||
import { Publisher } from './Publisher'; | ||
import { Author } from './Author'; | ||
import { BookRepository } from './BookRepository'; | ||
const orm = await MikroORM.init({ | ||
entitiesDirs: ['entities'], // relative to `baseDir` | ||
dbName: 'my-db-name', | ||
clientUrl: '...', // defaults to 'mongodb://localhost:27017' | ||
baseDir: __dirname, // defaults to `process.cwd()` | ||
}); | ||
console.log(orm.em); // EntityManager | ||
``` | ||
@Entity({ collection: 'books-table', customRepository: BookRepository }) | ||
And do not forget to clear entity manager before each request if you do not want | ||
to store all loaded entities in memory: | ||
```typescript | ||
const app = express(); | ||
app.use((req, res, next) => { | ||
orm.em.clear(); | ||
next(); | ||
}); | ||
``` | ||
Now you can define your entities (in one of the `entitiesDirs` folders): | ||
### Defining entity | ||
```typescript | ||
@Entity({ collection: 'books-table' }) | ||
export class Book extends BaseEntity { | ||
@@ -35,11 +65,2 @@ | ||
@Property() | ||
metaObject: object; | ||
@Property() | ||
metaArray: any[]; | ||
@Property() | ||
metaArrayOfStrings: string[]; | ||
constructor(title: string, author: Author) { | ||
@@ -49,5 +70,2 @@ super(); | ||
this.author = author; | ||
this.metaObject = {}; | ||
this.metaArray = [{test: 123, lol: true}]; | ||
this.metaArrayOfStrings = ['test']; | ||
} | ||
@@ -58,25 +76,5 @@ | ||
## Installation | ||
With your entities set up, you can start using entity manager and repositories as described | ||
in following section. For more examples, take a look at `tests/EntityManager.test.ts`. | ||
`$ yarn add mikro-orm` | ||
or | ||
`$ npm install mikro-orm` | ||
## Usage | ||
For more examples, take a look at `tests/EntityManager.test.ts`. | ||
```typescript | ||
import { MikroORM, Collection } from 'mikro-orm'; | ||
const orm = await MikroORM.init({ | ||
entitiesDirs: ['entities'], // relative to `baseDir` | ||
dbName: 'my-db-name', | ||
baseDir: __dirname, | ||
}); | ||
console.log(orm.em); // EntityManager | ||
``` | ||
## Persisting and cascading | ||
@@ -114,5 +112,25 @@ | ||
To fetch entities from database you can use `find()` and `findOne()` of `EntityManager`. | ||
To fetch entities from database you can use `find()` and `findOne()` of `EntityManager`: | ||
API: | ||
```typescript | ||
EntityManager.getCollection(entityName: string): Collection; // returns mongodb Collection for given entity | ||
EntityManager.getRepository<T extends BaseEntity>(entityName: string): EntityRepository<T>; | ||
EntityManager.find<T extends BaseEntity>(entityName: string, where?: FilterQuery<T>, populate?: string[], orderBy?: { [k: string]: 1 | -1; }, limit?: number, offset?: number): Promise<T[]>; | ||
EntityManager.findOne<T extends BaseEntity>(entityName: string, where: FilterQuery<T> | string, populate?: string[]): Promise<T>; | ||
EntityManager.merge<T extends BaseEntity>(entityName: string, data: any): T; | ||
EntityManager.getReference<T extends BaseEntity>(entityName: string, id: string): T; | ||
EntityManager.remove(entityName: string, where: BaseEntity | any): Promise<number>; | ||
EntityManager.removeEntity(entity: BaseEntity): Promise<number>; | ||
EntityManager.count(entityName: string, where: any): Promise<number>; | ||
EntityManager.persist(entity: BaseEntity | BaseEntity[], flush?: boolean): Promise<void>; | ||
EntityManager.flush(): Promise<void>; | ||
EntityManager.clear(): void; | ||
EntityManager.canPopulate(entityName: string, property: string): boolean; | ||
``` | ||
Example: | ||
```typescript | ||
const author = orm.em.findOne(Author.name, '...id...'); | ||
@@ -140,5 +158,21 @@ const books = orm.em.find(Book.name, {}); | ||
More convenient way of fetching entities from database is by using `EntityRepository`: | ||
More convenient way of fetching entities from database is by using `EntityRepository`, that | ||
carries the entity name so you do not have to pass it to every `find` and `findOne` calls: | ||
API: | ||
```typescript | ||
EntityRepository.persist(entity: BaseEntity, flush?: boolean): Promise<void>; | ||
EntityRepository.findOne(where: FilterQuery<BaseEntity> | string, populate?: string[]): Promise<BaseEntity>; | ||
EntityRepository.find(where: FilterQuery<BaseEntity>, populate?: string[], orderBy?: { [k: string]: 1 | -1; }, limit?: number, offset?: number): Promise<BaseEntity[]>; | ||
EntityRepository.findAll(populate?: string[], orderBy?: { [k: string]: 1 | -1; }, limit?: number, offset?: number): Promise<BaseEntity[]>; | ||
EntityRepository.remove(where: BaseEntity | any): Promise<number>; | ||
EntityRepository.flush(): Promise<void>; | ||
EntityRepository.canPopulate(property: string): boolean; | ||
EntityRepository.count(where?: any): Promise<number>; | ||
``` | ||
Example: | ||
```typescript | ||
const booksRepository = orm.em.getRepository<Book>(Book.name); | ||
@@ -153,6 +187,30 @@ | ||
TODO | ||
To use custom repository, just extend `EntityRepository<T>` class: | ||
## Identity Map | ||
```typescript | ||
export class CustomAuthorRepository extends EntityRepository<Author> { | ||
// your custom methods... | ||
public findAndUpdate(...) { | ||
// ... | ||
} | ||
} | ||
``` | ||
And register your repository as `@Entity` decorator: | ||
```typescript | ||
@Entity({ customRepository: CustomAuthorRepository }) | ||
export class Publisher extends BaseEntity { | ||
// ... | ||
} | ||
``` | ||
Then your custom repository can be accessed via `EntityManager.getRepository()` method. | ||
## Core features | ||
### Identity Map | ||
`MikroORM` uses identity map in background so you will always get the same instance of | ||
@@ -170,4 +228,10 @@ one entity. | ||
## Using references | ||
If you want to clear this identity map cache, you can do so via `EntityManager.clear()` method: | ||
```typescript | ||
orm.em.clear(); | ||
``` | ||
### Entity references | ||
Every single entity relation is mapped to an entity reference. Reference is an entity that has | ||
@@ -191,4 +255,58 @@ only its identifier. This reference is stored in identity map so you will get the same object | ||
## Collections | ||
### Using entity constructors | ||
Internally, `MikroORM` never calls entity constructor, so you are free to use it as you wish. | ||
The constructor will be called only when you instantiate the class yourself via `new` operator, | ||
so it is a handy place to require your data when creating new entity. | ||
For example following `Book` entity definition will always require to set `title` and `author`, | ||
but `publisher` will be optional: | ||
```typescript | ||
@Entity() | ||
export class Book extends BaseEntity { | ||
@Property() | ||
title: string; | ||
@ManyToOne({ entity: () => Author.name }) | ||
author: Author; | ||
@ManyToOne({ entity: () => Publisher.name }) | ||
publisher: Publisher; | ||
@ManyToMany({ entity: () => BookTag.name, inversedBy: 'books' }) | ||
tags: Collection<BookTag>; | ||
constructor(title: string, author: Author) { | ||
super(); | ||
this.title = title; | ||
this.author = author; | ||
} | ||
} | ||
``` | ||
### `ObjectID` and `string` duality | ||
Every entity has both `ObjectID` and `string` id available, also all methods of `EntityManager` | ||
and `EntityRepository` supports querying by both of them. | ||
```typescript | ||
const author = await orm.em.getReference('...id...'); | ||
console.log(author.id); // returns '...id...' | ||
console.log(author._id); // returns ObjectID('...id...') | ||
// all of those will return the same results | ||
const article = '...article id...'; // string id | ||
const book = '...book id...'; // string id | ||
const repo = orm.em.getRepository<Author>(Author.name); | ||
const foo1 = await repo.find({ id: { $in: [article] }, favouriteBook: book }); | ||
const bar1 = await repo.find({ id: { $in: [new ObjectID(article)] }, favouriteBook: new ObjectID(book) }); | ||
const foo2 = await repo.find({ _id: { $in: [article] }, favouriteBook: book }); | ||
const bar2 = await repo.find({ _id: { $in: [new ObjectID(article)] }, favouriteBook: new ObjectID(book) }); | ||
``` | ||
### Collections | ||
`OneToMany` and `ManyToMany` collections are stored in a `Collection` wrapper. It implements | ||
@@ -198,8 +316,7 @@ iterator so you can use `for of` loop to iterate through it. | ||
```typescript | ||
const author = orm.em.findOne(Author.name, '...'); | ||
const author = orm.em.findOne(Author.name, '...', ['books']); // populating books collection | ||
console.log(author.name); // Jon Snow | ||
// or we could lazy load books collection later via `init()` method | ||
await author.books.init(); | ||
await author.books.init(); // init all books | ||
for (const book of author.books) { | ||
@@ -229,10 +346,82 @@ console.log(book.title); // initialized | ||
console.log(author.books.getIdentifiers('id')); // array of string | ||
``` | ||
### `OneToMany` collections | ||
`OneToMany` collections are inverse side of `ManyToOne` references, to which they need to point via `fk` attribute: | ||
```typescript | ||
@Entity() | ||
export class Book extends BaseEntity { | ||
@ManyToOne({ entity: () => Author.name }) | ||
author: Author; | ||
} | ||
@Entity() | ||
export class BookTag extends BaseEntity { | ||
@OneToMany({ entity: () => Book.name, fk: 'author' }) | ||
books: Collection<Book>; | ||
} | ||
``` | ||
## Using entity constructors | ||
### `ManyToMany` collections | ||
Internally, `MikroORM` never calls entity constructor, so you are free to use it as you wish. | ||
The constructor will be called only when you instantiate the class yourself via `new` operator. | ||
As opposed to SQL databases, with MongoDB we do not need to have join tables for `ManyToMany` relations. | ||
All references are stored as an array of `ObjectID`s on owning entity. | ||
#### Unidirectional | ||
Unidirectional `ManyToMany` relations are defined only on one side, and marked explicitly as `owner`: | ||
```typescript | ||
@ManyToMany({ entity: () => Book.name, owner: true }) | ||
books: Collection<Book>; | ||
``` | ||
#### Bidirectional | ||
Bidirectional `ManyToMany` relations are defined on both sides, while one is owning side (where references are store), | ||
marked by `inversedBy` attribute pointing to the inverse side: | ||
```typescript | ||
@ManyToMany({ entity: () => BookTag.name, inversedBy: 'books' }) | ||
tags: Collection<BookTag>; | ||
``` | ||
And on the inversed side we define it with `mappedBy` attribute poining back to the owner: | ||
```typescript | ||
@ManyToMany({ entity: () => Book.name, mappedBy: 'tags' }) | ||
books: Collection<Book>; | ||
``` | ||
### Updating entity values with `BaseEntity.assign()` | ||
When you want to update entity based on user input, you will usually have just plain | ||
string ids of entity relations as user input. Normally you would need to use | ||
`EntityManager.getReference()` to create references from each id first, and then | ||
use those references to update entity relations: | ||
```typescript | ||
const jon = new Author('Jon Snow', 'snow@wall.st'); | ||
const book = new Book('Book', jon); | ||
book.author = orm.em.getReference<Author>(Author.name, '...id...'); | ||
``` | ||
Same result can be easily achiever with `BaseEntity.assign()`: | ||
```typescript | ||
book.assign({ | ||
title: 'Better Book 1', | ||
author: '...id...', | ||
}); | ||
console.log(book.title); // 'Better Book 1' | ||
console.log(book.author); // instnace of Author with id: '...id...' | ||
console.log(book.author.id); // '...id...' | ||
``` | ||
## TODO | ||
@@ -248,5 +437,2 @@ | ||
- 1:M / M:1 collections | ||
- many to many collections | ||
- custom repository | ||
- lifecycle hooks |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
144894
427