Skip to content

Commit

Permalink
added basic mongodb support, mongodb repository, entity manager and a…
Browse files Browse the repository at this point in the history
…ll major collection operations
  • Loading branch information
Umed Khudoiberdiev committed Feb 11, 2017
1 parent 64d38e3 commit 5fcc3ae
Show file tree
Hide file tree
Showing 72 changed files with 3,037 additions and 658 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ coverage/
node_modules/
npm-debug.log
ormconfig.json
.vscode
.vscode
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
17 changes: 12 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
# mysql
mysql:
image: "mysql:5.7.10"
container_name: "mysql"
container_name: "typeorm-mysql"
ports:
- "3306:3306"
environment:
Expand All @@ -16,7 +16,7 @@ services:
# mariadb
mariadb:
image: "mariadb:10.1.16"
container_name: "mariadb"
container_name: "typeorm-mariadb"
ports:
- "3307:3306"
environment:
Expand All @@ -28,7 +28,7 @@ services:
# postgres
postgres:
image: "postgres:9.6.1"
container_name: "postgres"
container_name: "typeorm-postgres"
ports:
- "5432:5432"
environment:
Expand All @@ -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"
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions sample/sample18-lazy-relations/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 7 additions & 5 deletions sample/sample19-one-side-relations/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
});

Expand Down
13 changes: 9 additions & 4 deletions sample/sample21-custom-join-table-column/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
47 changes: 47 additions & 0 deletions sample/sample34-mongodb/app.ts
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions sample/sample34-mongodb/entity/Post.ts
Original file line number Diff line number Diff line change
@@ -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;

}
57 changes: 55 additions & 2 deletions src/connection/Connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
3 changes: 3 additions & 0 deletions src/connection/ConnectionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/connection/ConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 5fcc3ae

Please sign in to comment.