trying to fix monogdb issues persisting single package

This commit is contained in:
Umed Khudoiberdiev 2017-05-20 14:06:40 +05:00
parent 7f192b6b5d
commit c9dd663019
14 changed files with 119 additions and 53 deletions

View File

@ -2,7 +2,7 @@
"name": "typeorm",
"private": true,
"version": "0.1.0-alpha.1",
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases.",
"description": "Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL, MongoDB databases.",
"license": "MIT",
"readmeFilename": "README.md",
"author": {
@ -73,7 +73,6 @@
"typescript": "^2.3.2"
},
"dependencies": {
"@types/mongodb": "^2.2.2",
"app-root-path": "^2.0.1",
"glob": "^7.1.1",
"reflect-metadata": "^0.1.10",

View File

@ -229,9 +229,8 @@ export class MongoDriver implements Driver {
const promises: Promise<any>[] = [];
await Promise.all(entityMetadatas.map(metadata => {
metadata.indices.forEach(index => {
// const columns = index.buildColumnsAsMap(1);
// const options = { name: index.name };
// promises.push(queryRunner.createCollectionIndex(metadata.tableName, columns, options));
const options = { name: index.name };
promises.push(queryRunner.createCollectionIndex(metadata.tableName, index.columnNamesWithOrderingMap, options));
});
}));
await Promise.all(promises);

View File

@ -6,27 +6,27 @@ export interface EmbeddedMetadataArgs {
/**
* Class to which this column is applied.
*/
readonly target: Function;
target: Function;
/**
* Class's property name to which this column is applied.
*/
readonly propertyName: string;
propertyName: string;
/**
* Indicates if this embedded is array or not.
*/
readonly isArray: boolean;
isArray: boolean;
/**
* Prefix of the embedded, used instead of propertyName.
* If set to empty string, then prefix is not set at all.
*/
readonly prefix?: string|boolean;
prefix?: string|boolean;
/**
* Type of the class to be embedded.
*/
readonly type: ((type?: any) => Function);
type: ((type?: any) => Function);
}

View File

@ -123,6 +123,7 @@ export class EmbeddedMetadata {
this.type = options.args.type();
this.propertyName = options.args.propertyName;
this.customPrefix = options.args.prefix;
this.isArray = options.args.isArray;
}
// ---------------------------------------------------------------------

View File

@ -481,7 +481,12 @@ export class EntityMetadata {
return undefined;
const primaryColumns = this.parentEntityMetadata ? this.primaryColumns : this.primaryColumns;
const map = primaryColumns.reduce((map, column) => OrmUtils.mergeDeep(map, column.getEntityValueMap(entity)), {});
const map = primaryColumns.reduce((map, column) => {
if (column.isObjectId)
return Object.assign(map, column.getEntityValueMap(entity));
return OrmUtils.mergeDeep(map, column.getEntityValueMap(entity));
}, {});
return Object.keys(map).length > 0 ? map : undefined;
}

View File

@ -54,6 +54,12 @@ export class IndexMetadata {
*/
tableName: string;
/**
* Map of column names with order set.
* Used only by MongoDB driver.
*/
columnNamesWithOrderingMap: { [key: string]: number } = {};
// ---------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------
@ -84,6 +90,8 @@ export class IndexMetadata {
* Must be called after all entity metadata's properties map, columns and relations are built.
*/
build(namingStrategy: NamingStrategyInterface): this {
const map: { [key: string]: number } = {};
this.tableName = this.entityMetadata.tableName;
// if columns already an array of string then simply return it
@ -91,11 +99,17 @@ export class IndexMetadata {
let columnPropertyNames: string[] = [];
if (this.givenColumnNames instanceof Array) {
columnPropertyNames = this.givenColumnNames;
columnPropertyNames.forEach(name => map[name] = 1);
} else {
// if columns is a function that returns array of field names then execute it and get columns names from it
const columnsFnResult = this.givenColumnNames(this.entityMetadata.propertiesMap);
const columnsNamesFromFnResult = columnsFnResult instanceof Array ? columnsFnResult : Object.keys(columnsFnResult);
columnPropertyNames = columnsNamesFromFnResult.map((i: any) => String(i));
if (columnsFnResult instanceof Array) {
columnPropertyNames = columnsFnResult.map((i: any) => String(i));
columnPropertyNames.forEach(name => map[name] = 1);
} else {
columnPropertyNames = Object.keys(columnsFnResult).map((i: any) => String(i));
Object.keys(columnsFnResult).forEach(columnName => map[columnName] = columnsFnResult[columnName]);
}
}
const columns = this.entityMetadata.columns.filter(column => columnPropertyNames.indexOf(column.propertyPath) !== -1);
@ -115,9 +129,13 @@ export class IndexMetadata {
this.columns = columns;
}
this.columnNamesWithOrderingMap = Object.keys(map).reduce((updatedMap, key) => {
const column = this.entityMetadata.columns.find(column => column.propertyName === key)!;
updatedMap[column.databaseName] = map[key];
return updatedMap;
}, {} as { [key: string]: number });
this.name = namingStrategy.indexName(this.givenName ? this.givenName : undefined, this.entityMetadata.tableName, this.columns.map(column => column.databaseName));
return this;
}
}

View File

@ -250,6 +250,8 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// if there no ids found (which means all entities are new and have generated ids) - then nothing to load there
// console.log("allIds: ", allIds);
// console.log("subject.entity: ", subjectGroup.subjects);
// console.log("allIds: ", allIds);
if (!allIds.length)
return;
// console.log("Y");
@ -260,6 +262,7 @@ export class SubjectBuilder<Entity extends ObjectLiteral> {
// todo: also custom queryRunnerProvider is an issue
let entities: any[];
if (this.connection.driver instanceof MongoDriver) {
entities = await this.connection
.getMongoRepository<ObjectLiteral>(subjectGroup.target)
.findByIds(allIds);

View File

@ -9,6 +9,7 @@ import {EntityManager} from "../entity-manager/EntityManager";
import {PromiseUtils} from "../util/PromiseUtils";
import {MongoDriver} from "../driver/mongodb/MongoDriver";
import {ColumnMetadata} from "../metadata/ColumnMetadata";
import {EmbeddedMetadata} from "../metadata/EmbeddedMetadata";
/**
* Executes all database operations (inserts, updated, deletes) that must be executed
@ -458,6 +459,43 @@ export class SubjectOperationExecutor {
}
}
private collectColumns(columns: ColumnMetadata[], entity: ObjectLiteral, object: ObjectLiteral) {
columns.forEach(column => {
if (column.isVirtual || column.isParentId || column.isDiscriminator)
return;
const value = entity[column.propertyName];
if (value === undefined)
return;
object[column.databaseNameWithoutPrefixes] = this.connection.driver.preparePersistentValue(value, column); // todo: maybe preparePersistentValue is not responsibility of this class
});
}
private collectEmbeds(embed: EmbeddedMetadata, entity: ObjectLiteral, object: ObjectLiteral) {
if (embed.isArray) {
if (entity[embed.propertyName] instanceof Array) {
if (!object[embed.prefix])
object[embed.prefix] = [];
entity[embed.propertyName].forEach((subEntity: any, index: number) => {
if (!object[embed.prefix][index])
object[embed.prefix][index] = {};
this.collectColumns(embed.columns, subEntity, object[embed.prefix][index]);
embed.embeddeds.forEach(childEmbed => this.collectEmbeds(childEmbed, subEntity, object[embed.prefix][index]));
});
}
} else {
if (entity[embed.propertyName] !== undefined) {
if (!object[embed.prefix])
object[embed.prefix] = {};
this.collectColumns(embed.columns, entity[embed.propertyName], object[embed.prefix]);
embed.embeddeds.forEach(childEmbed => this.collectEmbeds(childEmbed, entity[embed.propertyName], object[embed.prefix]));
}
}
}
/**
* Collects columns and values for the insert operation.
*/
@ -465,16 +503,22 @@ export class SubjectOperationExecutor {
const values: ObjectLiteral = {};
metadata.columns.forEach(column => {
if (column.isVirtual || column.isParentId || column.isDiscriminator)
return;
if (this.connection.driver instanceof MongoDriver) {
this.collectColumns(metadata.ownColumns, entity, values);
metadata.embeddeds.forEach(embed => this.collectEmbeds(embed, entity, values));
const value = column.getEntityValue(entity);
if (value === null || value === undefined)
return;
} else {
metadata.columns.forEach(column => {
if (column.isVirtual || column.isParentId || column.isDiscriminator)
return;
values[column.databaseName] = this.connection.driver.preparePersistentValue(value, column); // todo: maybe preparePersistentValue is not responsibility of this class
});
const value = column.getEntityValue(entity);
if (value === null || value === undefined) // todo: probably check for null should not be there
return;
values[column.databaseName] = this.connection.driver.preparePersistentValue(value, column); // todo: maybe preparePersistentValue is not responsibility of this class
});
}
metadata.relationsWithJoinColumns.forEach(relation => {
relation.joinColumns.forEach(joinColumn => {
@ -675,14 +719,18 @@ export class SubjectOperationExecutor {
if (!idMap)
throw new Error(`Internal error. Cannot get id of the updating entity.`);
const value: ObjectLiteral = {};
/*const value: ObjectLiteral = {};
subject.metadata.columns.forEach(column => {
const columnValue = column.getEntityValue(entity);
if (columnValue !== undefined)
value[column.databaseName] = columnValue;
});
});*/
// addEmbeddedValuesRecursively(entity, value, subject.metadata.embeddeds);
const value: ObjectLiteral = {};
this.collectColumns(subject.metadata.ownColumns, entity, value);
subject.metadata.embeddeds.forEach(embed => this.collectEmbeds(embed, entity, value));
// if number of updated columns = 0 no need to update updated date and version columns
if (Object.keys(value).length === 0)
return;
@ -693,8 +741,6 @@ export class SubjectOperationExecutor {
if (subject.metadata.versionColumn)
value[subject.metadata.versionColumn.databaseName] = this.connection.driver.preparePersistentValue(subject.metadata.versionColumn.getEntityValue(entity) + 1, subject.metadata.versionColumn);
// console.log(value);
// console.log("idMap:", idMap);
return this.queryRunner.update(subject.metadata.tableName, value, idMap);
}

View File

@ -32,18 +32,18 @@ export class DocumentToEntityTransformer {
let hasData = false;
// handle _id property the special way
if (metadata.objectIdColumn && document[metadata.objectIdColumn.databaseName]) {
if (metadata.objectIdColumn && document[metadata.objectIdColumn.databaseNameWithoutPrefixes]) {
// 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.propertyName] = this.driver.prepareHydratedValue(document[metadata.objectIdColumn.name"], metadata.objectIdColumn);
entity[metadata.objectIdColumn.propertyName] = document[metadata.objectIdColumn.databaseName];
entity[metadata.objectIdColumn.propertyName] = document[metadata.objectIdColumn.databaseNameWithoutPrefixes];
hasData = true;
}
// add special columns that contains relation ids
if (this.enableRelationIdValues) {
metadata.columns.filter(column => !!column.relationMetadata).forEach(column => {
const valueInObject = document[column.databaseName];
const valueInObject = document[column.databaseNameWithoutPrefixes];
if (valueInObject !== undefined && valueInObject !== null && column.propertyName) {
// todo: we can't use driver in this class
// const value = this.driver.prepareHydratedValue(valueInObject, column);
@ -67,8 +67,8 @@ export class DocumentToEntityTransformer {
});*/
// get value from columns selections and put them into object
metadata.columns.forEach(column => {
const valueInObject = document[column.databaseName];
metadata.ownColumns.forEach(column => {
const valueInObject = document[column.databaseNameWithoutPrefixes];
if (valueInObject !== undefined &&
valueInObject !== null &&
column.propertyName &&
@ -91,14 +91,14 @@ export class DocumentToEntityTransformer {
entity[embedded.propertyName] = (document[embedded.prefix] as any[]).map(subValue => {
const newItem = embedded.create();
embedded.columns.forEach(column => {
newItem[column.propertyName] = subValue[column.databaseName];
newItem[column.propertyName] = subValue[column.databaseNameWithoutPrefixes];
});
return newItem;
});
} else {
embedded.columns.forEach(column => {
const value = document[embedded.prefix][column.databaseName];
const value = document[embedded.prefix][column.databaseNameWithoutPrefixes];
if (!value) return;
if (!entity[embedded.propertyName])

View File

@ -122,7 +122,7 @@ export class MongoRepository<Entity extends ObjectLiteral> extends Repository<En
*/
async findByIds(ids: any[], optionsOrConditions?: FindManyOptions<Entity>|Partial<Entity>): Promise<Entity[]> {
const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions) || {};
query["_id"] = { $in: ids };
query["_id"] = { $in: ids.map(id => id[this.metadata.objectIdColumn!.propertyName]) };
const cursor = await this.createEntityCursor(query);
if (FindOptionsUtils.isFindManyOptions(optionsOrConditions)) {

View File

@ -55,16 +55,17 @@ export class OrmUtils {
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (isNaN(x) && isNaN(y) && typeof x === "number" && typeof y === "number") {
if (isNaN(x) && isNaN(y) && typeof x === "number" && typeof y === "number")
return true;
}
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
if (x === y)
return true;
if (x.equals instanceof Function && x.equals(y))
return true;
}
// Works in case when functions are created in constructor.
// Comparing dates is a common scenario. Another built-ins?
@ -73,31 +74,25 @@ export class OrmUtils {
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
(x instanceof Number && y instanceof Number))
return x.toString() === y.toString();
}
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
if (!(x instanceof Object && y instanceof Object))
return false;
}
if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
if (x.isPrototypeOf(y) || y.isPrototypeOf(x))
return false;
}
if (x.constructor !== y.constructor) {
if (x.constructor !== y.constructor)
return false;
}
if (x.prototype !== y.prototype) {
if (x.prototype !== y.prototype)
return false;
}
// Check for infinitive linking loops
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1)
return false;
}
// Quick checking of one object being a subset of another.
// todo: cache the structure of arguments[0] for performance

View File

@ -5,7 +5,7 @@ import {Post} from "./entity/Post";
import {Counters} from "./entity/Counters";
import {expect} from "chai";
describe.skip("mongodb > array columns", () => {
describe("mongodb > array columns", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({

View File

@ -6,7 +6,7 @@ import {Counters} from "./entity/Counters";
import {Information} from "./entity/Information";
import {expect} from "chai";
describe.skip("mongodb > embedded columns", () => {
describe("mongodb > embedded columns", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({

View File

@ -4,7 +4,7 @@ import {createTestingConnections, closeTestingConnections, reloadTestingDatabase
import {Post} from "./entity/Post";
import {expect} from "chai";
describe.only("mongodb > indices", () => {
describe("mongodb > indices", () => {
let connections: Connection[];
before(async () => connections = await createTestingConnections({