@blueleader07/typeorm
Version:
Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, MongoDB databases.
284 lines (282 loc) • 11.6 kB
JavaScript
/**
* 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 DynamoDB driver which has some specifics in its EntityManager.
*/
import { EntityManager } from "./EntityManager";
import { FindOptionsUtils } from "../find-options/FindOptionsUtils";
import { dynamoParamHelper } from "../driver/dynamo/helpers/DynamoParamHelper";
import { indexedColumns, populateGeneratedColumns, } from "../driver/dynamo/helpers/DynamoGlobalSecondaryIndexHelper";
import { DynamoFindOptions } from "../driver/dynamo/models/DynamoFindOptions";
import { dynamoBatchHelper } from "../driver/dynamo/helpers/DynamoBatchHelper";
import { mixin, isEmpty } from "../driver/dynamo/helpers/DynamoObjectHelper";
import { getDocumentClient } from "../driver/dynamo/DynamoClient";
// todo: we should look at the @PrimaryKey on the entity
const DEFAULT_KEY_MAPPER = (item) => {
return {
id: item.id,
};
};
export class DynamoEntityManager extends EntityManager {
get dynamodbQueryRunner() {
return this.connection.driver
.queryRunner;
}
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(connection) {
super(connection);
}
// -------------------------------------------------------------------------
// Overridden Methods
// -------------------------------------------------------------------------
createDefaultKeyMapper(target) {
const metadata = this.connection.getMetadata(target);
return (entity) => {
const keys = {};
for (let i = 0; i < metadata.primaryColumns.length; i++) {
const primaryColumn = metadata.primaryColumns[i];
const propertyName = primaryColumn.propertyName;
keys[propertyName] = entity[propertyName];
}
return keys;
};
}
async update(entityClassOrName, options) {
const metadata = this.connection.getMetadata(entityClassOrName);
const changedValues = mixin(options.setValues || {}, options.where);
indexedColumns(metadata, changedValues);
mixin(options.setValues, changedValues);
delete options.setValues.id;
const params = dynamoParamHelper.update(metadata.tablePath, options);
return getDocumentClient().update(params).promise();
}
/**
* Finds entities that match given find options or conditions.
*/
async find(entityClassOrName, options) {
if (options) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClassOrName);
const params = dynamoParamHelper.find(metadata.tablePath, options, metadata.indices);
const results = isEmpty(options.where)
? await dbClient.scan(params).promise()
: await dbClient.query(params).promise();
const items = results.Items || [];
items.lastEvaluatedKey = results.LastEvaluatedKey;
return items;
}
return [];
}
/**
* Finds entities that match given find options or conditions.
*/
async findAll(entityClassOrName, options) {
delete options.limit;
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClassOrName);
const params = dynamoParamHelper.find(metadata.tablePath, options, metadata.indices);
let items = [];
let results = await dbClient.query(params).promise();
items = items.concat(results.Items || []);
while (results.LastEvaluatedKey) {
params.ExclusiveStartKey = results.LastEvaluatedKey;
results = await dbClient.query(params).promise();
items = items.concat(results.Items || []);
}
return items;
}
async scan(entityClassOrName, options) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClassOrName);
const params = {
TableName: metadata.tablePath,
// IndexName: findOptions.index,
// KeyConditionExpression: FindOptions.toKeyConditionExpression(findOptions.where),
// ExpressionAttributeValues: FindOptions.toExpressionAttributeValues(findOptions.where)
};
if (options.limit) {
params.Limit = options.limit;
}
if (options.exclusiveStartKey) {
params.ExclusiveStartKey = options.exclusiveStartKey;
}
const results = await dbClient.scan(params).promise();
const items = results.Items || [];
items.LastEvaluatedKey = results.LastEvaluatedKey;
return items;
}
/**
* Finds first entity that matches given conditions and/or find options.
*/
async findOne(entityClass, options) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClass);
const id = typeof options === "string" ? options : undefined;
const findOneOptionsOrConditions = options;
let findOptions;
if (FindOptionsUtils.isFindOneOptions(findOneOptionsOrConditions)) {
findOptions = new DynamoFindOptions();
findOptions.where = findOneOptionsOrConditions.where;
findOptions.limit = 1;
}
else {
findOptions = new DynamoFindOptions();
findOptions.where = { id };
findOptions.limit = 1;
}
const params = dynamoParamHelper.find(metadata.tablePath, findOptions, metadata.indices);
const results = await dbClient.query(params).promise();
const items = results.Items || [];
return items.length > 0 ? items[0] : undefined;
}
/**
* Finds first entity that matches given conditions and/or find options.
*/
async findOneBy(entityClass, options) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClass);
const findOptions = new DynamoFindOptions();
findOptions.where = options;
findOptions.limit = 1;
const params = dynamoParamHelper.find(metadata.tablePath, findOptions, metadata.indices);
const results = await dbClient.query(params).promise();
const items = results.Items || [];
return items.length > 0 ? items[0] : undefined;
}
/**
* Put a given entity into the database.
* Unlike save method executes a primitive operation without cascades, relations and other operations included.
* Executes fast and efficient INSERT query.
* Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted.
* You can execute bulk inserts using this method.
*/
async put(target, entity) {
if (Array.isArray(entity)) {
return this.putMany(target, entity);
}
else {
return this.putOne(target, entity);
}
}
async delete(targetOrEntity, criteria) {
if (Array.isArray(criteria)) {
await this.deleteMany(targetOrEntity, criteria);
}
else {
await this.deleteOne(targetOrEntity, criteria);
}
return {
raw: {},
};
}
async deleteAll(target, options, keyMapper) {
let items = await this.scan(target, { limit: 500 });
while (items.length > 0) {
const itemIds = items.map(keyMapper || this.createDefaultKeyMapper(target));
await this.deleteMany(target, itemIds);
await this.deleteAll(target, keyMapper);
items = await this.scan(target, { limit: 500 });
}
}
deleteAllBy(target, options, keyMapper) {
options.limit = options.limit || 500;
return this.deleteQueryBatch(target, options, keyMapper);
}
async deleteQueryBatch(target, options, keyMapper) {
const items = await this.find(target, options);
if (items.length > 0) {
const metadata = this.connection.getMetadata(target);
keyMapper = keyMapper || DEFAULT_KEY_MAPPER;
const keys = items.map(keyMapper);
await this.deleteMany(metadata.tablePath, keys);
await this.deleteQueryBatch(target, options, keyMapper);
}
}
/**
* Delete multiple documents on DynamoDB.
*/
deleteMany(entityClassOrName, keys) {
const metadata = this.connection.getMetadata(entityClassOrName);
return this.dynamodbQueryRunner.deleteMany(metadata.tablePath, keys);
}
/**
* Delete a document on DynamoDB.
*/
deleteOne(entityClassOrName, key) {
const metadata = this.connection.getMetadata(entityClassOrName);
return this.dynamodbQueryRunner.deleteOne(metadata.tablePath, key);
}
/**
* Put an array of documents into DynamoDB.
*/
putMany(entityClassOrName, docs) {
const metadata = this.connection.getMetadata(entityClassOrName);
docs.forEach((doc) => {
indexedColumns(metadata, doc);
});
return this.dynamodbQueryRunner.putMany(metadata.tablePath, docs);
}
/**
* Put a single document into DynamoDB.
*/
putOne(entityClassOrName, doc) {
const metadata = this.connection.getMetadata(entityClassOrName);
indexedColumns(metadata, doc);
populateGeneratedColumns(metadata, doc);
return this.dynamodbQueryRunner.putOne(metadata.tablePath, doc);
}
/**
* Read from DynamoDB in batches.
*/
async batchRead(entityClassOrName, keys) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClassOrName);
const batches = dynamoBatchHelper.batch(keys, 100);
let items = [];
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const requestItems = {};
requestItems[metadata.tablePath] = {
Keys: batch,
};
const response = await dbClient
.batchGet({
RequestItems: requestItems,
})
.promise();
if (response.Responses !== undefined) {
items = items.concat(response.Responses[metadata.tablePath]);
}
}
return items;
}
/**
* Put an array of documents into DynamoDB in batches.
*/
// TODO: ... how do we update the indexColumn values here ... ?
async batchWrite(entityClassOrName, writes) {
const dbClient = getDocumentClient();
const metadata = this.connection.getMetadata(entityClassOrName);
const batches = dynamoBatchHelper.batch(writes, 25);
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
const requestItems = {};
requestItems[metadata.tablePath] = batch.map((write) => {
const request = {};
request[write.type] = {
Item: write.item,
};
return request;
});
await dbClient
.batchWrite({
RequestItems: requestItems,
})
.promise();
}
}
}
//# sourceMappingURL=DynamoEntityManager.js.map