mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
added basic mongodb support, mongodb repository, entity manager and all major collection operations
This commit is contained in:
parent
64d38e3563
commit
5fcc3aed23
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,4 +3,4 @@ coverage/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
ormconfig.json
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
37
CHANGELOG.md
37
CHANGELOG.md
@ -1,3 +1,40 @@
|
||||
# 0.1.0 (future)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* changed `find*` repository methods. Now conditions are `Partial<Entity>` which makes them type-safe.
|
||||
However now `FindOptions` cannot be used with `findOne`, `findAndCount`, `find` and other methods.
|
||||
Use `fineOneByOptions`, `findAndCountByOptions`, `findByOptions` methods instead
|
||||
* removed `FindOptions` interface and introduced two new interfaces: `FindOneOptions` and `FindManyOptions` -
|
||||
each for its own `findOne*` or `find*` methods
|
||||
* dropped out some of options of `FindOptions`. Use `QueryBuilder` instead.
|
||||
* deprecated method `addParameters` has been removed from `QueryBuilder`. Use `setParameters` instead.
|
||||
* table decorators were not removed in the release, however they will be removed in next. Be sure to replace them before that.
|
||||
* `QueryBuilder#setFirstResult` has been renamed to `QueryBuilder#from`
|
||||
* `QueryBuilder#setMaxResults` has been renamed to `QueryBuilder#take`
|
||||
|
||||
### NEW FEATURES
|
||||
|
||||
* added `mongodb` support
|
||||
* entity now can be saved partially within `persist` method
|
||||
|
||||
### TODOs
|
||||
|
||||
* mongodb index sync
|
||||
* make sure array column works in both relational and mongodb
|
||||
* implement true relations and joins in mongo
|
||||
* reorganize docs to cover both relational and mongo databases
|
||||
* try to add more document storage databases
|
||||
* make all naming things logically correct
|
||||
* create mongo version of entity manager
|
||||
* note that mongo indices has its own special properties
|
||||
* what to do with string version of object id? do we really need that?
|
||||
* need test organization and exclude mongodb where its usage is not possible
|
||||
* make sure lazy loading is working with mongo once mongo relations are setup
|
||||
* check if subscribers aren't broke
|
||||
* make sure create date / update date are working properly with mongo
|
||||
* make sure embedded works properly and nested embeddeds are supported
|
||||
|
||||
# 0.0.9 (next)
|
||||
|
||||
* fixed bug with indices from columns are not being inherited from parent entity [#242](https://github.com/typeorm/typeorm/issues/242)
|
||||
|
||||
@ -4,7 +4,7 @@ services:
|
||||
# mysql
|
||||
mysql:
|
||||
image: "mysql:5.7.10"
|
||||
container_name: "mysql"
|
||||
container_name: "typeorm-mysql"
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
@ -16,7 +16,7 @@ services:
|
||||
# mariadb
|
||||
mariadb:
|
||||
image: "mariadb:10.1.16"
|
||||
container_name: "mariadb"
|
||||
container_name: "typeorm-mariadb"
|
||||
ports:
|
||||
- "3307:3306"
|
||||
environment:
|
||||
@ -28,7 +28,7 @@ services:
|
||||
# postgres
|
||||
postgres:
|
||||
image: "postgres:9.6.1"
|
||||
container_name: "postgres"
|
||||
container_name: "typeorm-postgres"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
@ -39,11 +39,18 @@ services:
|
||||
# mssql
|
||||
# mssql:
|
||||
# image: "microsoft/mssql-server-linux"
|
||||
# container_name: "mssql"
|
||||
# container_name: "typeorm-mssql"
|
||||
# ports:
|
||||
# - "1433:1433"
|
||||
# environment:
|
||||
# ACCEPT_EULA: "Y"
|
||||
# SA_PASSWORD: "thisIs@V3ryh&rdP@55w0rd"
|
||||
# volumes:
|
||||
# - "./temp/mssql:/var/opt/mssql"
|
||||
# - "./temp/mssql:/var/opt/mssql"
|
||||
|
||||
# mongodb
|
||||
mongodb:
|
||||
image: "mongo:3.4.1"
|
||||
container_name: "typeorm-mongodb"
|
||||
ports:
|
||||
- "27017:27017"
|
||||
@ -58,6 +58,7 @@
|
||||
"gulp-uglify": "^2.0.0",
|
||||
"gulpclass": "0.1.1",
|
||||
"mocha": "^3.2.0",
|
||||
"mongodb": "^2.2.16",
|
||||
"mssql": "^3.3.0",
|
||||
"mysql": "^2.12.0",
|
||||
"mysql2": "^1.1.2",
|
||||
@ -72,6 +73,7 @@
|
||||
"typescript": "^2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/mongodb": "^2.1.40",
|
||||
"app-root-path": "^2.0.1",
|
||||
"glob": "^7.1.1",
|
||||
"reflect-metadata": "^0.1.9",
|
||||
|
||||
@ -60,7 +60,7 @@ createConnection(options).then(connection => {
|
||||
.then(updatedAuthor => {
|
||||
console.log("Author has been updated: ", updatedAuthor);
|
||||
console.log("Now lets load all posts with their authors:");
|
||||
return postRepository.find({ alias: "post", leftJoinAndSelect: { author: "post.author" } });
|
||||
return postRepository.find({ join: { alias: "post", leftJoinAndSelect: { author: "post.author" } } });
|
||||
})
|
||||
.then(posts => {
|
||||
console.log("Posts are loaded: ", posts);
|
||||
@ -92,7 +92,7 @@ createConnection(options).then(connection => {
|
||||
.then(posts => {
|
||||
console.log("Post has been saved with its categories. ");
|
||||
console.log("Lets find it now. ");
|
||||
return postRepository.find({ alias: "post", innerJoinAndSelect: { categories: "post.categories" } });
|
||||
return postRepository.find({ join: { alias: "post", innerJoinAndSelect: { categories: "post.categories" } } });
|
||||
})
|
||||
.then(posts => {
|
||||
console.log("Post with categories are loaded: ", posts);
|
||||
|
||||
@ -56,11 +56,13 @@ createConnection(options).then(connection => {
|
||||
|
||||
console.log("Now lets load posts with all their relations:");
|
||||
return postRepository.find({
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
author: "post.author",
|
||||
metadata: "post.metadata",
|
||||
categories: "post.categories"
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
author: "post.author",
|
||||
metadata: "post.metadata",
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -44,10 +44,15 @@ createConnection(options).then(connection => {
|
||||
.persist(post)
|
||||
.then(post => {
|
||||
console.log("Post has been saved. Lets load it now.");
|
||||
return postRepository.find({ alias: "post", leftJoinAndSelect: {
|
||||
categories: "post.categories",
|
||||
author: "post.user" // note that table column is used, not object property
|
||||
}});
|
||||
return postRepository.find({
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
categories: "post.categories",
|
||||
author: "post.user" // note that table column is used, not object property
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(loadedPosts => {
|
||||
console.log("loadedPosts: ", loadedPosts);
|
||||
|
||||
47
sample/sample34-mongodb/app.ts
Normal file
47
sample/sample34-mongodb/app.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import "reflect-metadata";
|
||||
import {createConnection, ConnectionOptions} from "../../src/index";
|
||||
import {Post} from "./entity/Post";
|
||||
|
||||
const options: ConnectionOptions = {
|
||||
driver: {
|
||||
type: "mongodb",
|
||||
host: "localhost",
|
||||
database: "test",
|
||||
},
|
||||
logging: {
|
||||
logQueries: true,
|
||||
logSchemaCreation: true
|
||||
},
|
||||
// autoSchemaSync: true,
|
||||
entities: [Post]
|
||||
};
|
||||
|
||||
createConnection(options).then(async connection => {
|
||||
|
||||
const post = new Post();
|
||||
post.text = "Hello how are you?";
|
||||
post.title = "hello";
|
||||
post.likesCount = 100;
|
||||
|
||||
await connection.getRepository(Post).persist(post);
|
||||
console.log("Post has been saved: ", post);
|
||||
|
||||
const loadedPost = await connection.getRepository(Post).findOne({
|
||||
text: "Hello how are you?",
|
||||
});
|
||||
console.log("Post has been loaded: ", loadedPost);
|
||||
|
||||
// take last 5 of saved posts
|
||||
const allPosts = await connection.getRepository(Post).find({ take: 5 });
|
||||
console.log("All posts: ", allPosts);
|
||||
|
||||
// perform mongodb-specific query using cursor which returns properly initialized entities
|
||||
const cursor1 = connection.getMongoRepository(Post).createEntityCursor({ title: "hello" });
|
||||
console.log("Post retrieved via cursor #1: ", await cursor1.next());
|
||||
console.log("Post retrieved via cursor #2: ", await cursor1.next());
|
||||
|
||||
// we can also perform mongodb-specific queries using mongodb-specific entity manager
|
||||
const cursor2 = connection.mongoEntityManager.createEntityCursor(Post, { title: "hello" });
|
||||
console.log("Only two posts retrieved via cursor: ", await cursor2.limit(2).toArray());
|
||||
|
||||
}, error => console.log("Error: ", error));
|
||||
22
sample/sample34-mongodb/entity/Post.ts
Normal file
22
sample/sample34-mongodb/entity/Post.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {Column, Entity} from "../../../src/index";
|
||||
import {ObjectIdColumn} from "../../../src/decorator/columns/ObjectIdColumn";
|
||||
import {ObjectID} from "mongodb";
|
||||
|
||||
@Entity("sample34_post")
|
||||
export class Post {
|
||||
|
||||
@ObjectIdColumn()
|
||||
id: ObjectID;
|
||||
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column()
|
||||
text: string;
|
||||
|
||||
@Column("int", {
|
||||
nullable: false
|
||||
})
|
||||
likesCount: number;
|
||||
|
||||
}
|
||||
@ -36,6 +36,9 @@ import {AbstractRepository} from "../repository/AbstractRepository";
|
||||
import {CustomRepositoryNotFoundError} from "../repository/error/CustomRepositoryNotFoundError";
|
||||
import {CustomRepositoryReusedError} from "../repository/error/CustomRepositoryReusedError";
|
||||
import {CustomRepositoryCannotInheritRepositoryError} from "../repository/error/CustomRepositoryCannotInheritRepositoryError";
|
||||
import {MongoRepository} from "../repository/MongoRepository";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {MongoEntityManager} from "../entity-manager/MongoEntityManager";
|
||||
|
||||
/**
|
||||
* Connection is a single database connection to a specific database of a database management system.
|
||||
@ -155,20 +158,31 @@ export class Connection {
|
||||
/**
|
||||
* Indicates if connection to the database already established for this connection.
|
||||
*/
|
||||
get isConnected() {
|
||||
get isConnected(): boolean {
|
||||
return this._isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets entity manager that allows to perform repository operations with any entity in this connection.
|
||||
*/
|
||||
get entityManager() {
|
||||
get entityManager(): EntityManager {
|
||||
// if (!this.isConnected)
|
||||
// throw new CannotGetEntityManagerNotConnectedError(this.name);
|
||||
|
||||
return this._entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mongodb entity manager that allows to perform mongodb-specific repository operations
|
||||
* with any entity in this connection.
|
||||
*/
|
||||
get mongoEntityManager(): MongoEntityManager {
|
||||
if (!(this._entityManager instanceof MongoEntityManager))
|
||||
throw new Error(`MongoEntityManager is only available for MongoDB databases.`);
|
||||
|
||||
return this._entityManager as MongoEntityManager;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
@ -456,33 +470,69 @@ export class Connection {
|
||||
* like ones decorated with @ClosureEntity decorator.
|
||||
*/
|
||||
getTreeRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): TreeRepository<Entity> {
|
||||
// todo: add checks if tree repository is supported by driver (not supported by mongodb at least)
|
||||
|
||||
const repository = this.findRepositoryAggregator(entityClassOrName).treeRepository;
|
||||
if (!repository)
|
||||
throw new RepositoryNotTreeError(entityClassOrName);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity class.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClass: ObjectType<Entity>): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityName: string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb-specific repository for the given entity class or name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity> {
|
||||
if (!(this.driver instanceof MongoDriver))
|
||||
throw new Error(`You can use getMongoRepository only for MongoDB connections.`);
|
||||
|
||||
return this.findRepositoryAggregator(entityClassOrName).repository as MongoRepository<Entity>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClass: ObjectType<Entity>): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityName: string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class or name.
|
||||
* SpecificRepository is a special repository that contains specific and non standard repository methods.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
getSpecificRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): SpecificRepository<Entity> {
|
||||
return this.findRepositoryAggregator(entityClassOrName).specificRepository;
|
||||
@ -690,6 +740,9 @@ export class Connection {
|
||||
* Creates a new default entity manager without single connection setup.
|
||||
*/
|
||||
protected createEntityManager() {
|
||||
if (this.driver instanceof MongoDriver)
|
||||
return new MongoEntityManager(this);
|
||||
|
||||
return new EntityManager(this);
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ import {OrmUtils} from "../util/OrmUtils";
|
||||
import {CannotDetermineConnectionOptionsError} from "./error/CannotDetermineConnectionOptionsError";
|
||||
import {PlatformTools} from "../platform/PlatformTools";
|
||||
import {WebsqlDriver} from "../driver/websql/WebsqlDriver";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
|
||||
/**
|
||||
* ConnectionManager is used to store and manage all these different connections.
|
||||
@ -443,6 +444,8 @@ export class ConnectionManager {
|
||||
return new SqlServerDriver(options, logger);
|
||||
case "websql":
|
||||
return new WebsqlDriver(options, logger);
|
||||
case "mongodb":
|
||||
return new MongoDriver(options, logger);
|
||||
default:
|
||||
throw new MissingDriverError(options.type);
|
||||
}
|
||||
|
||||
@ -77,6 +77,9 @@ export interface ConnectionOptions {
|
||||
* Be careful with this option and don't use this in production - otherwise you can loose production data.
|
||||
* This option is useful during debug and development.
|
||||
* Alternative to it, you can use CLI and run schema:sync command.
|
||||
*
|
||||
* Note that for MongoDB database it does not create schema, because MongoDB is schemaless.
|
||||
* Instead, it syncs just by creating indices.
|
||||
*/
|
||||
readonly autoSchemaSync?: boolean;
|
||||
|
||||
|
||||
42
src/decorator/columns/ArrayColumn.ts
Normal file
42
src/decorator/columns/ArrayColumn.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {ColumnTypeUndefinedError} from "../error/ColumnTypeUndefinedError";
|
||||
import {GeneratedOnlyForPrimaryError} from "../error/GeneratedOnlyForPrimaryError";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
import {ObjectType} from "../../common/ObjectType";
|
||||
|
||||
/**
|
||||
* Column decorator is used to mark a specific class property as a table column.
|
||||
* This decorator creates a special type of a column - array of values that are stored special way in a column.
|
||||
*/
|
||||
export function ArrayColumn<T>(type?: "string"|"number"|"boolean"|((type?: any) => ObjectType<T>), options?: ColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
|
||||
// todo: column type depend of driver. for relational databases it always will be string
|
||||
// for mongo it will be given "type"
|
||||
if (!options.type)
|
||||
options = Object.assign({ type: type } as ColumnOptions, options);
|
||||
|
||||
// todo: this is incomplete
|
||||
|
||||
// if we still don't have a type then we need to give error to user that type is required
|
||||
if (!options.type)
|
||||
throw new ColumnTypeUndefinedError(object, propertyName);
|
||||
|
||||
// check if auto increment is not set for simple column
|
||||
if (options.generated)
|
||||
throw new GeneratedOnlyForPrimaryError(object, propertyName);
|
||||
|
||||
// create and register a new column metadata
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: "regular", // todo: array
|
||||
options: options
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
25
src/decorator/columns/ObjectIdColumn.ts
Normal file
25
src/decorator/columns/ObjectIdColumn.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import {ColumnOptions} from "../options/ColumnOptions";
|
||||
import {getMetadataArgsStorage} from "../../index";
|
||||
import {ColumnMetadataArgs} from "../../metadata-args/ColumnMetadataArgs";
|
||||
|
||||
/**
|
||||
* Special type of column that is available only for MongoDB database.
|
||||
* Marks your entity's column to be an object id.
|
||||
*/
|
||||
export function ObjectIdColumn<T>(options?: ColumnOptions): Function {
|
||||
return function (object: Object, propertyName: string) {
|
||||
|
||||
// if column options are not given then create a new empty options
|
||||
if (!options) options = {} as ColumnOptions;
|
||||
options = Object.assign(options, { primary: true });
|
||||
|
||||
// create and register a new column metadata
|
||||
const args: ColumnMetadataArgs = {
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
mode: "objectId",
|
||||
options: options
|
||||
};
|
||||
getMetadataArgsStorage().columns.add(args);
|
||||
};
|
||||
}
|
||||
@ -12,8 +12,9 @@ export interface EntityOptions {
|
||||
|
||||
/**
|
||||
* Table's database engine type (like "InnoDB", "MyISAM", etc).
|
||||
* Note that it used only during table creation.
|
||||
* It is used only during table creation.
|
||||
* If you update this value and table is already created, it will not change table's engine type.
|
||||
* Note that not all databases support this option.
|
||||
*/
|
||||
readonly engine?: string;
|
||||
|
||||
|
||||
@ -19,6 +19,21 @@ export interface Driver {
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
/**
|
||||
* Creates repository instance of this driver.
|
||||
*/
|
||||
// createRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): Repository<any>;
|
||||
|
||||
/**
|
||||
* Creates tree repository instance of this driver.
|
||||
*/
|
||||
// createTreeRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): TreeRepository<any>;
|
||||
|
||||
/**
|
||||
* Creates specific repository instance of this driver.
|
||||
*/
|
||||
// createSpecificRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): SpecificRepository<any>;
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
* Based on pooling options, it can either create connection immediately,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Driver type.
|
||||
*/
|
||||
export type DriverType = "mysql"|"postgres"|"mariadb"|"sqlite"|"oracle"|"mssql"|"websql";
|
||||
export type DriverType = "mysql"|"postgres"|"mariadb"|"sqlite"|"oracle"|"mssql"|"websql"|"mongodb";
|
||||
|
||||
/**
|
||||
* Connectivity options used to connect to the database, and other database-driver-specific options.
|
||||
@ -44,8 +44,7 @@ export interface DriverOptions {
|
||||
readonly database?: string;
|
||||
|
||||
/**
|
||||
* Schema name. (Only used in Postgres)
|
||||
* default: "public"
|
||||
* Schema name. By default is "public" (used only in Postgres databases).
|
||||
*/
|
||||
readonly schemaName?: string;
|
||||
|
||||
@ -61,17 +60,22 @@ export interface DriverOptions {
|
||||
|
||||
/**
|
||||
* Indicates if connection pooling should be used or not.
|
||||
* Be default it is enabled if its supported by a platform. Set to false to disable it.
|
||||
* Be default it is enabled if its supported by a platform.
|
||||
* Set to false to disable it.
|
||||
*
|
||||
* @todo: rename to disablePool? What about mongodb pool?
|
||||
*/
|
||||
readonly usePool?: boolean;
|
||||
|
||||
/**
|
||||
* Extra connection options to be passed to the driver.
|
||||
* Extra connection options to be passed to the underlying driver.
|
||||
*/
|
||||
readonly extra?: any;
|
||||
|
||||
/**
|
||||
* Prefix to use on all tables of this connection in the database.
|
||||
* Prefix to use on all tables (collections) of this connection in the database.
|
||||
*
|
||||
* @todo: rename to entityPrefix
|
||||
*/
|
||||
readonly tablesPrefix?: string;
|
||||
|
||||
|
||||
27
src/driver/mongodb/EntityCursor.ts
Normal file
27
src/driver/mongodb/EntityCursor.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import {Cursor} from "mongodb";
|
||||
import {DocumentToEntityTransformer} from "../../query-builder/transformer/DocumentToEntityTransformer";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Special version of default mongodb Cursor, that takes care of transformation to entity
|
||||
* when its toArray method is called.
|
||||
*/
|
||||
export class EntityCursor<Entity> extends Cursor<Entity> {
|
||||
|
||||
/**
|
||||
* Metadata of the entity whose this cursor is.
|
||||
*/
|
||||
metadata: EntityMetadata;
|
||||
|
||||
async toArray(): Promise<Entity[]> {
|
||||
const results = await super.toArray();
|
||||
|
||||
// if for some reasons metadata is not set then return results as they are
|
||||
if (!this.metadata)
|
||||
return results;
|
||||
|
||||
const transformer = new DocumentToEntityTransformer();
|
||||
return transformer.transformAll(results, this.metadata);
|
||||
}
|
||||
|
||||
}
|
||||
264
src/driver/mongodb/MongoDriver.ts
Normal file
264
src/driver/mongodb/MongoDriver.ts
Normal file
@ -0,0 +1,264 @@
|
||||
import {Driver} from "../Driver";
|
||||
import {ConnectionIsNotSetError} from "../error/ConnectionIsNotSetError";
|
||||
import {DriverOptions} from "../DriverOptions";
|
||||
import {DatabaseConnection} from "../DatabaseConnection";
|
||||
import {DriverPackageNotInstalledError} from "../error/DriverPackageNotInstalledError";
|
||||
import {Logger} from "../../logger/Logger";
|
||||
import {QueryRunner} from "../../query-runner/QueryRunner";
|
||||
import {MongoQueryRunner} from "./MongoQueryRunner";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {DriverOptionNotSetError} from "../error/DriverOptionNotSetError";
|
||||
import {PlatformTools} from "../../platform/PlatformTools";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
|
||||
/**
|
||||
* Organizes communication with MongoDB.
|
||||
*/
|
||||
export class MongoDriver implements Driver {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Naming strategy used in the connection where this driver is used.
|
||||
*/
|
||||
namingStrategy: NamingStrategyInterface;
|
||||
|
||||
/**
|
||||
* Mongodb does not require to dynamically create query runner each time,
|
||||
* because it does not have a regular pool.
|
||||
*/
|
||||
queryRunner: MongoQueryRunner;
|
||||
|
||||
/**
|
||||
* Driver connection options.
|
||||
*/
|
||||
readonly options: DriverOptions;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Properties
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Underlying mongodb driver.
|
||||
*/
|
||||
protected mongodb: any;
|
||||
|
||||
/**
|
||||
* Connection to mongodb database provided by native driver.
|
||||
*/
|
||||
protected pool: any;
|
||||
|
||||
/**
|
||||
* Pool of database connections.
|
||||
*/
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(options: DriverOptions, logger: Logger, mongodb?: any) {
|
||||
|
||||
// validate options to make sure everything is correct and driver will be able to establish connection
|
||||
this.validateOptions(options);
|
||||
|
||||
// if mongodb package instance was not set explicitly then try to load it
|
||||
if (!mongodb)
|
||||
mongodb = this.loadDependencies();
|
||||
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
this.mongodb = mongodb;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Overridden Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Performs connection to the database.
|
||||
*/
|
||||
connect(): Promise<void> {
|
||||
return new Promise<void>((ok, fail) => {
|
||||
this.mongodb.MongoClient.connect(this.buildConnectionUrl(), this.options.extra, (err: any, database: any) => {
|
||||
if (err) return fail(err);
|
||||
|
||||
this.pool = database;
|
||||
const databaseConnection: DatabaseConnection = {
|
||||
id: 1,
|
||||
connection: this.pool,
|
||||
isTransactionActive: false
|
||||
};
|
||||
this.queryRunner = new MongoQueryRunner(databaseConnection, this, this.logger);
|
||||
ok();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection with the database.
|
||||
*/
|
||||
async disconnect(): Promise<void> {
|
||||
if (!this.pool)
|
||||
throw new ConnectionIsNotSetError("mongodb");
|
||||
|
||||
return new Promise<void>((ok, fail) => {
|
||||
const handler = (err: any) => err ? fail(err) : ok();
|
||||
|
||||
// if single connection is opened, then close it
|
||||
this.pool.end(handler);
|
||||
this.pool = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a query runner used for common queries.
|
||||
*/
|
||||
async createQueryRunner(): Promise<QueryRunner> {
|
||||
if (!this.pool)
|
||||
return Promise.reject(new ConnectionIsNotSetError("mongodb"));
|
||||
|
||||
return this.queryRunner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to the native implementation of the database.
|
||||
*/
|
||||
nativeInterface() {
|
||||
return {
|
||||
driver: this.mongodb,
|
||||
connection: this.pool
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces parameters in the given sql with special escaping character
|
||||
* and an array of parameter names to be passed to a query.
|
||||
*/
|
||||
escapeQueryWithParameters(sql: string, parameters: ObjectLiteral): [string, any[]] {
|
||||
throw new Error(`This operation is not supported by Mongodb driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a column name.
|
||||
*/
|
||||
escapeColumnName(columnName: string): string {
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes an alias.
|
||||
*/
|
||||
escapeAliasName(aliasName: string): string {
|
||||
return aliasName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a table name.
|
||||
*/
|
||||
escapeTableName(tableName: string): string {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares given value to a value to be persisted, based on its column type and metadata.
|
||||
*/
|
||||
preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any {
|
||||
if (value === null || value === undefined)
|
||||
return null;
|
||||
|
||||
switch (columnMetadata.type) {
|
||||
// case ColumnTypes.BOOLEAN:
|
||||
// return value === true ? 1 : 0;
|
||||
//
|
||||
// case ColumnTypes.DATE:
|
||||
// return DataTransformationUtils.mixedDateToDateString(value);
|
||||
//
|
||||
// case ColumnTypes.TIME:
|
||||
// return DataTransformationUtils.mixedDateToTimeString(value);
|
||||
//
|
||||
// case ColumnTypes.DATETIME:
|
||||
// if (columnMetadata.localTimezone) {
|
||||
// return DataTransformationUtils.mixedDateToDatetimeString(value);
|
||||
// } else {
|
||||
// return DataTransformationUtils.mixedDateToUtcDatetimeString(value);
|
||||
// }
|
||||
//
|
||||
// case ColumnTypes.JSON:
|
||||
// return JSON.stringify(value);
|
||||
//
|
||||
// case ColumnTypes.SIMPLE_ARRAY:
|
||||
// return DataTransformationUtils.simpleArrayToString(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares given value to a value to be persisted, based on its column type or metadata.
|
||||
*/
|
||||
prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any {
|
||||
switch (columnMetadata.type) {
|
||||
// case ColumnTypes.BOOLEAN:
|
||||
// return value ? true : false;
|
||||
//
|
||||
// case ColumnTypes.JSON:
|
||||
// return JSON.parse(value);
|
||||
//
|
||||
// case ColumnTypes.SIMPLE_ARRAY:
|
||||
// return DataTransformationUtils.stringToSimpleArray(value);
|
||||
}
|
||||
|
||||
// if (columnMetadata.isObjectId)
|
||||
// return new ObjectID(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate driver options to make sure everything is correct and driver will be able to establish connection.
|
||||
*/
|
||||
protected validateOptions(options: DriverOptions) {
|
||||
if (!options.url) {
|
||||
if (!options.host)
|
||||
throw new DriverOptionNotSetError("host");
|
||||
if (!options.database)
|
||||
throw new DriverOptionNotSetError("database");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If driver dependency is not given explicitly, then try to load it via "require".
|
||||
*/
|
||||
protected loadDependencies(): any {
|
||||
try {
|
||||
return PlatformTools.load("mongodb"); // try to load native driver dynamically
|
||||
|
||||
} catch (e) {
|
||||
throw new DriverPackageNotInstalledError("MongoDB", "mongodb");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds connection url that is passed to underlying driver to perform connection to the mongodb database.
|
||||
*/
|
||||
protected buildConnectionUrl(): string {
|
||||
if (this.options.url)
|
||||
return this.options.url;
|
||||
|
||||
return `mongodb://${this.options.host || "127.0.0.1"}:${this.options.port || "27017"}/${this.options.database}`;
|
||||
}
|
||||
|
||||
}
|
||||
684
src/driver/mongodb/MongoQueryRunner.ts
Normal file
684
src/driver/mongodb/MongoQueryRunner.ts
Normal file
@ -0,0 +1,684 @@
|
||||
import {QueryRunner} from "../../query-runner/QueryRunner";
|
||||
import {DatabaseConnection} from "../DatabaseConnection";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {Logger} from "../../logger/Logger";
|
||||
import {MongoDriver} from "./MongoDriver";
|
||||
import {ColumnSchema} from "../../schema-builder/schema/ColumnSchema";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {TableSchema} from "../../schema-builder/schema/TableSchema";
|
||||
import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
import {
|
||||
Cursor,
|
||||
Db,
|
||||
Collection,
|
||||
MongoCountPreferences,
|
||||
CollectionAggregationOptions,
|
||||
AggregationCursor,
|
||||
CollectionBluckWriteOptions,
|
||||
BulkWriteOpResultObject,
|
||||
IndexOptions,
|
||||
CollectionOptions,
|
||||
DeleteWriteOpResultObject,
|
||||
FindAndModifyWriteOpResultObject,
|
||||
FindOneAndReplaceOption,
|
||||
GeoHaystackSearchOptions,
|
||||
GeoNearOptions,
|
||||
ReadPreference,
|
||||
Code,
|
||||
OrderedBulkOperation,
|
||||
UnorderedBulkOperation,
|
||||
InsertWriteOpResult,
|
||||
CollectionInsertManyOptions,
|
||||
CollectionInsertOneOptions,
|
||||
InsertOneWriteOpResult,
|
||||
CommandCursor,
|
||||
MapReduceOptions,
|
||||
ParallelCollectionScanOptions,
|
||||
ReplaceOneOptions,
|
||||
UpdateWriteOpResult,
|
||||
CollStats
|
||||
} from "mongodb";
|
||||
|
||||
/**
|
||||
* Runs queries on a single MongoDB connection.
|
||||
*/
|
||||
export class MongoQueryRunner implements QueryRunner {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(protected databaseConnection: DatabaseConnection,
|
||||
protected driver: MongoDriver,
|
||||
protected logger: Logger) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB.
|
||||
*/
|
||||
cursor(collectionName: string, query?: ObjectLiteral): Cursor<any> {
|
||||
return this.getCollection(collectionName).find(query || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an aggregation framework pipeline against the collection.
|
||||
*/
|
||||
aggregate(collectionName: string, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor<any> {
|
||||
return this.getCollection(collectionName).aggregate(pipeline, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a bulkWrite operation without a fluent API.
|
||||
*/
|
||||
async bulkWrite(collectionName: string, operations: ObjectLiteral[], options?: CollectionBluckWriteOptions): Promise<BulkWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).bulkWrite(operations, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of matching documents in the db to a query.
|
||||
*/
|
||||
async count(collectionName: string, query?: ObjectLiteral, options?: MongoCountPreferences): Promise<any> {
|
||||
return await this.getCollection(collectionName).count(query || {}, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the db and collection.
|
||||
*/
|
||||
async createCollectionIndex(collectionName: string, fieldOrSpec: string|any, options?: IndexOptions): Promise<string> {
|
||||
return await this.getCollection(collectionName).createIndex(fieldOrSpec, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple indexes in the collection, this method is only supported for MongoDB 2.6 or higher.
|
||||
* Earlier version of MongoDB will throw a command not supported error. Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
|
||||
*/
|
||||
async createCollectionIndexes(collectionName: string, indexSpecs: ObjectLiteral[]): Promise<void> {
|
||||
return await this.getCollection(collectionName).createIndexes(indexSpecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple documents on MongoDB.
|
||||
*/
|
||||
async deleteMany(collectionName: string, query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).deleteMany(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a document on MongoDB.
|
||||
*/
|
||||
async deleteOne(collectionName: string, query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).deleteOne(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* The distinct command returns returns a list of distinct values for the given key across a collection.
|
||||
*/
|
||||
async distinct(collectionName: string, key: string, query: ObjectLiteral, options?: { readPreference?: ReadPreference|string }): Promise<any> {
|
||||
return await this.getCollection(collectionName).distinct(key, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an index from this collection.
|
||||
*/
|
||||
async dropCollectionIndex(collectionName: string, indexName: string, options?: CollectionOptions): Promise<any> {
|
||||
return await this.getCollection(collectionName).dropIndex(indexName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all indexes from the collection.
|
||||
*/
|
||||
async dropCollectionIndexes(collectionName: string): Promise<any> {
|
||||
return await this.getCollection(collectionName).dropIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndDelete(collectionName: string, query: ObjectLiteral, options?: { projection?: Object, sort?: Object, maxTimeMS?: number }): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).findOneAndDelete(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndReplace(collectionName: string, query: ObjectLiteral, replacement: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).findOneAndReplace(query, replacement, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and update it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndUpdate(collectionName: string, query: ObjectLiteral, update: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.getCollection(collectionName).findOneAndUpdate(query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a geo search using a geo haystack index on a collection.
|
||||
*/
|
||||
async geoHaystackSearch(collectionName: string, x: number, y: number, options?: GeoHaystackSearchOptions): Promise<any> {
|
||||
return await this.getCollection(collectionName).geoHaystackSearch(x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the geoNear command to search for items in the collection.
|
||||
*/
|
||||
async geoNear(collectionName: string, x: number, y: number, options?: GeoNearOptions): Promise<any> {
|
||||
return await this.getCollection(collectionName).geoNear(x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a group command across a collection.
|
||||
*/
|
||||
async group(collectionName: string, keys: Object|Array<any>|Function|Code, condition: Object, initial: Object, reduce: Function|Code, finalize: Function|Code, command: boolean, options?: { readPreference?: ReadPreference | string }): Promise<any> {
|
||||
return await this.getCollection(collectionName).group(keys, condition, initial, reduce, finalize, command, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
async collectionIndexes(collectionName: string): Promise<any> {
|
||||
return await this.getCollection(collectionName).indexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
async collectionIndexExists(collectionName: string, indexes: string|string[]): Promise<boolean> {
|
||||
return await this.getCollection(collectionName).indexExists(indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves this collections index info.
|
||||
*/
|
||||
async collectionIndexInformation(collectionName: string, options?: { full: boolean }): Promise<any> {
|
||||
return await this.getCollection(collectionName).indexInformation(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an In order bulk write operation, operations will be serially executed in the order they are added, creating a new operation for each switch in types.
|
||||
*/
|
||||
initializeOrderedBulkOp(collectionName: string, options?: CollectionOptions): OrderedBulkOperation {
|
||||
return this.getCollection(collectionName).initializeOrderedBulkOp(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
|
||||
*/
|
||||
initializeUnorderedBulkOp(collectionName: string, options?: CollectionOptions): UnorderedBulkOperation {
|
||||
return this.getCollection(collectionName).initializeUnorderedBulkOp(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an array of documents into MongoDB.
|
||||
*/
|
||||
async insertMany(collectionName: string, docs: ObjectLiteral[], options?: CollectionInsertManyOptions): Promise<InsertWriteOpResult> {
|
||||
return await this.getCollection(collectionName).insertMany(docs, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a single document into MongoDB.
|
||||
*/
|
||||
async insertOne(collectionName: string, doc: ObjectLiteral, options?: CollectionInsertOneOptions): Promise<InsertOneWriteOpResult> {
|
||||
return await this.getCollection(collectionName).insertOne(doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the collection is a capped collection.
|
||||
*/
|
||||
async isCapped(collectionName: string): Promise<any> {
|
||||
return await this.getCollection(collectionName).isCapped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all indexes information for the collection.
|
||||
*/
|
||||
listCollectionIndexes(collectionName: string, options?: { batchSize?: number, readPreference?: ReadPreference|string }): CommandCursor {
|
||||
return this.getCollection(collectionName).listIndexes(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
|
||||
*/
|
||||
async mapReduce(collectionName: string, map: Function|string, reduce: Function|string, options?: MapReduceOptions): Promise<any> {
|
||||
return await this.getCollection(collectionName).mapReduce(map, reduce, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return N number of parallel cursors for a collection allowing parallel reading of entire collection.
|
||||
* There are no ordering guarantees for returned results.
|
||||
*/
|
||||
async parallelCollectionScan(collectionName: string, options?: ParallelCollectionScanOptions): Promise<Cursor<any>[]> {
|
||||
return await this.getCollection(collectionName).parallelCollectionScan(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
async reIndex(collectionName: string): Promise<any> {
|
||||
return await this.getCollection(collectionName).reIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
async rename(collectionName: string, newName: string, options?: { dropTarget?: boolean }): Promise<Collection> {
|
||||
return await this.getCollection(collectionName).rename(newName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a document on MongoDB.
|
||||
*/
|
||||
async replaceOne(collectionName: string, query: ObjectLiteral, doc: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return await this.getCollection(collectionName).replaceOne(query, doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the collection statistics.
|
||||
*/
|
||||
async stats(collectionName: string, options?: { scale: number }): Promise<CollStats> {
|
||||
return await this.getCollection(collectionName).stats(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple documents on MongoDB.
|
||||
*/
|
||||
async updateMany(collectionName: string, query: ObjectLiteral, update: ObjectLiteral, options?: { upsert?: boolean, w?: any, wtimeout?: number, j?: boolean }): Promise<UpdateWriteOpResult> {
|
||||
return await this.getCollection(collectionName).updateMany(query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single document on MongoDB.
|
||||
*/
|
||||
async updateOne(collectionName: string, query: ObjectLiteral, update: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return await this.getCollection(collectionName).updateOne(query, update, options);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Implemented Methods (from QueryRunner)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* For MongoDB database we don't release connection, because its single connection.
|
||||
*/
|
||||
async release(): Promise<void> {
|
||||
// releasing connection are not supported by mongodb driver, so simply don't do anything here
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all collections from the currently connected database.
|
||||
* Be careful with using this method and avoid using it in production or migrations
|
||||
* (because it can clear all your database).
|
||||
*/
|
||||
async clearDatabase(): Promise<void> {
|
||||
await this.databaseConnection.connection.dropDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts transaction.
|
||||
*/
|
||||
async beginTransaction(): Promise<void> {
|
||||
// transactions are not supported by mongodb driver, so simply don't do anything here
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits transaction.
|
||||
*/
|
||||
async commitTransaction(): Promise<void> {
|
||||
// transactions are not supported by mongodb driver, so simply don't do anything here
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollbacks transaction.
|
||||
*/
|
||||
async rollbackTransaction(): Promise<void> {
|
||||
// transactions are not supported by mongodb driver, so simply don't do anything here
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if transaction is in progress.
|
||||
*/
|
||||
isTransactionActive(): boolean {
|
||||
return this.databaseConnection.isTransactionActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given SQL query.
|
||||
*/
|
||||
query(query: string, parameters?: any[]): Promise<any> {
|
||||
throw new Error(`Executing SQL query is not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new row with given values into given table.
|
||||
*/
|
||||
async insert(collectionName: string, keyValues: ObjectLiteral, generatedColumn?: ColumnMetadata): Promise<any> {
|
||||
const results = await this.databaseConnection
|
||||
.connection
|
||||
.collection(collectionName)
|
||||
.insertOne(keyValues);
|
||||
|
||||
return results.insertedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates rows that match given conditions in the given table.
|
||||
*/
|
||||
async update(collectionName: string, valuesMap: ObjectLiteral, conditions: ObjectLiteral): Promise<void> {
|
||||
await this.databaseConnection
|
||||
.connection
|
||||
.collection(collectionName)
|
||||
.replaceOne(conditions, valuesMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes from the given table by a given conditions.
|
||||
*/
|
||||
async delete(collectionName: string, condition: string, parameters?: any[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Deletes from the given table by a given conditions.
|
||||
*/
|
||||
async delete(collectionName: string, conditions: ObjectLiteral): Promise<void>;
|
||||
|
||||
/**
|
||||
* Deletes from the given table by a given conditions.
|
||||
*/
|
||||
async delete(collectionName: string, conditions: ObjectLiteral|string, maybeParameters?: any[]): Promise<void> {
|
||||
if (typeof conditions === "string")
|
||||
throw new Error(`String condition is not supported by MongoDB driver.`);
|
||||
|
||||
await this.databaseConnection
|
||||
.connection
|
||||
.collection(collectionName)
|
||||
.deleteOne(conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts rows into the closure table.
|
||||
*/
|
||||
async insertIntoClosureTable(collectionName: string, newEntityId: any, parentId: any, hasLevel: boolean): Promise<number> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads given table's data from the database.
|
||||
*/
|
||||
async loadTableSchema(collectionName: string): Promise<TableSchema|undefined> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all tables (with given names) from the database and creates a TableSchema from them.
|
||||
*/
|
||||
async loadTableSchemas(collectionNames: string[]): Promise<TableSchema[]> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
// так я от тебя не слышу что ты получаешь удовольствие. все что я слышу это как ты делаешь холодные расчеты для вы
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if table with the given name exist in the database.
|
||||
*/
|
||||
async hasTable(collectionName: string): Promise<boolean> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new table from the given table schema and column schemas inside it.
|
||||
*/
|
||||
async createTable(table: TableSchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if column with the given name exist in the given table.
|
||||
*/
|
||||
async hasColumn(collectionName: string, columnName: string): Promise<boolean> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new column from the column schema in the table.
|
||||
*/
|
||||
async addColumn(collectionName: string, column: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new column from the column schema in the table.
|
||||
*/
|
||||
async addColumn(tableSchema: TableSchema, column: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new column from the column schema in the table.
|
||||
*/
|
||||
async addColumn(tableSchemaOrName: TableSchema|string, column: ColumnSchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new columns from the column schema in the table.
|
||||
*/
|
||||
async addColumns(collectionName: string, columns: ColumnSchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new columns from the column schema in the table.
|
||||
*/
|
||||
async addColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new columns from the column schema in the table.
|
||||
*/
|
||||
async addColumns(tableSchemaOrName: TableSchema|string, columns: ColumnSchema[]): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames column in the given table.
|
||||
*/
|
||||
renameColumn(table: TableSchema, oldColumn: ColumnSchema, newColumn: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Renames column in the given table.
|
||||
*/
|
||||
renameColumn(collectionName: string, oldColumnName: string, newColumnName: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Renames column in the given table.
|
||||
*/
|
||||
async renameColumn(tableSchemaOrName: TableSchema|string, oldColumnSchemaOrName: ColumnSchema|string, newColumnSchemaOrName: ColumnSchema|string): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a column in the table.
|
||||
*/
|
||||
changeColumn(tableSchema: TableSchema, oldColumn: ColumnSchema, newColumn: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Changes a column in the table.
|
||||
*/
|
||||
changeColumn(tableSchema: string, oldColumn: string, newColumn: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Changes a column in the table.
|
||||
*/
|
||||
async changeColumn(tableSchemaOrName: TableSchema|string, oldColumnSchemaOrName: ColumnSchema|string, newColumn: ColumnSchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a column in the table.
|
||||
*/
|
||||
async changeColumns(tableSchema: TableSchema, changedColumns: { newColumn: ColumnSchema, oldColumn: ColumnSchema }[]): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops column in the table.
|
||||
*/
|
||||
async dropColumn(collectionName: string, columnName: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops column in the table.
|
||||
*/
|
||||
async dropColumn(tableSchema: TableSchema, column: ColumnSchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops column in the table.
|
||||
*/
|
||||
async dropColumn(tableSchemaOrName: TableSchema|string, columnSchemaOrName: ColumnSchema|string): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the columns in the table.
|
||||
*/
|
||||
async dropColumns(collectionName: string, columnNames: string[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops the columns in the table.
|
||||
*/
|
||||
async dropColumns(tableSchema: TableSchema, columns: ColumnSchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops the columns in the table.
|
||||
*/
|
||||
async dropColumns(tableSchemaOrName: TableSchema|string, columnSchemasOrNames: ColumnSchema[]|string[]): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates table's primary keys.
|
||||
*/
|
||||
async updatePrimaryKeys(tableSchema: TableSchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new foreign key.
|
||||
*/
|
||||
async createForeignKey(collectionName: string, foreignKey: ForeignKeySchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new foreign key.
|
||||
*/
|
||||
async createForeignKey(tableSchema: TableSchema, foreignKey: ForeignKeySchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new foreign key.
|
||||
*/
|
||||
async createForeignKey(tableSchemaOrName: TableSchema|string, foreignKey: ForeignKeySchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new foreign keys.
|
||||
*/
|
||||
async createForeignKeys(collectionName: string, foreignKeys: ForeignKeySchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new foreign keys.
|
||||
*/
|
||||
async createForeignKeys(tableSchema: TableSchema, foreignKeys: ForeignKeySchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Creates a new foreign keys.
|
||||
*/
|
||||
async createForeignKeys(tableSchemaOrName: TableSchema|string, foreignKeys: ForeignKeySchema[]): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign key from the table.
|
||||
*/
|
||||
async dropForeignKey(collectionName: string, foreignKey: ForeignKeySchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops a foreign key from the table.
|
||||
*/
|
||||
async dropForeignKey(tableSchema: TableSchema, foreignKey: ForeignKeySchema): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops a foreign key from the table.
|
||||
*/
|
||||
async dropForeignKey(tableSchemaOrName: TableSchema|string, foreignKey: ForeignKeySchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign keys from the table.
|
||||
*/
|
||||
async dropForeignKeys(collectionName: string, foreignKeys: ForeignKeySchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops a foreign keys from the table.
|
||||
*/
|
||||
async dropForeignKeys(tableSchema: TableSchema, foreignKeys: ForeignKeySchema[]): Promise<void>;
|
||||
|
||||
/**
|
||||
* Drops a foreign keys from the table.
|
||||
*/
|
||||
async dropForeignKeys(tableSchemaOrName: TableSchema|string, foreignKeys: ForeignKeySchema[]): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new index.
|
||||
*/
|
||||
async createIndex(collectionName: string, index: IndexSchema): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an index from the table.
|
||||
*/
|
||||
async dropIndex(collectionName: string, indexName: string): Promise<void> {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database type from a given column metadata.
|
||||
*/
|
||||
normalizeType(typeOptions: { type: ColumnType, length?: string|number, precision?: number, scale?: number, timezone?: boolean }) {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if "DEFAULT" values in the column metadata and in the database schema are equal.
|
||||
*/
|
||||
compareDefaultValues(columnMetadataValue: any, databaseValue: any): boolean {
|
||||
throw new Error(`Schema update queries are not supported by MongoDB driver.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops collection.
|
||||
*/
|
||||
async truncate(collectionName: string): Promise<void> {
|
||||
await this.databaseConnection
|
||||
.connection
|
||||
.dropCollection(collectionName);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Database name shortcut.
|
||||
*/
|
||||
protected get dbName(): string {
|
||||
return this.driver.options.database as string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets collection from the database with a given name.
|
||||
*/
|
||||
protected getCollection(collectionName: string): Collection {
|
||||
return (this.databaseConnection.connection as Db).collection(collectionName);
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,7 +59,7 @@ export class MysqlDriver implements Driver {
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
|
||||
@ -61,7 +61,7 @@ export class OracleDriver implements Driver {
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
|
||||
@ -64,7 +64,7 @@ export class PostgresDriver implements Driver {
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
@ -136,7 +136,7 @@ export class PostgresDriver implements Driver {
|
||||
if (err) {
|
||||
fail(err);
|
||||
} else {
|
||||
this.databaseConnection && this.databaseConnection.connection.query(`SET search_path TO '${this.schemaName}', 'public';`, (err: any, result: any) => {
|
||||
this.databaseConnection!.connection.query(`SET search_path TO '${this.schemaName}', 'public';`, (err: any, result: any) => {
|
||||
if (err) {
|
||||
this.logger.logFailedQuery(`SET search_path TO '${this.schemaName}', 'public';`);
|
||||
this.logger.logQueryError(err);
|
||||
|
||||
@ -13,7 +13,6 @@ import {IndexSchema} from "../../schema-builder/schema/IndexSchema";
|
||||
import {ForeignKeySchema} from "../../schema-builder/schema/ForeignKeySchema";
|
||||
import {PrimaryKeySchema} from "../../schema-builder/schema/PrimaryKeySchema";
|
||||
import {QueryRunnerAlreadyReleasedError} from "../../query-runner/error/QueryRunnerAlreadyReleasedError";
|
||||
import {NamingStrategyInterface} from "../../naming-strategy/NamingStrategyInterface";
|
||||
import {ColumnType} from "../../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
|
||||
@ -48,7 +48,7 @@ export class SqliteDriver implements Driver {
|
||||
protected databaseConnection: DatabaseConnection|undefined;
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ export class SqlServerDriver implements Driver {
|
||||
protected databaseConnectionPool: DatabaseConnection[] = [];
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ export class WebsqlDriver implements Driver {
|
||||
protected databaseConnection: DatabaseConnection|undefined;
|
||||
|
||||
/**
|
||||
* Logger used go log queries and errors.
|
||||
* Logger used to log queries and errors.
|
||||
*/
|
||||
protected logger: Logger;
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import {RepositoryNotTreeError} from "../connection/error/RepositoryNotTreeError
|
||||
import {NoNeedToReleaseEntityManagerError} from "./error/NoNeedToReleaseEntityManagerError";
|
||||
import {QueryRunnerProviderAlreadyReleasedError} from "../query-runner/error/QueryRunnerProviderAlreadyReleasedError";
|
||||
import {SpecificRepository} from "../repository/SpecificRepository";
|
||||
import {MongoRepository} from "../repository/MongoRepository";
|
||||
|
||||
/**
|
||||
* Common functions shared between different entity manager types.
|
||||
@ -109,6 +110,28 @@ export abstract class BaseEntityManager {
|
||||
return this.connection.getTreeRepository<Entity>(entityClassOrName as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mongodb repository for the given entity class.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClass: ObjectType<Entity>): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb repository for the given entity name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityName: string): MongoRepository<Entity>;
|
||||
|
||||
/**
|
||||
* Gets mongodb repository for the given entity class or name.
|
||||
*/
|
||||
getMongoRepository<Entity>(entityClassOrName: ObjectType<Entity>|string): MongoRepository<Entity> {
|
||||
|
||||
// if single db connection is used then create its own repository with reused query runner
|
||||
if (this.queryRunnerProvider)
|
||||
return this.obtainRepositoryAggregator(entityClassOrName as any).repository as MongoRepository<Entity>;
|
||||
|
||||
return this.connection.getMongoRepository<Entity>(entityClassOrName as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets specific repository for the given entity class.
|
||||
* If single database connection mode is used, then repository is obtained from the
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {FindOptions} from "../find-options/FindOptions";
|
||||
import {FindManyOptions} from "../find-options/FindManyOptions";
|
||||
import {ObjectType} from "../common/ObjectType";
|
||||
import {BaseEntityManager} from "./BaseEntityManager";
|
||||
import {QueryRunnerProviderAlreadyReleasedError} from "../query-runner/error/QueryRunnerProviderAlreadyReleasedError";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {FindOneOptions} from "../find-options/FindOneOptions";
|
||||
|
||||
/**
|
||||
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
|
||||
@ -129,135 +130,101 @@ export class EntityManager extends BaseEntityManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given options.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>, options?: FindManyOptions<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>): Promise<Entity[]>;
|
||||
find<Entity>(entityClass: ObjectType<Entity>, conditions?: Partial<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Finds entities that match given find options ir conditions.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>, options: FindOptions): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral, options: FindOptions): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
find<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<Entity[]> {
|
||||
if (conditionsOrFindOptions && options) {
|
||||
return this.getRepository(entityClass).find(conditionsOrFindOptions, options);
|
||||
|
||||
} else if (conditionsOrFindOptions) {
|
||||
return this.getRepository(entityClass).find(conditionsOrFindOptions);
|
||||
|
||||
} else {
|
||||
return this.getRepository(entityClass).find();
|
||||
}
|
||||
find<Entity>(entityClass: ObjectType<Entity>, optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
return this.getRepository(entityClass).find(optionsOrConditions as ObjectLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Finds entities that match given find options.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>): Promise<[ Entity[], number ]>;
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, options?: FindManyOptions<Entity>): Promise<[Entity[], number]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral): Promise<[ Entity[], number ]>;
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, conditions?: Partial<Entity>): Promise<[Entity[], number]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Finds entities that match given find options and conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, options: FindOptions): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
*/
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral, options: FindOptions): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
*/
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<[Entity[], number]> {
|
||||
if (conditionsOrFindOptions && options) {
|
||||
return this.getRepository(entityClass).findAndCount(conditionsOrFindOptions, options);
|
||||
|
||||
} else if (conditionsOrFindOptions) {
|
||||
return this.getRepository(entityClass).findAndCount(conditionsOrFindOptions);
|
||||
|
||||
} else {
|
||||
return this.getRepository(entityClass).findAndCount();
|
||||
}
|
||||
findAndCount<Entity>(entityClass: ObjectType<Entity>, optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<[Entity[], number]> {
|
||||
return this.getRepository(entityClass).findAndCount(optionsOrConditions as ObjectLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
* Finds first entity that matches given find options.
|
||||
*/
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>): Promise<Entity|undefined>;
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, options?: FindOneOptions<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral): Promise<Entity|undefined>;
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, conditions?: Partial<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, options: FindOptions): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, conditions: ObjectLiteral, options: FindOptions): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<Entity|undefined> {
|
||||
if (conditionsOrFindOptions && options) {
|
||||
return this.getRepository(entityClass).findOne(conditionsOrFindOptions, options);
|
||||
|
||||
} else if (conditionsOrFindOptions) {
|
||||
return this.getRepository(entityClass).findOne(conditionsOrFindOptions);
|
||||
|
||||
} else {
|
||||
return this.getRepository(entityClass).findOne();
|
||||
}
|
||||
findOne<Entity>(entityClass: ObjectType<Entity>, optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
return this.getRepository(entityClass).findOne(optionsOrConditions as ObjectLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities with ids.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
findByIds<Entity>(entityClass: ObjectType<Entity>, ids: any[], options?: FindOptions): Promise<Entity[]> {
|
||||
return this.getRepository(entityClass).findByIds(ids, options);
|
||||
findByIds<Entity>(entityClass: ObjectType<Entity>, ids: any[], options?: FindManyOptions<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities with ids.
|
||||
* Optionally conditions can be applied.
|
||||
*/
|
||||
findByIds<Entity>(entityClass: ObjectType<Entity>, ids: any[], conditions?: Partial<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities with ids.
|
||||
* Optionally find options or conditions can be applied.
|
||||
*/
|
||||
findByIds<Entity>(entityClass: ObjectType<Entity>, ids: any[], optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
return this.getRepository(entityClass).findByIds(ids, optionsOrConditions as ObjectLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entity with given id.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
findOneById<Entity>(entityClass: ObjectType<Entity>, id: any, options?: FindOptions): Promise<Entity|undefined> {
|
||||
return this.getRepository(entityClass).findOneById(id, options);
|
||||
findOneById<Entity>(entityClass: ObjectType<Entity>, id: any, options?: FindOneOptions<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds entity with given id.
|
||||
* Optionally conditions can be applied.
|
||||
*/
|
||||
findOneById<Entity>(entityClass: ObjectType<Entity>, id: any, conditions?: Partial<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds entity with given id.
|
||||
* Optionally find options or conditions can be applied.
|
||||
*/
|
||||
findOneById<Entity>(entityClass: ObjectType<Entity>, id: any, optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
return this.getRepository(entityClass).findOneById(id, optionsOrConditions as ObjectLiteral);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
334
src/entity-manager/MongoEntityManager.ts
Normal file
334
src/entity-manager/MongoEntityManager.ts
Normal file
@ -0,0 +1,334 @@
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {EntityManager} from "./EntityManager";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {ObjectType} from "../common/ObjectType";
|
||||
import {
|
||||
Cursor,
|
||||
Collection,
|
||||
MongoCountPreferences,
|
||||
CollectionAggregationOptions,
|
||||
AggregationCursor,
|
||||
CollectionBluckWriteOptions,
|
||||
BulkWriteOpResultObject,
|
||||
IndexOptions,
|
||||
CollectionOptions,
|
||||
DeleteWriteOpResultObject,
|
||||
FindAndModifyWriteOpResultObject,
|
||||
FindOneAndReplaceOption,
|
||||
GeoHaystackSearchOptions,
|
||||
GeoNearOptions,
|
||||
ReadPreference,
|
||||
Code,
|
||||
OrderedBulkOperation,
|
||||
UnorderedBulkOperation,
|
||||
InsertWriteOpResult,
|
||||
CollectionInsertManyOptions,
|
||||
CollectionInsertOneOptions,
|
||||
InsertOneWriteOpResult,
|
||||
CommandCursor,
|
||||
MapReduceOptions,
|
||||
ParallelCollectionScanOptions,
|
||||
ReplaceOneOptions,
|
||||
UpdateWriteOpResult,
|
||||
CollStats
|
||||
} from "mongodb";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
|
||||
* whatever entity type are you passing.
|
||||
*
|
||||
* This implementation is used for MongoDB driver which has some specifics in its EntityManager.
|
||||
*/
|
||||
export class MongoEntityManager extends EntityManager {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connection: Connection, queryRunnerProvider?: QueryRunnerProvider) {
|
||||
super(connection, queryRunnerProvider);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Overridden Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Executes raw SQL query and returns raw database results.
|
||||
*/
|
||||
query(query: string, parameters?: any[]): Promise<any> {
|
||||
throw new Error(`Queries aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided entity manager.
|
||||
*/
|
||||
transaction(runInTransaction: (entityManger: EntityManager) => Promise<any>): Promise<any> {
|
||||
throw new Error(`Transactions aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Query Builder with MongoDB is not supported yet.
|
||||
* Calling this method will return an error.
|
||||
*/
|
||||
createQueryBuilder<Entity>(entityClassOrName: ObjectType<Entity>|string, alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
throw new Error(`Query Builder is not supported by MongoDB.`);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB.
|
||||
*/
|
||||
createCursor<Entity>(entityClassOrName: ObjectType<Entity>|string, query?: ObjectLiteral): Cursor<Entity> {
|
||||
return this.getMongoRepository(entityClassOrName as any).createCursor(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB.
|
||||
* This returns modified version of cursor that transforms each result into Entity model.
|
||||
*/
|
||||
createEntityCursor<Entity>(entityClassOrName: ObjectType<Entity>|string, query?: ObjectLiteral): Cursor<Entity> {
|
||||
return this.getMongoRepository(entityClassOrName as any).createEntityCursor(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an aggregation framework pipeline against the collection.
|
||||
*/
|
||||
aggregate<Entity>(entityClassOrName: ObjectType<Entity>|string, pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor<Entity> {
|
||||
return this.getMongoRepository(entityClassOrName as any).aggregate(pipeline, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a bulkWrite operation without a fluent API.
|
||||
*/
|
||||
bulkWrite<Entity>(entityClassOrName: ObjectType<Entity>|string, operations: ObjectLiteral[], options?: CollectionBluckWriteOptions): Promise<BulkWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).bulkWrite(operations, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of matching documents in the db to a query.
|
||||
*/
|
||||
count<Entity>(entityClassOrName: ObjectType<Entity>|string, query?: ObjectLiteral, options?: MongoCountPreferences): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).count(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the db and collection.
|
||||
*/
|
||||
createCollectionIndex<Entity>(entityClassOrName: ObjectType<Entity>|string, fieldOrSpec: string|any, options?: IndexOptions): Promise<string> {
|
||||
return this.getMongoRepository(entityClassOrName as any).createCollectionIndex(fieldOrSpec, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple indexes in the collection, this method is only supported for MongoDB 2.6 or higher.
|
||||
* Earlier version of MongoDB will throw a command not supported error.
|
||||
* Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
|
||||
*/
|
||||
createCollectionIndexes<Entity>(entityClassOrName: ObjectType<Entity>|string, indexSpecs: ObjectLiteral[]): Promise<void> {
|
||||
return this.getMongoRepository(entityClassOrName as any).createCollectionIndexes(indexSpecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple documents on MongoDB.
|
||||
*/
|
||||
deleteMany<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).deleteMany(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a document on MongoDB.
|
||||
*/
|
||||
deleteOne<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).deleteOne(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* The distinct command returns returns a list of distinct values for the given key across a collection.
|
||||
*/
|
||||
distinct<Entity>(entityClassOrName: ObjectType<Entity>|string, key: string, query: ObjectLiteral, options?: { readPreference?: ReadPreference|string }): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).distinct(key, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an index from this collection.
|
||||
*/
|
||||
dropCollectionIndex<Entity>(entityClassOrName: ObjectType<Entity>|string, indexName: string, options?: CollectionOptions): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).dropCollectionIndex(indexName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all indexes from the collection.
|
||||
*/
|
||||
dropCollectionIndexes<Entity>(entityClassOrName: ObjectType<Entity>|string): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).dropCollectionIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
findOneAndDelete<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, options?: { projection?: Object, sort?: Object, maxTimeMS?: number }): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).findOneAndDelete(query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
findOneAndReplace<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, replacement: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).findOneAndReplace(query, replacement, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and update it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
findOneAndUpdate<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, update: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return this.getMongoRepository(entityClassOrName as any).findOneAndUpdate(query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a geo search using a geo haystack index on a collection.
|
||||
*/
|
||||
geoHaystackSearch<Entity>(entityClassOrName: ObjectType<Entity>|string, x: number, y: number, options?: GeoHaystackSearchOptions): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).geoHaystackSearch(x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the geoNear command to search for items in the collection.
|
||||
*/
|
||||
geoNear<Entity>(entityClassOrName: ObjectType<Entity>|string, x: number, y: number, options?: GeoNearOptions): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).geoNear(x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a group command across a collection.
|
||||
*/
|
||||
group<Entity>(entityClassOrName: ObjectType<Entity>|string, keys: Object|Array<any>|Function|Code, condition: Object, initial: Object, reduce: Function|Code, finalize: Function|Code, command: boolean, options?: { readPreference?: ReadPreference | string }): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).group(keys, condition, initial, reduce, finalize, command, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
collectionIndexes<Entity>(entityClassOrName: ObjectType<Entity>|string): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).collectionIndexes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
collectionIndexExists<Entity>(entityClassOrName: ObjectType<Entity>|string, indexes: string|string[]): Promise<boolean> {
|
||||
return this.getMongoRepository(entityClassOrName as any).collectionIndexExists(indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves this collections index info.
|
||||
*/
|
||||
collectionIndexInformation<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: { full: boolean }): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).collectionIndexInformation(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an In order bulk write operation, operations will be serially executed in the order they are added, creating a new operation for each switch in types.
|
||||
*/
|
||||
initializeOrderedBulkOp<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: CollectionOptions): OrderedBulkOperation {
|
||||
return this.getMongoRepository(entityClassOrName as any).initializeOrderedBulkOp(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
|
||||
*/
|
||||
initializeUnorderedBulkOp<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: CollectionOptions): UnorderedBulkOperation {
|
||||
return this.getMongoRepository(entityClassOrName as any).initializeUnorderedBulkOp(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an array of documents into MongoDB.
|
||||
*/
|
||||
insertMany<Entity>(entityClassOrName: ObjectType<Entity>|string, docs: ObjectLiteral[], options?: CollectionInsertManyOptions): Promise<InsertWriteOpResult> {
|
||||
return this.getMongoRepository(entityClassOrName as any).insertMany(docs, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a single document into MongoDB.
|
||||
*/
|
||||
insertOne<Entity>(entityClassOrName: ObjectType<Entity>|string, doc: ObjectLiteral, options?: CollectionInsertOneOptions): Promise<InsertOneWriteOpResult> {
|
||||
return this.getMongoRepository(entityClassOrName as any).insertOne(doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the collection is a capped collection.
|
||||
*/
|
||||
isCapped<Entity>(entityClassOrName: ObjectType<Entity>|string): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).isCapped();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all indexes information for the collection.
|
||||
*/
|
||||
listCollectionIndexes<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: { batchSize?: number, readPreference?: ReadPreference|string }): CommandCursor {
|
||||
return this.getMongoRepository(entityClassOrName as any).listCollectionIndexes(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
|
||||
*/
|
||||
mapReduce<Entity>(entityClassOrName: ObjectType<Entity>|string, map: Function|string, reduce: Function|string, options?: MapReduceOptions): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).mapReduce(map, reduce, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return N number of parallel cursors for a collection allowing parallel reading of entire collection.
|
||||
* There are no ordering guarantees for returned results.
|
||||
*/
|
||||
parallelCollectionScan<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: ParallelCollectionScanOptions): Promise<Cursor<Entity>[]> {
|
||||
return this.getMongoRepository(entityClassOrName as any).parallelCollectionScan(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
reIndex<Entity>(entityClassOrName: ObjectType<Entity>|string): Promise<any> {
|
||||
return this.getMongoRepository(entityClassOrName as any).reIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
rename<Entity>(entityClassOrName: ObjectType<Entity>|string, newName: string, options?: { dropTarget?: boolean }): Promise<Collection> {
|
||||
return this.getMongoRepository(entityClassOrName as any).rename(newName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a document on MongoDB.
|
||||
*/
|
||||
replaceOne<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, doc: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return this.getMongoRepository(entityClassOrName as any).replaceOne(query, doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the collection statistics.
|
||||
*/
|
||||
stats<Entity>(entityClassOrName: ObjectType<Entity>|string, options?: { scale: number }): Promise<CollStats> {
|
||||
return this.getMongoRepository(entityClassOrName as any).stats(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple documents on MongoDB.
|
||||
*/
|
||||
updateMany<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, update: ObjectLiteral, options?: { upsert?: boolean, w?: any, wtimeout?: number, j?: boolean }): Promise<UpdateWriteOpResult> {
|
||||
return this.getMongoRepository(entityClassOrName as any).updateMany(query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single document on MongoDB.
|
||||
*/
|
||||
updateOne<Entity>(entityClassOrName: ObjectType<Entity>|string, query: ObjectLiteral, update: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return this.getMongoRepository(entityClassOrName as any).updateOne(query, update, options);
|
||||
}
|
||||
|
||||
}
|
||||
18
src/find-options/FindManyOptions.ts
Normal file
18
src/find-options/FindManyOptions.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {FindOneOptions} from "./FindOneOptions";
|
||||
|
||||
/**
|
||||
* Defines a special criteria to find specific entities.
|
||||
*/
|
||||
export interface FindManyOptions<Entity> extends FindOneOptions<Entity> {
|
||||
|
||||
/**
|
||||
* Offset (paginated) where from entities should be taken.
|
||||
*/
|
||||
from?: number;
|
||||
|
||||
/**
|
||||
* Limit (paginated) - max number of entities should be taken.
|
||||
*/
|
||||
take?: number;
|
||||
|
||||
}
|
||||
21
src/find-options/FindOneOptions.ts
Normal file
21
src/find-options/FindOneOptions.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Defines a special criteria to find specific entity.
|
||||
*/
|
||||
export interface FindOneOptions<Entity> {
|
||||
|
||||
/**
|
||||
* Simple condition that should be applied to match entities.
|
||||
*/
|
||||
where?: Partial<Entity>;
|
||||
|
||||
/**
|
||||
* Specifies what relations should be loaded.
|
||||
*/
|
||||
join?: JoinOptions;
|
||||
|
||||
/**
|
||||
* Order, in which entities should be ordered.
|
||||
*/
|
||||
order?: { [P in keyof Entity]?: "ASC"|"DESC" };
|
||||
|
||||
}
|
||||
@ -1,144 +0,0 @@
|
||||
import {OrderByCondition} from "./OrderByCondition";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
* Options to be passed to find methods.
|
||||
*
|
||||
* Example:
|
||||
* const options: FindOptions = {
|
||||
* alias: "photo",
|
||||
* limit: 100,
|
||||
* offset: 0,
|
||||
* firstResult: 5,
|
||||
* maxResults: 10,
|
||||
* where: "photo.likesCount > 0 && photo.likesCount < 10",
|
||||
* having: "photo.viewsCount > 0 && photo.viewsCount < 1000",
|
||||
* whereConditions: {
|
||||
* "photo.isPublished": true,
|
||||
* "photo.name": "Me and Bears"
|
||||
* },
|
||||
* havingConditions: {
|
||||
* "photo.filename": "bears.jpg"
|
||||
* },
|
||||
* orderBy: {
|
||||
* "photo.id": "DESC"
|
||||
* },
|
||||
* groupBy: [
|
||||
* "photo.name"
|
||||
* ],
|
||||
* leftJoin: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* innerJoin: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* leftJoinAndSelect: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* innerJoinAndSelect: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
export interface FindOptions {
|
||||
|
||||
/**
|
||||
* Alias of the selected entity.
|
||||
*/
|
||||
alias: string;
|
||||
|
||||
/**
|
||||
* Selection limitation, e.g. LIMIT expression.
|
||||
*/
|
||||
limit?: number;
|
||||
|
||||
/**
|
||||
* From what position to select, e.g. OFFSET expression.
|
||||
*/
|
||||
offset?: number;
|
||||
|
||||
/**
|
||||
* First results to select (offset using pagination).
|
||||
*/
|
||||
firstResult?: number;
|
||||
|
||||
/**
|
||||
* Maximum result to select (limit using pagination).
|
||||
*/
|
||||
maxResults?: number;
|
||||
|
||||
/**
|
||||
* Regular WHERE expression.
|
||||
*/
|
||||
where?: string;
|
||||
|
||||
/**
|
||||
* Regular HAVING expression.
|
||||
*/
|
||||
having?: string;
|
||||
|
||||
/**
|
||||
* WHERE conditions. Key-value object pair, where each key is a column name and value is a column value.
|
||||
* "AND" is applied between all parameters.
|
||||
*/
|
||||
whereConditions?: ObjectLiteral;
|
||||
|
||||
/**
|
||||
* HAVING conditions. Key-value object pair, where each key is a column name and value is a column value.
|
||||
* "AND" is applied between all parameters.
|
||||
*/
|
||||
havingConditions?: ObjectLiteral;
|
||||
|
||||
/**
|
||||
* Array of ORDER BY expressions.
|
||||
*/
|
||||
orderBy?: OrderByCondition;
|
||||
|
||||
/**
|
||||
* Array of column to GROUP BY.
|
||||
*/
|
||||
groupBy?: string[];
|
||||
|
||||
/**
|
||||
* Array of columns to LEFT JOIN.
|
||||
*/
|
||||
leftJoinAndSelect?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to INNER JOIN.
|
||||
*/
|
||||
innerJoinAndSelect?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to LEFT JOIN.
|
||||
*/
|
||||
leftJoin?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to INNER JOIN.
|
||||
*/
|
||||
innerJoin?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Parameters used in the WHERE and HAVING expressions.
|
||||
*/
|
||||
parameters?: Object;
|
||||
|
||||
/**
|
||||
* Indicates if query builder should add virtual columns to the entity too.
|
||||
*/
|
||||
enabledOptions?: ("RELATION_ID_VALUES")[];
|
||||
|
||||
}
|
||||
@ -1,120 +1,152 @@
|
||||
import {FindOptions} from "./FindOptions";
|
||||
import {FindManyOptions} from "./FindManyOptions";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {FindOneOptions} from "./FindOneOptions";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
* Utilities to work with FindOptions.
|
||||
*/
|
||||
export class FindOptionsUtils {
|
||||
|
||||
/**
|
||||
* Checks if given object is really instance of FindOneOptions interface.
|
||||
*/
|
||||
static isFindOneOptions(object: any): object is FindOneOptions<any> {
|
||||
const possibleOptions: FindOneOptions<any> = object;
|
||||
return possibleOptions &&
|
||||
(
|
||||
possibleOptions.where instanceof Object ||
|
||||
possibleOptions.join instanceof Object ||
|
||||
possibleOptions.order instanceof Object
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given object is really instance of FindManyOptions interface.
|
||||
*/
|
||||
static isFindManyOptions(object: any): object is FindManyOptions<any> {
|
||||
const possibleOptions: FindManyOptions<any> = object;
|
||||
return possibleOptions &&
|
||||
(
|
||||
possibleOptions.where instanceof Object ||
|
||||
possibleOptions.join instanceof Object ||
|
||||
possibleOptions.order instanceof Object ||
|
||||
typeof possibleOptions.from === "number" ||
|
||||
typeof possibleOptions.take === "number"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given object is really instance of FindOptions interface.
|
||||
*/
|
||||
static isFindOptions(object: any): object is FindOptions {
|
||||
const possibleOptions: FindOptions = object;
|
||||
return possibleOptions && !!possibleOptions.alias && typeof possibleOptions.alias === "string" && (
|
||||
!!possibleOptions.limit ||
|
||||
!!possibleOptions.offset ||
|
||||
!!possibleOptions.firstResult ||
|
||||
!!possibleOptions.maxResults ||
|
||||
!!possibleOptions.where ||
|
||||
!!possibleOptions.having ||
|
||||
!!possibleOptions.whereConditions ||
|
||||
!!possibleOptions.havingConditions ||
|
||||
!!possibleOptions.orderBy ||
|
||||
!!possibleOptions.groupBy ||
|
||||
!!possibleOptions.leftJoinAndSelect ||
|
||||
!!possibleOptions.innerJoinAndSelect ||
|
||||
!!possibleOptions.leftJoin ||
|
||||
!!possibleOptions.innerJoin ||
|
||||
!!possibleOptions.parameters ||
|
||||
!!possibleOptions.enabledOptions
|
||||
);
|
||||
static extractFindOneOptionsAlias(object: any): string|undefined {
|
||||
if (this.isFindOneOptions(object) && object.join)
|
||||
return object.join.alias;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given object is really instance of FindOptions interface.
|
||||
*/
|
||||
static extractFindManyOptionsAlias(object: any): string|undefined {
|
||||
if (this.isFindManyOptions(object) && object.join)
|
||||
return object.join.alias;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies give find one options to the given query builder.
|
||||
*/
|
||||
static applyFindOneOptionsOrConditionsToQueryBuilder<T>(qb: QueryBuilder<T>, options: FindOneOptions<T>|Partial<T>|undefined): QueryBuilder<T> {
|
||||
if (this.isFindOneOptions(options))
|
||||
return this.applyOptionsToQueryBuilder(qb, options);
|
||||
|
||||
if (options)
|
||||
return this.applyConditions(qb, options);
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies give find many options to the given query builder.
|
||||
*/
|
||||
static applyFindManyOptionsOrConditionsToQueryBuilder<T>(qb: QueryBuilder<T>, options: FindManyOptions<T>|Partial<T>|undefined): QueryBuilder<T> {
|
||||
if (this.isFindManyOptions(options))
|
||||
return this.applyOptionsToQueryBuilder(qb, options);
|
||||
|
||||
if (options)
|
||||
return this.applyConditions(qb, options);
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies give find options to the given query builder.
|
||||
*/
|
||||
static applyOptionsToQueryBuilder(qb: QueryBuilder<any>, options: FindOptions): QueryBuilder<any> {
|
||||
static applyOptionsToQueryBuilder<T>(qb: QueryBuilder<T>, options: FindOneOptions<T>|FindManyOptions<T>|undefined): QueryBuilder<T> {
|
||||
|
||||
if (options.limit)
|
||||
qb.setLimit(options.limit);
|
||||
if (options.offset)
|
||||
qb.setOffset(options.offset);
|
||||
if (options.firstResult)
|
||||
qb.setFirstResult(options.firstResult);
|
||||
if (options.maxResults)
|
||||
qb.setMaxResults(options.maxResults);
|
||||
// if options are not set then simply return query builder. This is made for simplicity of usage.
|
||||
if (!options || !this.isFindOneOptions(options))
|
||||
return qb;
|
||||
|
||||
// apply all options from FindOptions
|
||||
if (options.where)
|
||||
qb.where(options.where);
|
||||
if (options.having)
|
||||
qb.having(options.having);
|
||||
this.applyConditions(qb, options.where);
|
||||
|
||||
if (options.whereConditions) {
|
||||
Object.keys(options.whereConditions).forEach((key, index) => {
|
||||
const name = key.indexOf(".") === -1 ? options.alias + "." + key : key;
|
||||
if (options.whereConditions![key] === null) {
|
||||
qb.andWhere(name + " IS NULL");
|
||||
if ((options as FindManyOptions<T>).from)
|
||||
qb.setFirstResult((options as FindManyOptions<T>).from!);
|
||||
|
||||
} else {
|
||||
const parameterName = "whereConditions_" + index;
|
||||
qb.andWhere(name + "=:" + parameterName, { [parameterName]: options.whereConditions![key] });
|
||||
}
|
||||
});
|
||||
}
|
||||
if ((options as FindManyOptions<T>).take)
|
||||
qb.setMaxResults((options as FindManyOptions<T>).take!);
|
||||
|
||||
if (options.havingConditions) {
|
||||
Object.keys(options.havingConditions).forEach((key, index) => {
|
||||
const name = key.indexOf(".") === -1 ? options.alias + "." + key : key;
|
||||
if (options.havingConditions![key] === null) {
|
||||
qb.andHaving(name + " IS NULL");
|
||||
|
||||
} else {
|
||||
const parameterName = "havingConditions_" + index;
|
||||
qb.andHaving(name + "=:" + parameterName, { [parameterName]: options.whereConditions![key] });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options.orderBy)
|
||||
Object.keys(options.orderBy).forEach(columnName => qb.addOrderBy(columnName, options.orderBy![columnName]));
|
||||
|
||||
if (options.groupBy)
|
||||
options.groupBy.forEach(groupBy => qb.addGroupBy(groupBy));
|
||||
|
||||
if (options.leftJoin)
|
||||
Object.keys(options.leftJoin).forEach(key => {
|
||||
if (options.leftJoin) // this check because of tsc bug
|
||||
qb.leftJoin(options.leftJoin[key], key);
|
||||
if (options.order)
|
||||
Object.keys(options.order).forEach(key => {
|
||||
qb.addOrderBy(qb.alias + "." + key, (options as FindOneOptions<T>).order![key as any]);
|
||||
});
|
||||
|
||||
if (options.innerJoin)
|
||||
Object.keys(options.innerJoin).forEach(key => {
|
||||
if (options.innerJoin) // this check because of tsc bug
|
||||
qb.innerJoin(options.innerJoin[key], key);
|
||||
});
|
||||
if (options.join) {
|
||||
if (options.join.leftJoin)
|
||||
Object.keys(options.join.leftJoin).forEach(key => {
|
||||
qb.leftJoin(options.join!.leftJoin![key], key);
|
||||
});
|
||||
|
||||
if (options.leftJoinAndSelect)
|
||||
Object.keys(options.leftJoinAndSelect).forEach(key => {
|
||||
if (options.leftJoinAndSelect) // this check because of tsc bug
|
||||
qb.leftJoinAndSelect(options.leftJoinAndSelect[key], key);
|
||||
});
|
||||
if (options.join.innerJoin)
|
||||
Object.keys(options.join.innerJoin).forEach(key => {
|
||||
qb.innerJoin(options.join!.innerJoin![key], key);
|
||||
});
|
||||
|
||||
if (options.innerJoinAndSelect)
|
||||
Object.keys(options.innerJoinAndSelect).forEach(key => {
|
||||
if (options.innerJoinAndSelect) // this check because of tsc bug
|
||||
qb.innerJoinAndSelect(options.innerJoinAndSelect[key], key);
|
||||
});
|
||||
if (options.join.leftJoinAndSelect)
|
||||
Object.keys(options.join.leftJoinAndSelect).forEach(key => {
|
||||
qb.leftJoinAndSelect(options.join!.leftJoinAndSelect![key], key);
|
||||
});
|
||||
|
||||
if (options.parameters)
|
||||
qb.setParameters(options.parameters);
|
||||
|
||||
if (options.enabledOptions) {
|
||||
options.enabledOptions.forEach(option => {
|
||||
qb.enableOption(option);
|
||||
});
|
||||
if (options.join.innerJoinAndSelect)
|
||||
Object.keys(options.join.innerJoinAndSelect).forEach(key => {
|
||||
qb.innerJoinAndSelect(options.join!.innerJoinAndSelect![key], key);
|
||||
});
|
||||
}
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies given simple conditions set to a given query builder.
|
||||
*/
|
||||
static applyConditions<T>(qb: QueryBuilder<T>, conditions: ObjectLiteral): QueryBuilder<T> {
|
||||
Object.keys(conditions).forEach((key, index) => {
|
||||
if (conditions![key] === null) {
|
||||
qb.andWhere(`${qb.alias}.${key} IS NULL`);
|
||||
|
||||
} else {
|
||||
const parameterName = "where_" + index;
|
||||
qb.andWhere(`${qb.alias}.${key}=:${parameterName}`)
|
||||
.setParameter(parameterName, conditions![key]);
|
||||
}
|
||||
});
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
}
|
||||
60
src/find-options/JoinOptions.ts
Normal file
60
src/find-options/JoinOptions.ts
Normal file
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Used to specify what entity relations should be loaded.
|
||||
*
|
||||
* Example:
|
||||
* const options: JoinOptions = {
|
||||
* alias: "photo",
|
||||
* leftJoin: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* innerJoin: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* leftJoinAndSelect: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* },
|
||||
* innerJoinAndSelect: {
|
||||
* author: "photo.author",
|
||||
* categories: "categories",
|
||||
* user: "categories.user",
|
||||
* profile: "user.profile"
|
||||
* }
|
||||
* };
|
||||
*/
|
||||
interface JoinOptions {
|
||||
|
||||
/**
|
||||
* Alias of the main entity.
|
||||
*/
|
||||
alias: string;
|
||||
|
||||
/**
|
||||
* Array of columns to LEFT JOIN.
|
||||
*/
|
||||
leftJoinAndSelect?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to INNER JOIN.
|
||||
*/
|
||||
innerJoinAndSelect?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to LEFT JOIN.
|
||||
*/
|
||||
leftJoin?: { [key: string]: string };
|
||||
|
||||
/**
|
||||
* Array of columns to INNER JOIN.
|
||||
*/
|
||||
innerJoin?: { [key: string]: string };
|
||||
|
||||
}
|
||||
@ -84,7 +84,7 @@ export {NamingStrategyInterface} from "./naming-strategy/NamingStrategyInterface
|
||||
export {Repository} from "./repository/Repository";
|
||||
export {TreeRepository} from "./repository/TreeRepository";
|
||||
export {SpecificRepository} from "./repository/SpecificRepository";
|
||||
export {FindOptions} from "./find-options/FindOptions";
|
||||
export {FindManyOptions} from "./find-options/FindManyOptions";
|
||||
export {InsertEvent} from "./subscriber/event/InsertEvent";
|
||||
export {UpdateEvent} from "./subscriber/event/UpdateEvent";
|
||||
export {RemoveEvent} from "./subscriber/event/RemoveEvent";
|
||||
|
||||
@ -10,7 +10,7 @@ import {RelationMetadata} from "./RelationMetadata";
|
||||
* For example, "primary" means that it will be a primary column, or "createDate" means that it will create a create
|
||||
* date column.
|
||||
*/
|
||||
export type ColumnMode = "regular"|"virtual"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel"|"discriminator"|"parentId";
|
||||
export type ColumnMode = "regular"|"virtual"|"createDate"|"updateDate"|"version"|"treeChildrenCount"|"treeLevel"|"discriminator"|"parentId"|"objectId";
|
||||
|
||||
/**
|
||||
* This metadata contains all information about entity's column.
|
||||
@ -255,6 +255,13 @@ export class ColumnMetadata {
|
||||
return this.mode === "version";
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this column contains an object id.
|
||||
*/
|
||||
get isObjectId() {
|
||||
return this.mode === "objectId";
|
||||
}
|
||||
|
||||
/**
|
||||
* If this column references some column, it gets the first referenced column of this column.
|
||||
*/
|
||||
|
||||
@ -397,6 +397,24 @@ export class EntityMetadata {
|
||||
return this._columns.filter(column => column.mode === "parentId");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if entity has an object id column.
|
||||
*/
|
||||
get hasObjectIdColumn(): boolean {
|
||||
return !!this._columns.find(column => column.mode === "objectId");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object id column used with mongodb database.
|
||||
*/
|
||||
get objectIdColumn(): ColumnMetadata {
|
||||
const column = this._columns.find(column => column.mode === "objectId");
|
||||
if (!column)
|
||||
throw new Error(`ObjectId was not found in entity ${this.name}`);
|
||||
|
||||
return column;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets single (values of which does not contain arrays) relations.
|
||||
*/
|
||||
|
||||
@ -147,6 +147,12 @@ export class Subject {
|
||||
*/
|
||||
newlyGeneratedId?: any;
|
||||
|
||||
/**
|
||||
* When subject is newly persisted it may have a generated object id.
|
||||
* This value will be stored here. This is actual only for mongodb database.
|
||||
*/
|
||||
generatedObjectId?: any;
|
||||
|
||||
/**
|
||||
* Generated id of the parent entity. Used in the class-table-inheritance.
|
||||
*/
|
||||
|
||||
@ -250,7 +250,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
// load database entities for all given ids
|
||||
const entities = await this.connection
|
||||
.getRepository<ObjectLiteral>(subjectGroup.target)
|
||||
.createQueryBuilder("operateSubject", this.queryRunnerProvider)
|
||||
.createQueryBuilder("operateSubject", this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.andWhereInIds(allIds)
|
||||
.enableOption("RELATION_ID_VALUES")
|
||||
.getMany();
|
||||
@ -363,7 +363,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
// (example) we need to load a details where details.id = post.details
|
||||
const databaseEntity = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.joinColumn.referencedColumn.propertyName + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity) // (example) subject.entity is a post here
|
||||
.enableOption("RELATION_ID_VALUES")
|
||||
@ -440,7 +440,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
// (example) we need to load a post where post.detailsId = details.id
|
||||
const databaseEntity = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.inverseSideProperty + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity) // (example) subject.entity is a details here, and the value is details.id
|
||||
.enableOption("RELATION_ID_VALUES")
|
||||
@ -527,7 +527,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
|
||||
databaseEntities = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.innerJoin(relation.junctionEntityMetadata.table.name, "persistenceJoinedRelation",
|
||||
escapeAlias("persistenceJoinedRelation") + "." + escapeColumn(relation.joinTable.inverseJoinColumnName) + "=" + escapeAlias(qbAlias) + "." + escapeColumn(relation.joinTable.inverseReferencedColumn.name) +
|
||||
" AND " + escapeAlias("persistenceJoinedRelation") + "." + escapeColumn(relation.joinTable.joinColumnName) + "=:id")
|
||||
@ -546,7 +546,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
|
||||
databaseEntities = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.innerJoin(relation.junctionEntityMetadata.table.name, "persistenceJoinedRelation",
|
||||
escapeAlias("persistenceJoinedRelation") + "." + escapeColumn(relation.joinTable.joinColumnName) + "=" + escapeAlias(qbAlias) + "." + escapeColumn(relation.joinTable.referencedColumn.name) +
|
||||
" AND " + escapeAlias("persistenceJoinedRelation") + "." + escapeColumn(relation.inverseRelation.joinTable.inverseJoinColumnName) + "=:id")
|
||||
@ -565,7 +565,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
|
||||
databaseEntities = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.where(qbAlias + "." + relation.inverseSideProperty + "=:id")
|
||||
.setParameter("id", relationIdInDatabaseEntity)
|
||||
.enableOption("RELATION_ID_VALUES")
|
||||
@ -606,7 +606,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
|
||||
if (id) { // if there is no id (for newly inserted) then we cant load
|
||||
const databaseEntity = await this.connection
|
||||
.getRepository<ObjectLiteral>(valueMetadata.target)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider)
|
||||
.createQueryBuilder(qbAlias, this.queryRunnerProvider) // todo: this wont work for mongodb. implement this in some method and call it here instead?
|
||||
.andWhereInIds([id])
|
||||
.enableOption("RELATION_ID_VALUES")
|
||||
.getOne();
|
||||
|
||||
@ -227,6 +227,10 @@ export class SubjectOperationExecutor {
|
||||
// if we don't have relation id then use special values
|
||||
if (referencedColumn.isGenerated) {
|
||||
relationId = insertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (referencedColumn.isObjectId) {
|
||||
relationId = insertSubject.generatedObjectId;
|
||||
|
||||
}
|
||||
// todo: handle other special types too
|
||||
}
|
||||
@ -245,6 +249,9 @@ export class SubjectOperationExecutor {
|
||||
// if we don't have relation id then use special values
|
||||
if (referencedColumn.isGenerated) {
|
||||
relationId = insertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (referencedColumn.isObjectId) {
|
||||
relationId = insertSubject.generatedObjectId;
|
||||
}
|
||||
// todo: handle other special types too
|
||||
}
|
||||
@ -275,8 +282,14 @@ export class SubjectOperationExecutor {
|
||||
let relationIdOfEntityValue = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
|
||||
if (!relationIdOfEntityValue) {
|
||||
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
|
||||
if (entityValueInsertSubject && columnRelation.joinColumn.referencedColumn.isGenerated) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
|
||||
if (entityValueInsertSubject) {
|
||||
if (columnRelation.joinColumn.referencedColumn.isGenerated) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (columnRelation.joinColumn.referencedColumn.isObjectId) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (relationIdOfEntityValue) {
|
||||
@ -289,6 +302,9 @@ export class SubjectOperationExecutor {
|
||||
} else {
|
||||
if (subject.newlyGeneratedId) {
|
||||
conditions[column.name] = subject.newlyGeneratedId;
|
||||
|
||||
} else if (subject.generatedObjectId) {
|
||||
conditions[column.name] = subject.generatedObjectId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,8 +335,13 @@ export class SubjectOperationExecutor {
|
||||
let relationIdOfEntityValue = entityValue[columnRelation.joinColumn.referencedColumn.propertyName];
|
||||
if (!relationIdOfEntityValue) {
|
||||
const entityValueInsertSubject = this.insertSubjects.find(subject => subject.entity === entityValue);
|
||||
if (entityValueInsertSubject && columnRelation.joinColumn.referencedColumn.isGenerated) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
|
||||
if (entityValueInsertSubject) {
|
||||
if (columnRelation.joinColumn.referencedColumn.isGenerated) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (columnRelation.joinColumn.referencedColumn.isObjectId) {
|
||||
relationIdOfEntityValue = entityValueInsertSubject.generatedObjectId;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (relationIdOfEntityValue) {
|
||||
@ -334,6 +355,10 @@ export class SubjectOperationExecutor {
|
||||
} else {
|
||||
if (entityValueInsertSubject && entityValueInsertSubject.newlyGeneratedId) {
|
||||
conditions[column.name] = entityValueInsertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (entityValueInsertSubject && entityValueInsertSubject.generatedObjectId) {
|
||||
conditions[column.name] = entityValueInsertSubject.generatedObjectId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -348,12 +373,17 @@ export class SubjectOperationExecutor {
|
||||
if (!id) {
|
||||
const insertSubject = this.insertSubjects.find(subject => subject.entity === subject.entity[referencedColumn.propertyName]);
|
||||
if (insertSubject) {
|
||||
id = insertSubject.newlyGeneratedId;
|
||||
if (insertSubject.newlyGeneratedId) {
|
||||
id = insertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (insertSubject.generatedObjectId) {
|
||||
id = insertSubject.generatedObjectId;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateOptions[relation.inverseRelation.joinColumn.name] = id;
|
||||
} else {
|
||||
updateOptions[relation.inverseRelation.joinColumn.name] = subject.entity[referencedColumn.propertyName] || subject.newlyGeneratedId;
|
||||
updateOptions[relation.inverseRelation.joinColumn.name] = subject.entity[referencedColumn.propertyName] || subject.newlyGeneratedId || subRelatedEntity.generatedObjectId;
|
||||
}
|
||||
|
||||
const updatePromise = this.queryRunner.update(relation.inverseEntityMetadata.table.name, updateOptions, conditions);
|
||||
@ -400,8 +430,16 @@ export class SubjectOperationExecutor {
|
||||
if (parentGeneratedId)
|
||||
subject.parentGeneratedId = parentGeneratedId;
|
||||
|
||||
if (newlyGeneratedId && metadata.hasGeneratedColumn)
|
||||
subject.newlyGeneratedId = newlyGeneratedId;
|
||||
// todo: better if insert method will return object with all generated ids, object id, etc.
|
||||
if (newlyGeneratedId) {
|
||||
if (metadata.hasGeneratedColumn) {
|
||||
subject.newlyGeneratedId = newlyGeneratedId;
|
||||
|
||||
} else if (metadata.hasObjectIdColumn) {
|
||||
subject.generatedObjectId = newlyGeneratedId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -445,6 +483,8 @@ export class SubjectOperationExecutor {
|
||||
|
||||
if (referencedColumn.isGenerated)
|
||||
relationValue = alreadyInsertedSubject.newlyGeneratedId;
|
||||
if (referencedColumn.isObjectId)
|
||||
relationValue = alreadyInsertedSubject.generatedObjectId;
|
||||
// if it references to create or update date columns
|
||||
if (referencedColumn.isCreateDate || referencedColumn.isUpdateDate)
|
||||
relationValue = this.connection.driver.preparePersistentValue(alreadyInsertedSubject.date, referencedColumn);
|
||||
@ -559,6 +599,7 @@ export class SubjectOperationExecutor {
|
||||
let newEntityId = subject.entity[referencedColumn.propertyName];
|
||||
if (!newEntityId && referencedColumn.isGenerated) {
|
||||
newEntityId = subject.newlyGeneratedId;
|
||||
// we should not handle object id here because closure tables are not supported by mongodb driver.
|
||||
} // todo: implement other special column types too
|
||||
|
||||
const parentEntity = subject.entity[subject.metadata.treeParentRelation.propertyName];
|
||||
@ -799,6 +840,10 @@ export class SubjectOperationExecutor {
|
||||
if (!ownId) {
|
||||
if (firstColumn.isGenerated) {
|
||||
ownId = subject.newlyGeneratedId;
|
||||
|
||||
} else if (firstColumn.isObjectId) {
|
||||
ownId = subject.generatedObjectId;
|
||||
|
||||
}
|
||||
// todo: implement other special referenced column types (update date, create date, version, discriminator column, etc.)
|
||||
}
|
||||
@ -824,6 +869,10 @@ export class SubjectOperationExecutor {
|
||||
if (insertSubject) {
|
||||
if (secondColumn.isGenerated) {
|
||||
relationId = insertSubject.newlyGeneratedId;
|
||||
|
||||
} else if (secondColumn.isObjectId) {
|
||||
relationId = insertSubject.generatedObjectId;
|
||||
|
||||
}
|
||||
// todo: implement other special values too
|
||||
}
|
||||
@ -890,6 +939,9 @@ export class SubjectOperationExecutor {
|
||||
|
||||
// update entity columns that gets updated on each entity insert
|
||||
this.insertSubjects.forEach(subject => {
|
||||
if (subject.generatedObjectId && subject.metadata.hasObjectIdColumn)
|
||||
subject.entity[subject.metadata.objectIdColumn.propertyName] = subject.generatedObjectId;
|
||||
|
||||
subject.metadata.primaryColumns.forEach(primaryColumn => {
|
||||
if (subject.newlyGeneratedId)
|
||||
subject.entity[primaryColumn.propertyName] = subject.newlyGeneratedId;
|
||||
|
||||
163
src/query-builder/transformer/DocumentToEntityTransformer.ts
Normal file
163
src/query-builder/transformer/DocumentToEntityTransformer.ts
Normal file
@ -0,0 +1,163 @@
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
import {Driver} from "../../driver/Driver";
|
||||
import {MongoDriver} from "../../driver/mongodb/MongoDriver";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
|
||||
/**
|
||||
* Transforms raw document into entity object.
|
||||
* Entity is constructed based on its entity metadata.
|
||||
*/
|
||||
export class DocumentToEntityTransformer {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(// private aliasMap: AliasMap,
|
||||
// private joinMappings: JoinMapping[],
|
||||
// private relationCountMetas: RelationCountMeta[],
|
||||
private enableRelationIdValues: boolean = false
|
||||
) {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
transformAll(documents: ObjectLiteral[], metadata: EntityMetadata) {
|
||||
return documents.map(document => this.transform(document, metadata));
|
||||
}
|
||||
|
||||
transform(document: any, metadata: EntityMetadata) {
|
||||
const entity: any = metadata.create();
|
||||
let hasData = false;
|
||||
|
||||
// handle _id property the special way
|
||||
if (metadata.hasObjectIdColumn && document["_id"]) {
|
||||
// todo: we can't use driver in this class
|
||||
// do we really need prepare hydrated value here? If no then no problem. If yes then think maybe prepareHydratedValue process should be extracted out of driver class?
|
||||
// entity[metadata.objectIdColumn.name] = this.driver.prepareHydratedValue(document["_id"], metadata.objectIdColumn);
|
||||
entity[metadata.objectIdColumn.name] = document["_id"];
|
||||
hasData = true;
|
||||
}
|
||||
|
||||
// add special columns that contains relation ids
|
||||
if (this.enableRelationIdValues) {
|
||||
metadata.columns.filter(column => !!column.relationMetadata).forEach(column => {
|
||||
const valueInObject = document[column.name];
|
||||
if (valueInObject !== undefined && valueInObject !== null && column.propertyName) {
|
||||
// todo: we can't use driver in this class
|
||||
// const value = this.driver.prepareHydratedValue(valueInObject, column);
|
||||
entity[column.propertyName] = valueInObject;
|
||||
hasData = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*this.joinMappings
|
||||
.filter(joinMapping => joinMapping.parentName === alias.name && !joinMapping.alias.parentAliasName && joinMapping.alias.target)
|
||||
.map(joinMapping => {
|
||||
const relatedEntities = this.transformIntoSingleResult(rawSqlResults, joinMapping.alias);
|
||||
const isResultArray = joinMapping.isMany;
|
||||
const result = !isResultArray ? relatedEntities[0] : relatedEntities;
|
||||
|
||||
if (result && (!isResultArray || result.length > 0)) {
|
||||
entity[joinMapping.propertyName] = result;
|
||||
hasData = true;
|
||||
}
|
||||
});*/
|
||||
|
||||
// get value from columns selections and put them into object
|
||||
metadata.columns.forEach(column => {
|
||||
const valueInObject = document[column.name];
|
||||
if (valueInObject !== undefined &&
|
||||
valueInObject !== null &&
|
||||
column.propertyName &&
|
||||
!column.isVirtual &&
|
||||
!column.isParentId &&
|
||||
!column.isDiscriminator) {
|
||||
// const value = this.driver.prepareHydratedValue(valueInObject, column);
|
||||
|
||||
if (column.isInEmbedded) {
|
||||
if (!entity[column.embeddedProperty])
|
||||
entity[column.embeddedProperty] = column.embeddedMetadata.create();
|
||||
|
||||
entity[column.embeddedProperty][column.propertyName] = valueInObject;
|
||||
} else {
|
||||
entity[column.propertyName] = valueInObject;
|
||||
}
|
||||
hasData = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if relation is loaded then go into it recursively and transform its values too
|
||||
/*metadata.relations.forEach(relation => {
|
||||
const relationAlias = this.aliasMap.findAliasByParent(alias.name, relation.propertyName);
|
||||
if (relationAlias) {
|
||||
const joinMapping = this.joinMappings.find(joinMapping => joinMapping.type === "join" && joinMapping.alias === relationAlias);
|
||||
const relatedEntities = this.transformIntoSingleResult(rawSqlResults, relationAlias);
|
||||
const isResultArray = relation.isManyToMany || relation.isOneToMany;
|
||||
const result = !isResultArray ? relatedEntities[0] : relatedEntities;
|
||||
|
||||
if (result) {
|
||||
let propertyName = relation.propertyName;
|
||||
if (joinMapping) {
|
||||
propertyName = joinMapping.propertyName;
|
||||
}
|
||||
|
||||
if (relation.isLazy) {
|
||||
entity["__" + propertyName + "__"] = result;
|
||||
} else {
|
||||
entity[propertyName] = result;
|
||||
}
|
||||
|
||||
if (!isResultArray || result.length > 0)
|
||||
hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if relation has id field then relation id/ids to that field.
|
||||
if (relation.isManyToMany) {
|
||||
if (relationAlias) {
|
||||
const ids: any[] = [];
|
||||
const joinMapping = this.joinMappings.find(joinMapping => joinMapping.type === "relationId" && joinMapping.alias === relationAlias);
|
||||
|
||||
if (relation.idField || joinMapping) {
|
||||
const propertyName = joinMapping ? joinMapping.propertyName : relation.idField as string;
|
||||
const junctionMetadata = relation.junctionEntityMetadata;
|
||||
const columnName = relation.isOwning ? junctionMetadata.columns[1].name : junctionMetadata.columns[0].name;
|
||||
|
||||
rawSqlResults.forEach(results => {
|
||||
if (relationAlias) {
|
||||
const resultsKey = relationAlias.name + "_" + columnName;
|
||||
const value = this.driver.prepareHydratedValue(results[resultsKey], relation.referencedColumn);
|
||||
if (value !== undefined && value !== null)
|
||||
ids.push(value);
|
||||
}
|
||||
});
|
||||
|
||||
if (ids && ids.length)
|
||||
entity[propertyName] = ids;
|
||||
}
|
||||
}
|
||||
} else if (relation.idField) {
|
||||
const relationName = relation.name;
|
||||
entity[relation.idField] = this.driver.prepareHydratedValue(rawSqlResults[0][alias.name + "_" + relationName], relation.referencedColumn);
|
||||
}
|
||||
|
||||
// if relation counter
|
||||
this.relationCountMetas.forEach(joinMeta => {
|
||||
if (joinMeta.alias === relationAlias) {
|
||||
// console.log("relation count was found for relation: ", relation);
|
||||
// joinMeta.entity = entity;
|
||||
joinMeta.entities.push({ entity: entity, metadata: metadata });
|
||||
// console.log(joinMeta);
|
||||
// console.log("---------------------");
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
return hasData ? entity : null;
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,6 +7,8 @@ import {ColumnType} from "../metadata/types/ColumnTypes";
|
||||
|
||||
/**
|
||||
* Runs queries on a single database connection.
|
||||
*
|
||||
* todo: extract schema build operations out of query runner.
|
||||
*/
|
||||
export interface QueryRunner {
|
||||
|
||||
@ -231,6 +233,8 @@ export interface QueryRunner {
|
||||
|
||||
/**
|
||||
* Truncates table.
|
||||
*
|
||||
* todo: probably this should be renamed to drop or clear?
|
||||
*/
|
||||
truncate(tableName: string): Promise<void>;
|
||||
|
||||
|
||||
501
src/repository/MongoRepository.ts
Normal file
501
src/repository/MongoRepository.ts
Normal file
@ -0,0 +1,501 @@
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {Repository} from "./Repository";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {FindManyOptions} from "../find-options/FindManyOptions";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {MongoQueryRunner} from "../driver/mongodb/MongoQueryRunner";
|
||||
import {DocumentToEntityTransformer} from "../query-builder/transformer/DocumentToEntityTransformer";
|
||||
import {FindOneOptions} from "../find-options/FindOneOptions";
|
||||
import {FindOptionsUtils} from "../find-options/FindOptionsUtils";
|
||||
import {
|
||||
Cursor,
|
||||
Collection,
|
||||
MongoCountPreferences,
|
||||
CollectionAggregationOptions,
|
||||
AggregationCursor,
|
||||
CollectionBluckWriteOptions,
|
||||
BulkWriteOpResultObject,
|
||||
IndexOptions,
|
||||
CollectionOptions,
|
||||
DeleteWriteOpResultObject,
|
||||
FindAndModifyWriteOpResultObject,
|
||||
FindOneAndReplaceOption,
|
||||
GeoHaystackSearchOptions,
|
||||
GeoNearOptions,
|
||||
ReadPreference,
|
||||
Code,
|
||||
OrderedBulkOperation,
|
||||
UnorderedBulkOperation,
|
||||
InsertWriteOpResult,
|
||||
CollectionInsertManyOptions,
|
||||
CollectionInsertOneOptions,
|
||||
InsertOneWriteOpResult,
|
||||
CommandCursor,
|
||||
MapReduceOptions,
|
||||
ParallelCollectionScanOptions,
|
||||
ReplaceOneOptions,
|
||||
UpdateWriteOpResult,
|
||||
CollStats, MongoCallback, MongoError, CursorResult
|
||||
} from "mongodb";
|
||||
import {EntityCursor} from "../driver/mongodb/EntityCursor";
|
||||
|
||||
/**
|
||||
* Repository used to manage mongodb documents of a single entity type.
|
||||
*/
|
||||
export class MongoRepository<Entity extends ObjectLiteral> extends Repository<Entity> {
|
||||
|
||||
// todo: implement join from find options too
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Overridden Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Raw SQL query execution is not supported by MongoDB.
|
||||
* Calling this method will return an error.
|
||||
*/
|
||||
query(query: string, parameters?: any[]): Promise<any> {
|
||||
throw new Error(`Queries aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transactions are not supported by MongoDB.
|
||||
* Calling this method will return an error.
|
||||
*/
|
||||
transaction(runInTransaction: (repository: Repository<Entity>) => Promise<any>|any): Promise<any> {
|
||||
throw new Error(`Transactions aren't supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using Query Builder with MongoDB is not supported yet.
|
||||
* Calling this method will return an error.
|
||||
*/
|
||||
createQueryBuilder(alias: string, queryRunnerProvider?: QueryRunnerProvider): QueryBuilder<Entity> {
|
||||
throw new Error(`Query Builder is not supported by MongoDB.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity from the given plan javascript object. If entity already exist in the database, then
|
||||
* it loads it (and everything related to it), replaces all values with the new ones from the given object
|
||||
* and returns this new entity. This new entity is actually a loaded from the db entity with all properties
|
||||
* replaced from the new object.
|
||||
*/
|
||||
async preload(object: Object): Promise<Entity> {
|
||||
// todo: implement
|
||||
return {} as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given find options or conditions.
|
||||
*/
|
||||
async find(optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions);
|
||||
const cursor = await this.createEntityCursor(query);
|
||||
if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) {
|
||||
if (optionsOrConditions.from)
|
||||
cursor.min(optionsOrConditions.from);
|
||||
if (optionsOrConditions.take)
|
||||
cursor.limit(optionsOrConditions.take);
|
||||
if (optionsOrConditions.order)
|
||||
cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order));
|
||||
}
|
||||
return cursor.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given find options or conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
async findAndCount(optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<[ Entity[], number ]> {
|
||||
const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions);
|
||||
const cursor = await this.createEntityCursor(query);
|
||||
if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) {
|
||||
if (optionsOrConditions.from)
|
||||
cursor.min(optionsOrConditions.from);
|
||||
if (optionsOrConditions.take)
|
||||
cursor.limit(optionsOrConditions.take);
|
||||
if (optionsOrConditions.order)
|
||||
cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order));
|
||||
}
|
||||
const [results, count] = await Promise.all<any>([
|
||||
cursor.toArray(),
|
||||
this.queryRunner.count(this.metadata.table.name, query),
|
||||
]);
|
||||
return [results, parseInt(count)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities by ids.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
async findByIds(ids: any[], optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions) || {};
|
||||
query["_id"] = { $in: ids };
|
||||
|
||||
const cursor = await this.createEntityCursor(query);
|
||||
if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) {
|
||||
if (optionsOrConditions.from)
|
||||
cursor.min(optionsOrConditions.from);
|
||||
if (optionsOrConditions.take)
|
||||
cursor.limit(optionsOrConditions.take);
|
||||
if (optionsOrConditions.order)
|
||||
cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order));
|
||||
}
|
||||
return await cursor.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions and/or find options.
|
||||
*/
|
||||
async findOne(optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
const query = this.convertFindOneOptionsOrConditionsToMongodbQuery(optionsOrConditions);
|
||||
const cursor = await this.createEntityCursor(query);
|
||||
if (FindOptionsUtils.isFindOneOptions(optionsOrConditions)) {
|
||||
if (optionsOrConditions.order)
|
||||
cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order));
|
||||
}
|
||||
|
||||
// const result = await cursor.limit(1).next();
|
||||
const result = await cursor.limit(1).toArray();
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entity by given id.
|
||||
* Optionally find options or conditions can be applied.
|
||||
*/
|
||||
async findOneById(id: any, optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
const query = this.convertFindOneOptionsOrConditionsToMongodbQuery(optionsOrConditions) || {};
|
||||
query["_id"] = id;
|
||||
const cursor = await this.createEntityCursor(query);
|
||||
if (FindOptionsUtils.isFindOneOptions(optionsOrConditions)) {
|
||||
if (optionsOrConditions.order)
|
||||
cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order));
|
||||
}
|
||||
|
||||
// const result = await cursor.limit(1).next();
|
||||
const result = await cursor.limit(1).toArray();
|
||||
return result.length > 0 ? result[0] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB.
|
||||
*/
|
||||
createCursor(query?: ObjectLiteral): Cursor<Entity> {
|
||||
return this.queryRunner.cursor(this.metadata.table.name, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor for a query that can be used to iterate over results from MongoDB.
|
||||
* This returns modified version of cursor that transforms each result into Entity model.
|
||||
*/
|
||||
createEntityCursor(query?: ObjectLiteral): Cursor<Entity> {
|
||||
const cursor = this.createCursor(query);
|
||||
const repository = this;
|
||||
cursor.toArray = function (callback?: MongoCallback<Entity[]>) {
|
||||
if (callback) {
|
||||
Cursor.prototype.toArray.call(this, (error: MongoError, results: Entity[]): void => {
|
||||
if (error) {
|
||||
callback(error, results);
|
||||
return;
|
||||
}
|
||||
|
||||
const transformer = new DocumentToEntityTransformer();
|
||||
return callback(error, transformer.transformAll(results, repository.metadata));
|
||||
});
|
||||
} else {
|
||||
return Cursor.prototype.toArray.call(this).then((results: Entity[]) => {
|
||||
const transformer = new DocumentToEntityTransformer();
|
||||
return transformer.transformAll(results, repository.metadata);
|
||||
});
|
||||
}
|
||||
};
|
||||
cursor.next = function (callback?: MongoCallback<CursorResult>) {
|
||||
if (callback) {
|
||||
Cursor.prototype.next.call(this, (error: MongoError, result: CursorResult): void => {
|
||||
if (error || !result) {
|
||||
callback(error, result);
|
||||
return;
|
||||
}
|
||||
|
||||
const transformer = new DocumentToEntityTransformer();
|
||||
return callback(error, transformer.transform(result, repository.metadata));
|
||||
});
|
||||
} else {
|
||||
return Cursor.prototype.next.call(this).then((result: Entity) => {
|
||||
if (!result) return result;
|
||||
const transformer = new DocumentToEntityTransformer();
|
||||
return transformer.transform(result, repository.metadata);
|
||||
});
|
||||
}
|
||||
};
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an aggregation framework pipeline against the collection.
|
||||
*/
|
||||
aggregate(pipeline: ObjectLiteral[], options?: CollectionAggregationOptions): AggregationCursor<Entity> {
|
||||
return this.queryRunner.aggregate(this.metadata.table.name, pipeline, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a bulkWrite operation without a fluent API.
|
||||
*/
|
||||
async bulkWrite(operations: ObjectLiteral[], options?: CollectionBluckWriteOptions): Promise<BulkWriteOpResultObject> {
|
||||
return await this.queryRunner.bulkWrite(this.metadata.table.name, operations, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count number of matching documents in the db to a query.
|
||||
*/
|
||||
async count(query?: ObjectLiteral, options?: MongoCountPreferences): Promise<any> {
|
||||
return await this.queryRunner.count(this.metadata.table.name, query || {}, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the db and collection.
|
||||
*/
|
||||
async createCollectionIndex(fieldOrSpec: string|any, options?: IndexOptions): Promise<string> {
|
||||
return await this.queryRunner.createCollectionIndex(this.metadata.table.name, fieldOrSpec, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates multiple indexes in the collection, this method is only supported for MongoDB 2.6 or higher.
|
||||
* Earlier version of MongoDB will throw a command not supported error.
|
||||
* Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/.
|
||||
*/
|
||||
async createCollectionIndexes(indexSpecs: ObjectLiteral[]): Promise<void> {
|
||||
return await this.queryRunner.createCollectionIndexes(this.metadata.table.name, indexSpecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple documents on MongoDB.
|
||||
*/
|
||||
async deleteMany(query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return await this.queryRunner.deleteMany(this.metadata.table.name, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a document on MongoDB.
|
||||
*/
|
||||
async deleteOne(query: ObjectLiteral, options?: CollectionOptions): Promise<DeleteWriteOpResultObject> {
|
||||
return await this.queryRunner.deleteOne(this.metadata.table.name, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* The distinct command returns returns a list of distinct values for the given key across a collection.
|
||||
*/
|
||||
async distinct(key: string, query: ObjectLiteral, options?: { readPreference?: ReadPreference|string }): Promise<any> {
|
||||
return await this.queryRunner.distinct(this.metadata.table.name, key, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops an index from this collection.
|
||||
*/
|
||||
async dropCollectionIndex(indexName: string, options?: CollectionOptions): Promise<any> {
|
||||
return await this.queryRunner.dropCollectionIndex(this.metadata.table.name, indexName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops all indexes from the collection.
|
||||
*/
|
||||
async dropCollectionIndexes(): Promise<any> {
|
||||
return await this.queryRunner.dropCollectionIndexes(this.metadata.table.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndDelete(query: ObjectLiteral, options?: { projection?: Object, sort?: Object, maxTimeMS?: number }): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.queryRunner.findOneAndDelete(this.metadata.table.name, query, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndReplace(query: ObjectLiteral, replacement: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.queryRunner.findOneAndReplace(this.metadata.table.name, query, replacement, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a document and update it in one atomic operation, requires a write lock for the duration of the operation.
|
||||
*/
|
||||
async findOneAndUpdate(query: ObjectLiteral, update: Object, options?: FindOneAndReplaceOption): Promise<FindAndModifyWriteOpResultObject> {
|
||||
return await this.queryRunner.findOneAndUpdate(this.metadata.table.name, query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a geo search using a geo haystack index on a collection.
|
||||
*/
|
||||
async geoHaystackSearch(x: number, y: number, options?: GeoHaystackSearchOptions): Promise<any> {
|
||||
return await this.queryRunner.geoHaystackSearch(this.metadata.table.name, x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the geoNear command to search for items in the collection.
|
||||
*/
|
||||
async geoNear(x: number, y: number, options?: GeoNearOptions): Promise<any> {
|
||||
return await this.queryRunner.geoNear(this.metadata.table.name, x, y, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a group command across a collection.
|
||||
*/
|
||||
async group(keys: Object|Array<any>|Function|Code, condition: Object, initial: Object, reduce: Function|Code, finalize: Function|Code, command: boolean, options?: { readPreference?: ReadPreference | string }): Promise<any> {
|
||||
return await this.queryRunner.group(this.metadata.table.name, keys, condition, initial, reduce, finalize, command, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
async collectionIndexes(): Promise<any> {
|
||||
return await this.queryRunner.collectionIndexes(this.metadata.table.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the indexes on the collection.
|
||||
*/
|
||||
async collectionIndexExists(indexes: string|string[]): Promise<boolean> {
|
||||
return await this.queryRunner.collectionIndexExists(this.metadata.table.name, indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves this collections index info.
|
||||
*/
|
||||
async collectionIndexInformation(options?: { full: boolean }): Promise<any> {
|
||||
return await this.queryRunner.collectionIndexInformation(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an In order bulk write operation, operations will be serially executed in the order they are added, creating a new operation for each switch in types.
|
||||
*/
|
||||
initializeOrderedBulkOp(options?: CollectionOptions): OrderedBulkOperation {
|
||||
return this.queryRunner.initializeOrderedBulkOp(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order.
|
||||
*/
|
||||
initializeUnorderedBulkOp(options?: CollectionOptions): UnorderedBulkOperation {
|
||||
return this.queryRunner.initializeUnorderedBulkOp(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an array of documents into MongoDB.
|
||||
*/
|
||||
async insertMany(docs: ObjectLiteral[], options?: CollectionInsertManyOptions): Promise<InsertWriteOpResult> {
|
||||
return await this.queryRunner.insertMany(this.metadata.table.name, docs, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a single document into MongoDB.
|
||||
*/
|
||||
async insertOne(doc: ObjectLiteral, options?: CollectionInsertOneOptions): Promise<InsertOneWriteOpResult> {
|
||||
return await this.queryRunner.insertOne(this.metadata.table.name, doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the collection is a capped collection.
|
||||
*/
|
||||
async isCapped(): Promise<any> {
|
||||
return await this.queryRunner.isCapped(this.metadata.table.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of all indexes information for the collection.
|
||||
*/
|
||||
listCollectionIndexes(options?: { batchSize?: number, readPreference?: ReadPreference|string }): CommandCursor {
|
||||
return this.queryRunner.listCollectionIndexes(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Map Reduce across a collection. Be aware that the inline option for out will return an array of results not a collection.
|
||||
*/
|
||||
async mapReduce(map: Function|string, reduce: Function|string, options?: MapReduceOptions): Promise<any> {
|
||||
return await this.queryRunner.mapReduce(this.metadata.table.name, map, reduce, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return N number of parallel cursors for a collection allowing parallel reading of entire collection.
|
||||
* There are no ordering guarantees for returned results.
|
||||
*/
|
||||
async parallelCollectionScan(options?: ParallelCollectionScanOptions): Promise<Cursor<Entity>[]> {
|
||||
return await this.queryRunner.parallelCollectionScan(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
async reIndex(): Promise<any> {
|
||||
return await this.queryRunner.reIndex(this.metadata.table.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections.
|
||||
*/
|
||||
async rename(newName: string, options?: { dropTarget?: boolean }): Promise<Collection> {
|
||||
return await this.queryRunner.rename(this.metadata.table.name, newName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a document on MongoDB.
|
||||
*/
|
||||
async replaceOne(query: ObjectLiteral, doc: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return await this.queryRunner.replaceOne(this.metadata.table.name, query, doc, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the collection statistics.
|
||||
*/
|
||||
async stats(options?: { scale: number }): Promise<CollStats> {
|
||||
return await this.queryRunner.stats(this.metadata.table.name, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update multiple documents on MongoDB.
|
||||
*/
|
||||
async updateMany(query: ObjectLiteral, update: ObjectLiteral, options?: { upsert?: boolean, w?: any, wtimeout?: number, j?: boolean }): Promise<UpdateWriteOpResult> {
|
||||
return await this.queryRunner.updateMany(this.metadata.table.name, query, update, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single document on MongoDB.
|
||||
*/
|
||||
async updateOne(query: ObjectLiteral, update: ObjectLiteral, options?: ReplaceOneOptions): Promise<UpdateWriteOpResult> {
|
||||
return await this.queryRunner.updateOne(this.metadata.table.name, query, update, options);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// todo: extra these methods into separate class
|
||||
|
||||
protected get queryRunner(): MongoQueryRunner {
|
||||
return (this.connection.driver as MongoDriver).queryRunner;
|
||||
}
|
||||
|
||||
protected convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions: FindOneOptions<Entity>|Partial<Entity>|undefined): ObjectLiteral|undefined {
|
||||
if (!optionsOrConditions)
|
||||
return undefined;
|
||||
|
||||
return FindOptionsUtils.isFindManyOptions(optionsOrConditions) ? optionsOrConditions.where : optionsOrConditions;
|
||||
}
|
||||
|
||||
protected convertFindOneOptionsOrConditionsToMongodbQuery(optionsOrConditions: FindOneOptions<Entity>|Partial<Entity>|undefined): ObjectLiteral|undefined {
|
||||
if (!optionsOrConditions)
|
||||
return undefined;
|
||||
|
||||
return FindOptionsUtils.isFindOneOptions(optionsOrConditions) ? optionsOrConditions.where : optionsOrConditions;
|
||||
}
|
||||
|
||||
protected convertFindOptionsOrderToOrderCriteria<P>(order: { [P in keyof Entity]?: "ASC"|"DESC" }) {
|
||||
const orderCriteria: ObjectLiteral = {};
|
||||
Object.keys(order).forEach(key => orderCriteria[key] = [key, order[key]!.toLowerCase()]);
|
||||
return orderCriteria;
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,12 +3,13 @@ import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {QueryBuilder} from "../query-builder/QueryBuilder";
|
||||
import {PlainObjectToNewEntityTransformer} from "../query-builder/transformer/PlainObjectToNewEntityTransformer";
|
||||
import {PlainObjectToDatabaseEntityTransformer} from "../query-builder/transformer/PlainObjectToDatabaseEntityTransformer";
|
||||
import {FindOptions} from "../find-options/FindOptions";
|
||||
import {FindManyOptions} from "../find-options/FindManyOptions";
|
||||
import {FindOptionsUtils} from "../find-options/FindOptionsUtils";
|
||||
import {ObjectLiteral} from "../common/ObjectLiteral";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {SubjectOperationExecutor} from "../persistence/SubjectOperationExecutor";
|
||||
import {SubjectBuilder} from "../persistence/SubjectBuilder";
|
||||
import {FindOneOptions} from "../find-options/FindOneOptions";
|
||||
|
||||
/**
|
||||
* Repository is supposed to work with your entity objects. Find entities, insert, update, delete, etc.
|
||||
@ -134,6 +135,8 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
|
||||
/**
|
||||
* Persists one or many given entities.
|
||||
*
|
||||
* todo: use Partial<Entity> instead, and make sure it works properly
|
||||
*/
|
||||
async persist(entityOrEntities: Entity|Entity[]): Promise<Entity|Entity[]> {
|
||||
|
||||
@ -197,119 +200,114 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all entities.
|
||||
* Finds entities that match given options.
|
||||
*/
|
||||
async find(): Promise<Entity[]>;
|
||||
find(options?: FindManyOptions<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
*/
|
||||
async find(conditions: ObjectLiteral): Promise<Entity[]>;
|
||||
find(conditions?: Partial<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities with given find options.
|
||||
* Finds entities that match given find options or conditions.
|
||||
*/
|
||||
async find(options: FindOptions): Promise<Entity[]>;
|
||||
find(optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
const qb = this.createQueryBuilder(FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || this.metadata.table.name);
|
||||
return FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions).getMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions and find options.
|
||||
* Finds entities that match given find options.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
async find(conditions: ObjectLiteral, options: FindOptions): Promise<Entity[]>;
|
||||
findAndCount(options?: FindManyOptions<Entity>): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions and/or find options.
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
async find(conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<Entity[]> {
|
||||
return this.createFindQueryBuilder(conditionsOrFindOptions, options)
|
||||
findAndCount(conditions?: Partial<Entity>): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given find options or conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (from and take options).
|
||||
*/
|
||||
findAndCount(optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<[ Entity[], number ]> {
|
||||
const qb = this.createQueryBuilder(FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || this.metadata.table.name);
|
||||
return FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions).getManyAndCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities by ids.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
findByIds(ids: any[], options?: FindManyOptions<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities by ids.
|
||||
* Optionally conditions can be applied.
|
||||
*/
|
||||
findByIds(ids: any[], conditions?: Partial<Entity>): Promise<Entity[]>;
|
||||
|
||||
/**
|
||||
* Finds entities by ids.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
findByIds(ids: any[], optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
|
||||
const qb = this.createQueryBuilder(FindOptionsUtils.extractFindManyOptionsAlias(optionsOrConditions) || this.metadata.table.name);
|
||||
return FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions)
|
||||
.andWhereInIds(ids)
|
||||
.getMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* Finds first entity that matches given options.
|
||||
*/
|
||||
async findAndCount(): Promise<[ Entity[], number ]>;
|
||||
findOne(options?: FindOneOptions<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
async findAndCount(conditions: ObjectLiteral): Promise<[ Entity[], number ]>;
|
||||
findOne(conditions?: Partial<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
* Finds first entity that matches given conditions.
|
||||
*/
|
||||
async findAndCount(options: FindOptions): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
*/
|
||||
async findAndCount(conditions: ObjectLiteral, options: FindOptions): Promise<[ Entity[], number ]>;
|
||||
|
||||
/**
|
||||
* Finds entities that match given conditions.
|
||||
* Also counts all entities that match given conditions,
|
||||
* but ignores pagination settings (maxResults, firstResult) options.
|
||||
*/
|
||||
async findAndCount(conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<[ Entity[], number ]> {
|
||||
return this.createFindQueryBuilder(conditionsOrFindOptions, options)
|
||||
.getManyAndCount();
|
||||
findOne(optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
const qb = this.createQueryBuilder(FindOptionsUtils.extractFindOneOptionsAlias(optionsOrConditions) || this.metadata.table.name);
|
||||
return FindOptionsUtils.applyFindOneOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions).getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
* Finds entity by given id.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
async findOne(): Promise<Entity|undefined>;
|
||||
findOneById(id: any, options?: FindOneOptions<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions.
|
||||
* Finds entity by given id.
|
||||
* Optionally conditions can be applied.
|
||||
*/
|
||||
async findOne(conditions: ObjectLiteral): Promise<Entity|undefined>;
|
||||
findOneById(id: any, conditions?: Partial<Entity>): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given find options.
|
||||
* Finds entity by given id.
|
||||
* Optionally find options or conditions can be applied.
|
||||
*/
|
||||
async findOne(options: FindOptions): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions and find options.
|
||||
*/
|
||||
async findOne(conditions: ObjectLiteral, options: FindOptions): Promise<Entity|undefined>;
|
||||
|
||||
/**
|
||||
* Finds first entity that matches given conditions and/or find options.
|
||||
*/
|
||||
async findOne(conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): Promise<Entity|undefined> {
|
||||
return this.createFindQueryBuilder(conditionsOrFindOptions, options)
|
||||
findOneById(id: any, optionsOrConditions?: FindOneOptions<Entity>|Partial<Entity>): Promise<Entity|undefined> {
|
||||
const qb = this.createQueryBuilder(FindOptionsUtils.extractFindOneOptionsAlias(optionsOrConditions) || this.metadata.table.name);
|
||||
return FindOptionsUtils.applyFindOneOptionsOrConditionsToQueryBuilder(qb, optionsOrConditions)
|
||||
.andWhereInIds([id])
|
||||
.getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entities with ids.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
async findByIds(ids: any[], options?: FindOptions): Promise<Entity[]> {
|
||||
const qb = this.createFindQueryBuilder(undefined, options);
|
||||
return qb.andWhereInIds(ids).getMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds entity with given id.
|
||||
* Optionally find options can be applied.
|
||||
*/
|
||||
async findOneById(id: any, options?: FindOptions): Promise<Entity|undefined> {
|
||||
const qb = this.createFindQueryBuilder(undefined, options);
|
||||
return qb.andWhereInIds([id]).getOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a raw SQL query and returns a raw database results.
|
||||
* Raw query execution is supported only by relational databases (MongoDB is not supported).
|
||||
*/
|
||||
async query(query: string, parameters?: any[]): Promise<any> {
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
|
||||
@ -325,6 +323,13 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
/**
|
||||
* Wraps given function execution (and all operations made there) in a transaction.
|
||||
* All database operations must be executed using provided repository.
|
||||
*
|
||||
* Most important, you should execute all your database operations using provided repository instance,
|
||||
* all other operations would not be included in the transaction.
|
||||
* If you want to execute transaction and persist multiple different entity types, then
|
||||
* use EntityManager.transaction method instead.
|
||||
*
|
||||
* Transactions are supported only by relational databases (MongoDB is not supported).
|
||||
*/
|
||||
async transaction(runInTransaction: (repository: Repository<Entity>) => Promise<any>|any): Promise<any> {
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver, true);
|
||||
@ -356,7 +361,7 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the data from the given table (truncates/drops it).
|
||||
* Clears all the data from the given table/collection (truncates/drops it).
|
||||
*/
|
||||
async clear(): Promise<void> {
|
||||
const queryRunnerProvider = this.queryRunnerProvider || new QueryRunnerProvider(this.connection.driver);
|
||||
@ -369,40 +374,4 @@ export class Repository<Entity extends ObjectLiteral> {
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Protected Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a query builder from the given conditions or find options.
|
||||
* Used to create a query builder for find* methods.
|
||||
*/
|
||||
protected createFindQueryBuilder(conditionsOrFindOptions?: ObjectLiteral|FindOptions, options?: FindOptions): QueryBuilder<Entity> {
|
||||
const findOptions = FindOptionsUtils.isFindOptions(conditionsOrFindOptions) ? conditionsOrFindOptions : options as FindOptions;
|
||||
const conditions = FindOptionsUtils.isFindOptions(conditionsOrFindOptions) ? undefined : conditionsOrFindOptions;
|
||||
|
||||
const alias = findOptions ? findOptions.alias : this.metadata.table.name;
|
||||
const qb = this.createQueryBuilder(alias);
|
||||
|
||||
// if find options are given then apply them to query builder
|
||||
if (findOptions)
|
||||
FindOptionsUtils.applyOptionsToQueryBuilder(qb, findOptions);
|
||||
|
||||
// if conditions are given then apply them to query builder
|
||||
if (conditions) {
|
||||
Object.keys(conditions).forEach(key => {
|
||||
const name = key.indexOf(".") === -1 ? alias + "." + key : key;
|
||||
if (conditions![key] === null) {
|
||||
qb.andWhere(name + " IS NULL");
|
||||
|
||||
} else {
|
||||
qb.andWhere(name + "=:" + key);
|
||||
}
|
||||
});
|
||||
qb.setParameters(conditions);
|
||||
}
|
||||
|
||||
return qb;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,10 +2,10 @@ import {Repository} from "./Repository";
|
||||
import {EntityMetadata} from "../metadata/EntityMetadata";
|
||||
import {SpecificRepository} from "./SpecificRepository";
|
||||
import {Connection} from "../connection/Connection";
|
||||
import {getFromContainer} from "../index";
|
||||
import {RepositoryFactory} from "./RepositoryFactory";
|
||||
import {TreeRepository} from "./TreeRepository";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {RepositoryFactory} from "./RepositoryFactory";
|
||||
import {getFromContainer} from "../container";
|
||||
|
||||
/**
|
||||
* Aggregates all repositories of the specific metadata.
|
||||
@ -41,16 +41,17 @@ export class RepositoryAggregator {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
constructor(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider) {
|
||||
const repositoryFactory = getFromContainer(RepositoryFactory);
|
||||
this.metadata = metadata;
|
||||
|
||||
const factory = getFromContainer(RepositoryFactory);
|
||||
|
||||
if (metadata.table.isClosure) {
|
||||
this.repository = this.treeRepository = repositoryFactory.createTreeRepository(connection, metadata, queryRunnerProvider);
|
||||
this.repository = this.treeRepository = factory.createTreeRepository(connection, metadata, queryRunnerProvider);
|
||||
} else {
|
||||
this.repository = repositoryFactory.createRepository(connection, metadata, queryRunnerProvider);
|
||||
this.repository = factory.createRepository(connection, metadata, queryRunnerProvider);
|
||||
}
|
||||
|
||||
this.specificRepository = repositoryFactory.createSpecificRepository(connection, metadata, this.repository, queryRunnerProvider);
|
||||
this.specificRepository = factory.createSpecificRepository(connection, metadata, queryRunnerProvider);
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,6 +4,8 @@ import {Connection} from "../connection/Connection";
|
||||
import {Repository} from "./Repository";
|
||||
import {SpecificRepository} from "./SpecificRepository";
|
||||
import {QueryRunnerProvider} from "../query-runner/QueryRunnerProvider";
|
||||
import {MongoDriver} from "../driver/mongodb/MongoDriver";
|
||||
import {MongoRepository} from "./MongoRepository";
|
||||
|
||||
/**
|
||||
* Factory used to create different types of repositories.
|
||||
@ -21,7 +23,12 @@ export class RepositoryFactory {
|
||||
|
||||
// NOTE: dynamic access to protected properties. We need this to prevent unwanted properties in those classes to be exposed,
|
||||
// however we need these properties for internal work of the class
|
||||
const repository = new Repository<any>();
|
||||
let repository: Repository<any>;
|
||||
if (connection.driver instanceof MongoDriver) {
|
||||
repository = new MongoRepository();
|
||||
} else {
|
||||
repository = new Repository<any>();
|
||||
}
|
||||
(repository as any)["connection"] = connection;
|
||||
(repository as any)["metadata"] = metadata;
|
||||
(repository as any)["queryRunnerProvider"] = queryRunnerProvider;
|
||||
@ -45,7 +52,7 @@ export class RepositoryFactory {
|
||||
/**
|
||||
* Creates a specific repository.
|
||||
*/
|
||||
createSpecificRepository(connection: Connection, metadata: EntityMetadata, repository: Repository<any>, queryRunnerProvider?: QueryRunnerProvider): SpecificRepository<any> {
|
||||
createSpecificRepository(connection: Connection, metadata: EntityMetadata, queryRunnerProvider?: QueryRunnerProvider): SpecificRepository<any> {
|
||||
return new SpecificRepository(connection, metadata, queryRunnerProvider);
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import {PrimaryKeySchema} from "./PrimaryKeySchema";
|
||||
import {ColumnMetadata} from "../../metadata/ColumnMetadata";
|
||||
import {QueryRunner} from "../../query-runner/QueryRunner";
|
||||
import {ObjectLiteral} from "../../common/ObjectLiteral";
|
||||
import {EntityMetadata} from "../../metadata/EntityMetadata";
|
||||
|
||||
/**
|
||||
* Table schema in the database represented in this class.
|
||||
@ -212,4 +213,22 @@ export class TableSchema {
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Static Methods
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates table schema from a given entity metadata.
|
||||
*
|
||||
* todo: need deeper implementation
|
||||
*/
|
||||
static create(entityMetadata: EntityMetadata, queryRunner: QueryRunner) {
|
||||
const tableSchema = new TableSchema(entityMetadata.table.name);
|
||||
entityMetadata.columns.forEach(column => {
|
||||
tableSchema.columns.push(ColumnSchema.create(column, queryRunner.normalizeType(column)));
|
||||
});
|
||||
|
||||
return tableSchema;
|
||||
}
|
||||
|
||||
}
|
||||
@ -29,9 +29,11 @@ describe.skip("cascades > should insert by cascades from both sides (#57)", () =
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
details: "post.details"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
details: "post.details"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -51,7 +51,6 @@ describe("jsonb type", () => {
|
||||
record.data = [1, "2", { a: 3 }];
|
||||
let persistedRecord = await recordRepo.persist(record);
|
||||
let foundRecord = await recordRepo.findOneById(persistedRecord.id);
|
||||
console.log("array", foundRecord!.data);
|
||||
expect(foundRecord).to.be.not.undefined;
|
||||
expect(foundRecord!.data).to.deep.include.members([1, "2", { a: 3 }]);
|
||||
})));
|
||||
|
||||
@ -179,7 +179,7 @@ describe("lazy-relations", () => {
|
||||
await connection.entityManager.persist(category);
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "post with great category" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "post with great category" } });
|
||||
const loadedCategory = await loadedPost!.category;
|
||||
|
||||
loadedCategory.name.should.be.equal("category of great post");
|
||||
@ -216,7 +216,7 @@ describe("lazy-relations", () => {
|
||||
await connection.entityManager.persist(category);
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "post with great category" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "post with great category" } });
|
||||
const loadedCategory = await loadedPost!.twoSideCategory;
|
||||
|
||||
loadedCategory.name.should.be.equal("category of great post");
|
||||
|
||||
@ -130,12 +130,14 @@ describe("persistence > cascade operations", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.id": "ASC"
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
@ -180,12 +182,14 @@ describe("persistence > cascade operations", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.id": "ASC"
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -35,12 +35,14 @@ describe("persistence > cascade operations with custom name", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.id": "ASC"
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -69,7 +69,7 @@ describe("persistence > custom-column-names", function() {
|
||||
// load a post
|
||||
before(function() {
|
||||
return postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.category" } })
|
||||
.findOneById(1, { join: { alias: "post", leftJoinAndSelect: { category: "post.category" } }})
|
||||
.then(post => loadedPost = post!);
|
||||
});
|
||||
|
||||
@ -104,7 +104,7 @@ describe("persistence > custom-column-names", function() {
|
||||
// load a post
|
||||
before(function() {
|
||||
return postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.category" } })
|
||||
.findOneById(1, { join: { alias: "post", leftJoinAndSelect: { category: "post.category" } } })
|
||||
.then(post => loadedPost = post!);
|
||||
});
|
||||
|
||||
@ -134,7 +134,7 @@ describe("persistence > custom-column-names", function() {
|
||||
// load a post
|
||||
before(function() {
|
||||
return postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.category" } })
|
||||
.findOneById(1, { join: { alias: "post", leftJoinAndSelect: { category: "post.category" } }})
|
||||
.then(post => loadedPost = post!);
|
||||
});
|
||||
|
||||
@ -182,7 +182,7 @@ describe("persistence > custom-column-names", function() {
|
||||
// load a post
|
||||
before(function() {
|
||||
return postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.category", metadata: "category.metadata" } })
|
||||
.findOneById(1, { join: { alias: "post", leftJoinAndSelect: { category: "post.category", metadata: "category.metadata" } } })
|
||||
.then(post => loadedPost = post!);
|
||||
});
|
||||
|
||||
@ -227,7 +227,7 @@ describe("persistence > custom-column-names", function() {
|
||||
// load a post
|
||||
before(function() {
|
||||
return postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { category: "post.category", metadata: "category.metadata" } })
|
||||
.findOneById(1, { join: { alias: "post", leftJoinAndSelect: { category: "post.category", metadata: "category.metadata" } } })
|
||||
.then(post => loadedPost = post!);
|
||||
});
|
||||
|
||||
|
||||
@ -53,8 +53,10 @@ describe("persistence > many-to-many", function() {
|
||||
|
||||
// load a post
|
||||
const loadedUser = await userRepository.findOneById(1, {
|
||||
alias: "user",
|
||||
leftJoinAndSelect: { post: "user.post", categories: "post.categories" }
|
||||
join: {
|
||||
alias: "user",
|
||||
leftJoinAndSelect: { post: "user.post", categories: "post.categories" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedUser!).not.to.be.empty;
|
||||
|
||||
@ -35,12 +35,14 @@ describe("persistence > multi primary keys", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.firstId": "ASC"
|
||||
order: {
|
||||
firstId: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -41,12 +41,14 @@ describe("persistence > multi primary keys", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.firstId": "ASC"
|
||||
order: {
|
||||
firstId: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import {expect} from "chai";
|
||||
import {Connection} from "../../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {Category} from "./entity/Category";
|
||||
import {FindOptions} from "../../../../src/find-options/FindOptions";
|
||||
import {FindManyOptions} from "../../../../src/find-options/FindManyOptions";
|
||||
import {closeTestingConnections, reloadTestingDatabases, createTestingConnections} from "../../../utils/test-utils";
|
||||
|
||||
describe("persistence > one-to-many", function() {
|
||||
@ -44,8 +44,14 @@ describe("persistence > one-to-many", function() {
|
||||
newPost.categories = [newCategory];
|
||||
await postRepository.persist(newPost);
|
||||
|
||||
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
|
||||
const loadedPost = (await postRepository.findOneById(1, findOptions))!;
|
||||
const loadedPost = (await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
}))!;
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost.categories).not.to.be.empty;
|
||||
expect(loadedPost.categories![0]).not.to.be.empty;
|
||||
@ -68,8 +74,14 @@ describe("persistence > one-to-many", function() {
|
||||
newPost.categories = [newCategory];
|
||||
await postRepository.persist(newPost);
|
||||
|
||||
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
|
||||
const loadedPost = await postRepository.findOneById(1, findOptions);
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.categories).not.to.be.empty;
|
||||
expect(loadedPost!.categories![0]).not.to.be.empty;
|
||||
@ -101,8 +113,14 @@ describe("persistence > one-to-many", function() {
|
||||
newPost.categories = [firstNewCategory];
|
||||
await postRepository.persist(newPost);
|
||||
|
||||
const findOptions: FindOptions = { alias: "post", innerJoinAndSelect: { categories: "post.categories" } };
|
||||
const loadedPost = await postRepository.findOneById(1, findOptions);
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.categories).not.to.be.empty;
|
||||
expect(loadedPost!.categories![0]).not.to.be.empty;
|
||||
@ -135,8 +153,14 @@ describe("persistence > one-to-many", function() {
|
||||
newPost.categories = [];
|
||||
await postRepository.persist(newPost);
|
||||
|
||||
const findOptions: FindOptions = { alias: "post", leftJoinAndSelect: { categories: "post.categories" } };
|
||||
const loadedPost = await postRepository.findOneById(1, findOptions);
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost!.categories).to.be.empty;
|
||||
})));
|
||||
@ -167,8 +191,14 @@ describe("persistence > one-to-many", function() {
|
||||
newPost.categories = null; // todo: what to do with undefined?
|
||||
await postRepository.persist(newPost);
|
||||
|
||||
const findOptions: FindOptions = { alias: "post", leftJoinAndSelect: { categories: "post.categories" } };
|
||||
const loadedPost = (await postRepository.findOneById(1, findOptions))!;
|
||||
const loadedPost = (await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
categories: "post.categories"
|
||||
}
|
||||
}
|
||||
}))!;
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
expect(loadedPost.categories).to.be.empty;
|
||||
})));
|
||||
|
||||
@ -30,9 +30,11 @@ describe.skip("relations > relation mapped to relation with different name (#56)
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
details: "post.details"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
details: "post.details"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -41,12 +41,14 @@ describe.skip("relations > relation with primary key", () => {
|
||||
|
||||
// now check
|
||||
const posts = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
"post.category": "ASC"
|
||||
order: {
|
||||
category: "ASC"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import {expect} from "chai";
|
||||
import {createTestingConnections, closeTestingConnections, reloadTestingDatabases} from "../../../utils/test-utils";
|
||||
import {Connection} from "../../../../src/connection/Connection";
|
||||
import {Post} from "./entity/Post";
|
||||
import {FindOptions} from "../../../../src/find-options/FindOptions";
|
||||
import {FindManyOptions} from "../../../../src/find-options/FindManyOptions";
|
||||
import {User} from "./model/User";
|
||||
|
||||
describe("repository > find methods", () => {
|
||||
@ -44,7 +44,7 @@ describe("repository > find methods", () => {
|
||||
savedPosts.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.find({ alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
const loadedPosts = await postRepository.find({ order: { id: "ASC" }});
|
||||
loadedPosts.should.be.instanceOf(Array);
|
||||
loadedPosts.length.should.be.equal(100);
|
||||
loadedPosts[0].id.should.be.equal(0);
|
||||
@ -53,7 +53,7 @@ describe("repository > find methods", () => {
|
||||
loadedPosts[99].title.should.be.equal("post #99");
|
||||
|
||||
// check findAndCount method
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({ alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({ order: { id: "ASC" }});
|
||||
count.should.be.equal(100);
|
||||
loadedPosts2.should.be.instanceOf(Array);
|
||||
loadedPosts2.length.should.be.equal(100);
|
||||
@ -78,7 +78,10 @@ describe("repository > find methods", () => {
|
||||
savedPosts.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.find({ categoryName: "odd" }, { alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
const loadedPosts = await postRepository.find({
|
||||
where: { categoryName: "odd" },
|
||||
order: { id: "ASC" }
|
||||
});
|
||||
loadedPosts.should.be.instanceOf(Array);
|
||||
loadedPosts.length.should.be.equal(50);
|
||||
loadedPosts[0].id.should.be.equal(1);
|
||||
@ -87,7 +90,10 @@ describe("repository > find methods", () => {
|
||||
loadedPosts[49].title.should.be.equal("post #99");
|
||||
|
||||
// check findAndCount method
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "odd" }, { alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({
|
||||
where: { categoryName: "odd" },
|
||||
order: { id: "ASC" }
|
||||
});
|
||||
count.should.be.equal(50);
|
||||
loadedPosts2.should.be.instanceOf(Array);
|
||||
loadedPosts2.length.should.be.equal(50);
|
||||
@ -113,7 +119,10 @@ describe("repository > find methods", () => {
|
||||
savedPosts.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.find({ categoryName: "odd", isNew: true }, { alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
const loadedPosts = await postRepository.find({
|
||||
where: { categoryName: "odd", isNew: true },
|
||||
order: { id: "ASC" }
|
||||
});
|
||||
loadedPosts.should.be.instanceOf(Array);
|
||||
loadedPosts.length.should.be.equal(5);
|
||||
loadedPosts[0].id.should.be.equal(91);
|
||||
@ -122,7 +131,10 @@ describe("repository > find methods", () => {
|
||||
loadedPosts[4].title.should.be.equal("post #99");
|
||||
|
||||
// check findAndCount method
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "odd", isNew: true }, { alias: "post", orderBy: { "post.id": "ASC" }});
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({
|
||||
where: { categoryName: "odd", isNew: true },
|
||||
order: { id: "ASC" }
|
||||
});
|
||||
count.should.be.equal(5);
|
||||
loadedPosts2.should.be.instanceOf(Array);
|
||||
loadedPosts2.length.should.be.equal(5);
|
||||
@ -147,20 +159,15 @@ describe("repository > find methods", () => {
|
||||
const savedPosts = await Promise.all(promises);
|
||||
savedPosts.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
const findOptions: FindOptions = {
|
||||
alias: "post",
|
||||
where: "post.title LIKE :likeTitle AND post.categoryName = :categoryName",
|
||||
parameters: {
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.createQueryBuilder("post")
|
||||
.where("post.title LIKE :likeTitle AND post.categoryName = :categoryName")
|
||||
.setParameters({
|
||||
likeTitle: "new post #%",
|
||||
categoryName: "even"
|
||||
},
|
||||
orderBy: {
|
||||
"post.id": "ASC"
|
||||
}
|
||||
};
|
||||
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.find(findOptions);
|
||||
})
|
||||
.orderBy("post.id", "ASC")
|
||||
.getMany();
|
||||
loadedPosts.should.be.instanceOf(Array);
|
||||
loadedPosts.length.should.be.equal(5);
|
||||
loadedPosts[0].id.should.be.equal(92);
|
||||
@ -169,7 +176,14 @@ describe("repository > find methods", () => {
|
||||
loadedPosts[4].title.should.be.equal("new post #100");
|
||||
|
||||
// check findAndCount method
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount(findOptions);
|
||||
const [loadedPosts2, count] = await postRepository.createQueryBuilder("post")
|
||||
.where("post.title LIKE :likeTitle AND post.categoryName = :categoryName")
|
||||
.setParameters({
|
||||
likeTitle: "new post #%",
|
||||
categoryName: "even"
|
||||
})
|
||||
.orderBy("post.id", "ASC")
|
||||
.getManyAndCount();
|
||||
count.should.be.equal(5);
|
||||
loadedPosts2.should.be.instanceOf(Array);
|
||||
loadedPosts2.length.should.be.equal(5);
|
||||
@ -194,17 +208,18 @@ describe("repository > find methods", () => {
|
||||
const savedPosts = await Promise.all(promises);
|
||||
savedPosts.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
const findOptions: FindOptions = {
|
||||
alias: "post",
|
||||
firstResult: 1,
|
||||
maxResults: 2,
|
||||
orderBy: {
|
||||
"post.id": "ASC"
|
||||
}
|
||||
};
|
||||
|
||||
// check find method
|
||||
const loadedPosts = await postRepository.find({ categoryName: "even", isNew: true }, findOptions);
|
||||
const loadedPosts = await postRepository.find({
|
||||
where: {
|
||||
categoryName: "even",
|
||||
isNew: true
|
||||
},
|
||||
from: 1,
|
||||
take: 2,
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
});
|
||||
loadedPosts.should.be.instanceOf(Array);
|
||||
loadedPosts.length.should.be.equal(2);
|
||||
loadedPosts[0].id.should.be.equal(94);
|
||||
@ -213,7 +228,17 @@ describe("repository > find methods", () => {
|
||||
loadedPosts[1].title.should.be.equal("new post #96");
|
||||
|
||||
// check findAndCount method
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({ categoryName: "even", isNew: true }, findOptions);
|
||||
let [loadedPosts2, count] = await postRepository.findAndCount({
|
||||
where: {
|
||||
categoryName: "even",
|
||||
isNew: true
|
||||
},
|
||||
from: 1,
|
||||
take: 2,
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
});
|
||||
count.should.be.equal(5);
|
||||
loadedPosts2.should.be.instanceOf(Array);
|
||||
loadedPosts2.length.should.be.equal(2);
|
||||
@ -242,7 +267,7 @@ describe("repository > find methods", () => {
|
||||
const savedUsers = await Promise.all(promises);
|
||||
savedUsers.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
const loadedUser = (await userRepository.findOne({ alias: "user", orderBy: { "user.id": "ASC" }}))!;
|
||||
const loadedUser = (await userRepository.findOne({ order: { id: "ASC" }}))!;
|
||||
loadedUser.id.should.be.equal(0);
|
||||
loadedUser.firstName.should.be.equal("name #0");
|
||||
loadedUser.secondName.should.be.equal("Doe");
|
||||
@ -263,7 +288,7 @@ describe("repository > find methods", () => {
|
||||
const savedUsers = await Promise.all(promises);
|
||||
savedUsers.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
const loadedUser = (await userRepository.findOne({ firstName: "name #1" }, { alias: "user", orderBy: { "user.id": "ASC" }}))!;
|
||||
const loadedUser = (await userRepository.findOne({ where: { firstName: "name #1" }, order: { id: "ASC" } }))!;
|
||||
loadedUser.id.should.be.equal(1);
|
||||
loadedUser.firstName.should.be.equal("name #1");
|
||||
loadedUser.secondName.should.be.equal("Doe");
|
||||
@ -284,18 +309,18 @@ describe("repository > find methods", () => {
|
||||
const savedUsers = await Promise.all(promises);
|
||||
savedUsers.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
const findOptions: FindOptions = {
|
||||
alias: "user",
|
||||
where: "user.firstName=:firstName AND user.secondName =:secondName",
|
||||
parameters: {
|
||||
const loadedUser = await userRepository.findOne({
|
||||
where: {
|
||||
firstName: "name #99",
|
||||
secondName: "Doe"
|
||||
},
|
||||
order: {
|
||||
id: "ASC"
|
||||
}
|
||||
};
|
||||
const loadedUser = (await userRepository.findOne(findOptions, { alias: "user", orderBy: { "user.id": "ASC" }}))!;
|
||||
loadedUser.id.should.be.equal(99);
|
||||
loadedUser.firstName.should.be.equal("name #99");
|
||||
loadedUser.secondName.should.be.equal("Doe");
|
||||
});
|
||||
loadedUser!.id.should.be.equal(99);
|
||||
loadedUser!.firstName.should.be.equal("name #99");
|
||||
loadedUser!.secondName.should.be.equal("Doe");
|
||||
})));
|
||||
|
||||
});
|
||||
@ -345,29 +370,23 @@ describe("repository > find methods", () => {
|
||||
promises.push(userRepository.persist(user));
|
||||
}
|
||||
|
||||
const findOptions1: FindOptions = {
|
||||
alias: "user",
|
||||
whereConditions: {
|
||||
secondName: "Doe"
|
||||
}
|
||||
};
|
||||
|
||||
const findOptions2: FindOptions = {
|
||||
alias: "user",
|
||||
whereConditions: {
|
||||
secondName: "Dorian"
|
||||
}
|
||||
};
|
||||
|
||||
const savedUsers = await Promise.all(promises);
|
||||
savedUsers.length.should.be.equal(100); // check if they all are saved
|
||||
|
||||
let loadedUser = await userRepository.findOneById(0, findOptions1);
|
||||
let loadedUser = await userRepository.findOneById(0, {
|
||||
where: {
|
||||
secondName: "Doe"
|
||||
}
|
||||
});
|
||||
loadedUser!.id.should.be.equal(0);
|
||||
loadedUser!.firstName.should.be.equal("name #0");
|
||||
loadedUser!.secondName.should.be.equal("Doe");
|
||||
|
||||
loadedUser = await userRepository.findOneById(1, findOptions2);
|
||||
loadedUser = await userRepository.findOneById(1, {
|
||||
where: {
|
||||
secondName: "Dorian"
|
||||
}
|
||||
});
|
||||
expect(loadedUser).to.be.undefined;
|
||||
})));
|
||||
|
||||
|
||||
@ -49,8 +49,14 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
await postSpecificRepository.addToRelation(post => post.manyCategories, newPost.id, [newCategory1.id, newCategory2.id]);
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedPost = await postRepository
|
||||
.findOneById(1, { alias: "post", leftJoinAndSelect: { manyCategories: "post.manyCategories" } });
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: {
|
||||
manyCategories: "post.manyCategories"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPost!).not.to.be.empty;
|
||||
expect(loadedPost!.manyCategories).not.to.be.empty;
|
||||
@ -85,8 +91,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedCategory = await categoryRepository.findOneById(1, {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { manyPosts: "category.manyPosts" } }
|
||||
join: {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { manyPosts: "category.manyPosts" } }
|
||||
}
|
||||
);
|
||||
|
||||
expect(loadedCategory).not.to.be.empty;
|
||||
@ -127,8 +135,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { manyCategories: "post.manyCategories" }
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { manyCategories: "post.manyCategories" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPost!).not.to.be.empty;
|
||||
@ -170,8 +180,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedCategory = await categoryRepository.findOneById(1, {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { manyPosts: "category.manyPosts" }
|
||||
join: {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { manyPosts: "category.manyPosts" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedCategory!).not.to.be.empty;
|
||||
@ -202,8 +214,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { categories: "post.categories" }
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { categories: "post.categories" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPost!).not.to.be.empty;
|
||||
@ -233,8 +247,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedCategory = await categoryRepository.findOneById(1, {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { post: "category.post" }
|
||||
join: {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { post: "category.post" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedCategory!).not.to.be.empty;
|
||||
@ -263,8 +279,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedPost = await postRepository.findOneById(1, {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { categories: "post.categories" }
|
||||
join: {
|
||||
alias: "post",
|
||||
leftJoinAndSelect: { categories: "post.categories" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPost!).not.to.be.empty;
|
||||
@ -293,8 +311,10 @@ describe("repository > set/add/remove relation methods", function() {
|
||||
|
||||
// load a post, want to have categories count
|
||||
const loadedCategory = await categoryRepository.findOneById(1, {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { post: "category.post" }
|
||||
join: {
|
||||
alias: "category",
|
||||
leftJoinAndSelect: { post: "category.post" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedCategory).not.to.be.empty;
|
||||
|
||||
@ -33,11 +33,11 @@ describe("transaction > method wrapped into transaction decorator", () => {
|
||||
await controller.save.apply(controller, [post, category]);
|
||||
|
||||
// controller should have saved both post and category successfully
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "successfully saved post" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "successfully saved post" } });
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
loadedPost!.should.be.eql(post);
|
||||
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { name: "successfully saved category" });
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { where: { name: "successfully saved category" } });
|
||||
expect(loadedCategory).not.to.be.empty;
|
||||
loadedCategory!.should.be.eql(category);
|
||||
|
||||
@ -59,10 +59,10 @@ describe("transaction > method wrapped into transaction decorator", () => {
|
||||
}
|
||||
expect(throwError).not.to.be.empty;
|
||||
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "successfully saved post" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "successfully saved post" }});
|
||||
expect(loadedPost).to.be.empty;
|
||||
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { name: "successfully saved category" });
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { where: { name: "successfully saved category" }});
|
||||
expect(loadedCategory).to.be.empty;
|
||||
|
||||
})));
|
||||
@ -83,10 +83,10 @@ describe("transaction > method wrapped into transaction decorator", () => {
|
||||
}
|
||||
expect(throwError).not.to.be.empty;
|
||||
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "successfully saved post" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "successfully saved post" }});
|
||||
expect(loadedPost).to.be.empty;
|
||||
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { name: "successfully saved category" });
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { where: { name: "successfully saved category" }});
|
||||
expect(loadedCategory).to.be.empty;
|
||||
|
||||
})));
|
||||
@ -109,11 +109,11 @@ describe("transaction > method wrapped into transaction decorator", () => {
|
||||
expect(throwError).not.to.be.empty;
|
||||
|
||||
// controller should have saved both post and category successfully
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { title: "successfully saved post" });
|
||||
const loadedPost = await connection.entityManager.findOne(Post, { where: { title: "successfully saved post" }});
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
loadedPost!.should.be.eql(post);
|
||||
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { name: "successfully saved category" });
|
||||
const loadedCategory = await connection.entityManager.findOne(Category, { where: { name: "successfully saved category" }});
|
||||
expect(loadedCategory).to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
@ -36,14 +36,14 @@ describe("transaction > transaction with entity manager", () => {
|
||||
|
||||
});
|
||||
|
||||
const post = await connection.entityManager.findOne(Post, { title: "Post #1" });
|
||||
const post = await connection.entityManager.findOne(Post, { where: { title: "Post #1" }});
|
||||
expect(post).not.to.be.empty;
|
||||
post!.should.be.eql({
|
||||
id: postId,
|
||||
title: "Post #1"
|
||||
});
|
||||
|
||||
const category = await connection.entityManager.findOne(Category, { name: "Category #1" });
|
||||
const category = await connection.entityManager.findOne(Category, { where: { name: "Category #1" }});
|
||||
expect(category).not.to.be.empty;
|
||||
category!.should.be.eql({
|
||||
id: categoryId,
|
||||
@ -70,14 +70,14 @@ describe("transaction > transaction with entity manager", () => {
|
||||
postId = post.id;
|
||||
categoryId = category.id;
|
||||
|
||||
const loadedPost = await entityManager.findOne(Post, { title: "Post #1" });
|
||||
const loadedPost = await entityManager.findOne(Post, { where: { title: "Post #1" }});
|
||||
expect(loadedPost).not.to.be.empty;
|
||||
loadedPost!.should.be.eql({
|
||||
id: postId,
|
||||
title: "Post #1"
|
||||
});
|
||||
|
||||
const loadedCategory = await entityManager.findOne(Category, { name: "Category #1" });
|
||||
const loadedCategory = await entityManager.findOne(Category, { where: { name: "Category #1" }});
|
||||
expect(loadedCategory).not.to.be.empty;
|
||||
loadedCategory!.should.be.eql({
|
||||
id: categoryId,
|
||||
@ -93,10 +93,10 @@ describe("transaction > transaction with entity manager", () => {
|
||||
/* skip error */
|
||||
}
|
||||
|
||||
const post = await connection.entityManager.findOne(Post, { title: "Post #1" });
|
||||
const post = await connection.entityManager.findOne(Post, { where: { title: "Post #1" }});
|
||||
expect(post).to.be.empty;
|
||||
|
||||
const category = await connection.entityManager.findOne(Category, { name: "Category #1" });
|
||||
const category = await connection.entityManager.findOne(Category, { where: { name: "Category #1" }});
|
||||
expect(category).to.be.empty;
|
||||
|
||||
})));
|
||||
|
||||
@ -29,9 +29,11 @@ describe("github issues > #151 joinAndSelect can't find entity from inverse side
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPost = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -63,17 +65,17 @@ describe("github issues > #151 joinAndSelect can't find entity from inverse side
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPostWithCategory = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
category: "post.category"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedPostWithCategory).to.be.empty;
|
||||
|
||||
const loadedPostWithoutCategory = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post"
|
||||
});
|
||||
const loadedPostWithoutCategory = await connection.entityManager.findOneById(Post, 1);
|
||||
|
||||
expect(loadedPostWithoutCategory).not.to.be.empty;
|
||||
loadedPostWithoutCategory!.should.be.eql({
|
||||
@ -102,16 +104,16 @@ describe("github issues > #151 joinAndSelect can't find entity from inverse side
|
||||
await connection.entityManager.persist(post);
|
||||
|
||||
const loadedPostWithMetadata = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
metadata: "post.metadata"
|
||||
join: {
|
||||
alias: "post",
|
||||
innerJoinAndSelect: {
|
||||
metadata: "post.metadata"
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(loadedPostWithMetadata).to.be.empty;
|
||||
|
||||
const loadedPostWithoutMetadata = await connection.entityManager.findOneById(Post, 1, {
|
||||
alias: "post"
|
||||
});
|
||||
const loadedPostWithoutMetadata = await connection.entityManager.findOneById(Post, 1);
|
||||
expect(loadedPostWithoutMetadata).not.to.be.empty;
|
||||
loadedPostWithoutMetadata!.should.be.eql({
|
||||
id: 1,
|
||||
|
||||
@ -30,9 +30,11 @@ describe("github issues > #161 joinAndSelect can't find entity from inverse side
|
||||
await connection.entityManager.persist(ticket);
|
||||
|
||||
const loadedTicketWithRequest = await connection.entityManager.findOneById(Ticket, 1, {
|
||||
alias: "ticket",
|
||||
innerJoinAndSelect: {
|
||||
"request": "ticket.request"
|
||||
join: {
|
||||
alias: "ticket",
|
||||
innerJoinAndSelect: {
|
||||
"request": "ticket.request"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -49,9 +51,11 @@ describe("github issues > #161 joinAndSelect can't find entity from inverse side
|
||||
});
|
||||
|
||||
const loadedRequestWithTicket = await connection.entityManager.findOneById(Request, 1, {
|
||||
alias: "request",
|
||||
innerJoinAndSelect: {
|
||||
"ticket": "request.ticket"
|
||||
join: {
|
||||
alias: "request",
|
||||
innerJoinAndSelect: {
|
||||
"ticket": "request.ticket"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -91,8 +95,10 @@ describe("github issues > #161 joinAndSelect can't find entity from inverse side
|
||||
await connection.entityManager.persist(request);
|
||||
|
||||
const loadedRequest = await connection.entityManager.findOneById(Request, 2, {
|
||||
alias: "request",
|
||||
innerJoinAndSelect: { ticket: "request.ticket" }
|
||||
join: {
|
||||
alias: "request",
|
||||
innerJoinAndSelect: { ticket: "request.ticket" }
|
||||
}
|
||||
});
|
||||
|
||||
expect(loadedRequest).not.to.be.undefined;
|
||||
|
||||
@ -37,7 +37,7 @@ describe("github issues > #163 ManyToMany relation : Cannot read property 'joinC
|
||||
|
||||
const loadedPlatform = await connection
|
||||
.getRepository(Platform)
|
||||
.findOne({ slug: "windows" });
|
||||
.findOne({ where: { slug: "windows" } });
|
||||
|
||||
let jediAcademy = new Game();
|
||||
jediAcademy.name = "SW Jedi Academy";
|
||||
|
||||
@ -25,7 +25,7 @@ describe("github issues > #176 @CreateDateColumn and @UpdateDateColumn does not
|
||||
// persist
|
||||
await connection.entityManager.persist(post1);
|
||||
|
||||
const loadedPosts1 = await connection.entityManager.findOne(Post, { title: "Hello Post #1" });
|
||||
const loadedPosts1 = await connection.entityManager.findOne(Post, { where: { title: "Hello Post #1" } });
|
||||
expect(loadedPosts1!).not.to.be.empty;
|
||||
|
||||
loadedPosts1!.date.toISOString().should.be.equal("2017-01-10T17:38:06.000Z");
|
||||
|
||||
@ -26,7 +26,7 @@ describe("github issues > #182 enums are not saved properly", () => {
|
||||
// persist
|
||||
await connection.entityManager.persist(post1);
|
||||
|
||||
const loadedPosts1 = await connection.entityManager.findOne(Post, { title: "Hello Post #1" });
|
||||
const loadedPosts1 = await connection.entityManager.findOne(Post, { where: { title: "Hello Post #1" } });
|
||||
expect(loadedPosts1!).not.to.be.empty;
|
||||
loadedPosts1!.should.be.eql({
|
||||
id: 1,
|
||||
@ -44,7 +44,7 @@ describe("github issues > #182 enums are not saved properly", () => {
|
||||
// persist
|
||||
await connection.entityManager.persist(post2);
|
||||
|
||||
const loadedPosts2 = await connection.entityManager.findOne(Post, { title: "Hello Post #1" });
|
||||
const loadedPosts2 = await connection.entityManager.findOne(Post, { where: { title: "Hello Post #1" } });
|
||||
expect(loadedPosts2!).not.to.be.empty;
|
||||
loadedPosts2!.should.be.eql({
|
||||
id: 2,
|
||||
@ -62,7 +62,7 @@ describe("github issues > #182 enums are not saved properly", () => {
|
||||
// persist
|
||||
await connection.entityManager.persist(post3);
|
||||
|
||||
const loadedPosts3 = await connection.entityManager.findOne(Post, { title: "Hello Post #1" });
|
||||
const loadedPosts3 = await connection.entityManager.findOne(Post, { where: { title: "Hello Post #1" } });
|
||||
expect(loadedPosts3!).not.to.be.empty;
|
||||
loadedPosts3!.should.be.eql({
|
||||
id: 3,
|
||||
|
||||
@ -25,25 +25,12 @@ describe("github issues > #219 FindOptions should be able to resolve null values
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
const postsWithoutText1 = await connection.entityManager.find(Post, { text: null });
|
||||
const postsWithoutText1 = await connection.entityManager.find(Post, { where: { text: null } });
|
||||
postsWithoutText1.length.should.be.equal(5);
|
||||
|
||||
const postsWithText1 = await connection.entityManager.find(Post, { text: "about post" });
|
||||
const postsWithText1 = await connection.entityManager.find(Post, { where: { text: "about post" } });
|
||||
postsWithText1.length.should.be.equal(5);
|
||||
|
||||
// also should work if queried via complex FindOptions object
|
||||
const postsWithoutText2 = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
whereConditions: { "post.text": null }
|
||||
});
|
||||
postsWithoutText2.length.should.be.equal(5);
|
||||
|
||||
const postsWithText2 = await connection.entityManager.find(Post, {
|
||||
alias: "post",
|
||||
whereConditions: { "post.text": "about post" }
|
||||
});
|
||||
postsWithText2.length.should.be.equal(5);
|
||||
|
||||
})));
|
||||
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user