zing-orm
Version:
397 lines • 28.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ArangodbRepository = void 0;
// const _ = require('lodash');
const moment = require("moment");
const arangojs_1 = require("arangojs");
const RepositoryParams_1 = require("../types/RepositoryParams");
const JoiUtils_1 = require("../../util/JoiUtils");
// import { CommonTools } from '../../util/CommonTools';
const globals_1 = require("../../util/globals");
/**
* Entity manager supposed to work with any entity, automatically find its repository and call its methods,
* whatever entity type are you passing.
*/
class ArangodbRepository {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
constructor(_connection, _entity) {
// 过滤属性,在非赋值情况不覆盖数据:update,insert
this.unset = ['_id', '_rev', '_key', '_status', '_create_date'];
// super(async () => {
try {
if (!_entity) {
throw Error(`constructor params::_entity can not be ${JSON.stringify(_entity)} !!!`);
}
this.connection = _connection;
// this.queryBuilder = knex({ client: 'mssql' });
this.entity = _entity;
this.tableName = (0, globals_1.findTableName)(_entity);
}
catch (err) {
//todo
console.error("初始化 ArangodbRepository Class 失败!!!", err);
}
// })
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
async executeSql(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.ExecuteSqlParamsSchema, params);
return await ArangodbRepository.executeSqlRaw(this.connection, params);
}
catch (err) {
console.log("executeSql error:", err);
throw Error(`executeSql failed!!!, error: ${err}`);
}
}
static async executeSqlRaw(connection, params) {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.ExecuteSqlParamsSchema, params);
var { sql, binds = {}, options = {} } = params;
console.log("executeSqlRaw params: ", sql, binds, options);
const cursor = await await connection.query({
query: sql,
bindVars: binds
});
let result = await cursor.all();
console.log("executeSqlRaw Query results: ");
console.dir(result);
return result;
}
// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------
/**
* 处理数组属性的查询参数
* @param {string} alias 别名
* @param {string} key 属性名
* @param {any} data 数据值
*/
getArrayAttrAql(alias, key, data) {
const arrayAttrAqlList = [];
// 数组拆分元素用 or 连接
if (Array.isArray(data)) {
// 处理 or 条件
data && data.forEach((el, idx) => {
if (idx === 0) {
arrayAttrAqlList.push((0, arangojs_1.aql) `\nFILTER `);
}
// filter 隐含了 ‘and’条件,所以只需要所有的 ‘or’ 条件连接在一个 filter 即可
if (idx === data.length - 1) {
arrayAttrAqlList.push((0, arangojs_1.aql) `${el} in ${alias}.${key} `);
}
else {
arrayAttrAqlList.push((0, arangojs_1.aql) `${el} in ${alias}.${key} or `);
}
});
}
else {
arrayAttrAqlList.push((0, arangojs_1.aql) `\nFILTER ${data} in ${alias}.${key}`);
}
return arangojs_1.aql.join(arrayAttrAqlList);
}
/**
* 拼装过滤参数:使用FILTER可以利用索引(and连接)
* - (单数类型)默认==匹配
* - array类型使用in匹配
* - object{opr, value}类型使用指定操作符
* - opr = 'POSITION' 时需要处理数组属性的查询参数
* aql.join, aql.literal @see https://www.arangodb.com/docs/devel/appendix-java-script-modules-arango-db.html
* @param {object} filter 查询参数对象
* @param {string} alias alias of collection
* @return {AQL} obj
*/
getFilterAql(filter, alias) {
// 默认别名为't'
if (!alias) {
alias = 't';
}
alias = arangojs_1.aql.literal(alias);
let filterAql;
if (filter) {
let filterAqlList = [];
const otherFilterAqlList = [];
const arrayAttrAqlList = [];
let first = true;
for (const key in filter) {
const data = filter[key];
if (data !== undefined) {
if (Array.isArray(data)) {
// 数组参数,则‘in’
filterAqlList.push((0, arangojs_1.aql) ` and ${alias}.${key} in ${data}`);
}
else if (!!data && typeof data === 'object') {
/**
* 对象参数结构{opr, value}
* 支持:==, !=, <, <=, >, >=, IN, NOT IN, LIKE, =~, !~
* 注意: typeof null === 'object'
* 增加:opr = 'POSITION' 时需要处理数组属性的查询参数
*/
if (data.opr === 'POSITION') {
arrayAttrAqlList.push(this.getArrayAttrAql(alias, key, data.value));
}
else {
otherFilterAqlList.push((0, arangojs_1.aql) ` and ${alias}.${key} ${arangojs_1.aql.literal(data.opr)} ${data.value}`);
}
}
else {
// 单个参数,默认‘==’
filterAqlList.push((0, arangojs_1.aql) ` and ${alias}.${key} == ${data}`);
}
if (first)
first = false;
}
}
// otherFilterAqlList在后,符合最左匹配原则
filterAqlList = filterAqlList.concat(otherFilterAqlList).concat(arrayAttrAqlList);
if (filterAqlList.length !== 0) {
filterAql = arangojs_1.aql.join(filterAqlList);
}
}
return filterAql;
}
/**
* 处理排序字符串(不含SORT)
* @param {object} sorts params
* @param {string} alias alias of collection
* @return {AQL} obj
*/
getSortFieldAqlList(sorts, alias) {
// 默认别名为't'
if (!alias) {
alias = 't';
}
alias = arangojs_1.aql.literal(alias);
// 默认时间倒序和_id倒序
const fieldAqlList = [(0, arangojs_1.aql) ` ${alias}._create_date desc, ${alias}._id desc `];
// const fieldAqlList = [aql` ${alias}._id `];
if (sorts) {
// 删除默认排序
fieldAqlList.shift();
sorts && sorts.forEach((el) => {
if (!el.direction) {
fieldAqlList.push((0, arangojs_1.aql) `${alias}.${el.field}`);
}
else {
fieldAqlList.push((0, arangojs_1.aql) `${alias}.${el.field} ${el.direction}`);
}
});
}
return fieldAqlList;
}
/**
* 拼装排序条件
* @param {array} sortFieldAqlList params
* @return {AQL} obj
*/
getSortAql(sortFieldAqlList) {
if (sortFieldAqlList && sortFieldAqlList.length > 0) {
// 添加到开头
const sorts = [(0, arangojs_1.aql) ` SORT`];
sorts.push(sortFieldAqlList[0]);
// 补逗号
for (let i = 1; i < sortFieldAqlList.length; i++) {
sorts.push((0, arangojs_1.aql) `, `);
sorts.push(sortFieldAqlList[i]);
}
sortFieldAqlList = sorts;
}
// 拼接完整sort AQL
return arangojs_1.aql.join(sortFieldAqlList);
}
/**
* Find with pagination and condition.
* - cant use findAndCount for both params
* - use createQueryBuilder
* @param params 参数
* @param params.current 页码
* @param params.pageSize 页面条数
* @param params.sort 排序
* @param dataFunc 数据处理函数
*/
//todo add filter, knex
async getPage(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.GetPageParamsSchema, params);
let { current, pageSize, sorts, filter
// options
} = params;
// 转换分页参数:pageNum,page_size => offset,count
let offset = (current - 1) * pageSize;
let count = pageSize;
const collection = arangojs_1.aql.literal(`${this.tableName}`);
const filterAql = this.getFilterAql(filter);
// 拼装排序条件 默认创建时间倒序
const sortAql = this.getSortAql(this.getSortFieldAqlList(sorts));
// const collections = this.aql.literal(`${pluralize(this.getLowerCollectionName())}`);
const query = (0, arangojs_1.aql) `
LET ts = (
FOR t IN ${collection}
FILTER t._status == true ${filterAql}
${sortAql}
RETURN t
)
LET tsl = (
LET list = (
FOR tl IN ts
LIMIT ${offset},${count}
RETURN tl
)
RETURN list
)
LET total = LENGTH(ts)
LET flag = (${offset} + ${count} >= total)
LET hasMore = !flag
RETURN {total:total, hasMore:hasMore, list:tsl[0]}`;
const rows = await this.executeSql({
sql: query.query,
binds: query.bindVars
});
let result = Object.assign({}, rows[0], {
totalPage: Math.ceil(rows[0].total / pageSize),
current,
pageSize,
});
return result;
}
catch (err) {
console.error("ArangodbRepository getPage 失败!!!", err);
throw Error(`ArangodbRepository getPage 失败!!!, 错误: ${err}`);
}
}
/**
* get documents by filter of collection
* @param {object} _params - params object
* @param {object} _params.filter - filter of attrs.
* @param {object} _params.like - the object to like the documents to get from the collection
* @param {array} _params.sorts - array of sort str for attrs.
* @param {object} _params.options - query options, e.g. keepAttrs-res attrs; hasEdge-edge res;
* @return {AqlQuery} - interface AqlQuery by arangodb
*/
async getsByFilter(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.GetsByFilterParamsSchema, params);
let { filter, sorts,
// options
} = params;
// 修改为使用FILTER可以利用索引
const filterAql = this.getFilterAql(filter);
// 拼装排序条件 默认创建时间倒序
const sortAql = this.getSortAql(this.getSortFieldAqlList(sorts));
const collection = arangojs_1.aql.literal(`${this.tableName}`);
const query = (0, arangojs_1.aql) `
FOR t IN ${collection}
FILTER t._status == true ${filterAql}
${sortAql}
RETURN t`;
const rows = await this.executeSql({
sql: query.query,
binds: query.bindVars
});
return rows;
}
catch (err) {
console.error("ArangodbRepository getsByFilter 失败!!!", err);
throw Error(`ArangodbRepository getsByFilter 失败!!!, 错误: ${err}`);
}
}
/**
* update document by _id && newObj from collection
* @param {object} _params - the _id of the document
* @param {string} _params.filter - the _id of the document
* @param {object} _params.newObj - the newObj of the document
* @return {AqlQuery} - interface AqlQuery by arangodb
*/
async updatesByFilter(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.UpdatesByFilterParamsSchema, params);
let { filter, newObj,
// options
} = params;
const update_date = moment().format('YYYY-MM-DD HH:mm:ss');
const filterAql = this.getFilterAql(filter);
const collection = arangojs_1.aql.literal(`${this.tableName}`);
// todo add returns 返回 NEW. etc.
const query = (0, arangojs_1.aql) `
let tsl = (
FOR t IN ${collection}
FILTER t._status == true ${filterAql}
UPDATE t WITH MERGE(UNSET(${newObj}, ${this.unset}), { _update_date:${update_date}})
IN ${collection} OPTIONS { keepNull: false }
RETURN NEW._id
)
RETURN {ids: tsl}`;
const rows = await this.executeSql({
sql: query.query,
binds: query.bindVars
});
return rows[0];
}
catch (err) {
console.error("ArangodbRepository updatesByFilter 失败!!!", err);
throw Error(`ArangodbRepository updatesByFilter 失败!!!, 错误: ${err}`);
}
}
async saves(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.SavesParamsSchema, params);
let { objs,
// options
} = params;
//todo 检查Entity attributes
const create_date = moment().format('YYYY-MM-DD HH:mm:ss');
const collection = arangojs_1.aql.literal(`${this.tableName}`);
const query = (0, arangojs_1.aql) `
let tsl = (
FOR t IN ${objs}
INSERT MERGE(UNSET(t, ${this.unset}), { _create_date:${create_date}, _status: true })
INTO ${collection}
RETURN NEW._id
)
RETURN {ids: tsl}`;
const rows = await this.executeSql({
sql: query.query,
binds: query.bindVars
});
return rows[0];
}
catch (err) {
console.error("ArangodbRepository save 失败!!!", err);
throw Error(`ArangodbRepository save 失败!!!, 错误: ${err}`);
}
}
async deletesByFilter(params) {
try {
await JoiUtils_1.JoiUtils.checkParams(RepositoryParams_1.DeletesByFilterParamsSchema, params);
let { filter,
// options
} = params;
const delete_date = moment().format('YYYY-MM-DD HH:mm:ss');
const filterAql = this.getFilterAql(filter);
const collection = arangojs_1.aql.literal(`${this.tableName}`);
const query = (0, arangojs_1.aql) `
let tsl = (
FOR t IN ${collection}
FILTER t._status == true ${filterAql}
UPDATE t WITH MERGE(${{ _status: false }}, { _delete_date:${delete_date}})
IN ${collection} OPTIONS { keepNull: false }
RETURN NEW._id
)
RETURN {ids: tsl}`;
const rows = await this.executeSql({
sql: query.query,
binds: query.bindVars
});
return rows[0];
}
catch (err) {
console.error("ArangodbRepository deletesByFilter 失败!!!", err);
throw Error(`ArangodbRepository deletesByFilter 失败!!!, 错误: ${err}`);
}
}
}
exports.ArangodbRepository = ArangodbRepository;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQXJhbmdvZGJSZXBvc2l0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiQXJhbmdvZGJSZXBvc2l0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLCtCQUErQjtBQUMvQixpQ0FBaUM7QUFDakMsdUNBQStCO0FBQy9CLGdFQWFtQztBQUNuQyxrREFBK0M7QUFDL0Msd0RBQXdEO0FBQ3hELGdEQUFtRDtBQUVuRDs7O0dBR0c7QUFDSCxNQUFhLGtCQUFrQjtJQW9CM0IsNEVBQTRFO0lBQzVFLGNBQWM7SUFDZCw0RUFBNEU7SUFDNUUsWUFBWSxXQUFnQixFQUFFLE9BQVk7UUFOMUMsaUNBQWlDO1FBQ2pDLFVBQUssR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQU12RCxzQkFBc0I7UUFDdEIsSUFBSTtZQUNBLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ1YsTUFBTSxLQUFLLENBQUMsMENBQTBDLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2FBQ3hGO1lBQ0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxXQUFXLENBQUM7WUFDOUIsaURBQWlEO1lBQ2pELElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDO1lBQ3RCLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBQSx1QkFBYSxFQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQzNDO1FBQUMsT0FBTyxHQUFHLEVBQUU7WUFDVixNQUFNO1lBQ04sT0FBTyxDQUFDLEtBQUssQ0FBQyxvQ0FBb0MsRUFBRSxHQUFHLENBQUMsQ0FBQztTQUM1RDtRQUNELEtBQUs7SUFDVCxDQUFDO0lBRUQsNEVBQTRFO0lBQzVFLGlCQUFpQjtJQUNqQiw0RUFBNEU7SUFFNUUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxNQUF3QjtRQUNyQyxJQUFJO1lBQ0EsTUFBTSxtQkFBUSxDQUFDLFdBQVcsQ0FBQyx5Q0FBc0IsRUFB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