@chevre/domain
Version:
Chevre Domain Library for Node.js
544 lines (543 loc) • 23.5 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OfferCatalogRepo = void 0;
const factory = require("../factory");
const offerCatalog_1 = require("./mongoose/schemas/offerCatalog");
const AVAILABLE_PROJECT_FIELDS = [
'name',
'description',
'project',
'typeOf',
'identifier',
'itemOffered',
'additionalProperty',
'numberOfItems',
'itemListElementTypeOf',
'dateSynced'
];
const SLICE_ITEM_LIST_ELEMENT_MAX_LIMIT = 100;
/**
* カタログリポジトリ
*/
class OfferCatalogRepo {
constructor(connection) {
this.offerCatalogModel = connection.model(offerCatalog_1.modelName, (0, offerCatalog_1.createSchema)());
}
// tslint:disable-next-line:max-func-body-length
static CREATE_MONGO_CONDITIONS(params) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
// MongoDB検索条件
const andConditions = [];
const projectIdEq = (_b = (_a = params.project) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.$eq;
if (typeof projectIdEq === 'string') {
andConditions.push({
'project.id': {
$eq: projectIdEq
}
});
}
const idIn = (_c = params.id) === null || _c === void 0 ? void 0 : _c.$in;
if (Array.isArray(idIn)) {
andConditions.push({ _id: { $in: idIn } });
}
const idRegex = (_d = params.id) === null || _d === void 0 ? void 0 : _d.$regex;
if (typeof idRegex === 'string' && idRegex.length > 0) {
andConditions.push({ _id: { $regex: new RegExp(idRegex) } });
}
const identifierEq = (_e = params.identifier) === null || _e === void 0 ? void 0 : _e.$eq;
if (typeof identifierEq === 'string') {
andConditions.push({
identifier: { $eq: identifierEq }
});
}
const identifierIn = (_f = params.identifier) === null || _f === void 0 ? void 0 : _f.$in;
if (Array.isArray(identifierIn)) {
andConditions.push({ identifier: { $in: identifierIn } });
}
const identifierRegex = (_g = params.identifier) === null || _g === void 0 ? void 0 : _g.$regex;
if (typeof identifierRegex === 'string' && identifierRegex.length > 0) {
andConditions.push({ identifier: { $regex: new RegExp(identifierRegex) } });
}
if (params.name !== undefined) {
andConditions.push({
$or: [
{ 'name.ja': new RegExp(params.name) },
{ 'name.en': new RegExp(params.name) }
]
});
}
const itemListElementTypeOfEq = (_j = (_h = params.itemListElement) === null || _h === void 0 ? void 0 : _h.typeOf) === null || _j === void 0 ? void 0 : _j.$eq;
if (typeof itemListElementTypeOfEq === 'string') {
andConditions.push({ 'itemListElement.typeOf': { $exists: true, $eq: itemListElementTypeOfEq } });
}
const itemListElementIdIn = (_l = (_k = params.itemListElement) === null || _k === void 0 ? void 0 : _k.id) === null || _l === void 0 ? void 0 : _l.$in;
if (Array.isArray(itemListElementIdIn)) {
andConditions.push({
'itemListElement.id': {
$exists: true,
$in: itemListElementIdIn
}
});
}
const itemListElementIdNin = (_o = (_m = params.itemListElement) === null || _m === void 0 ? void 0 : _m.id) === null || _o === void 0 ? void 0 : _o.$nin;
if (Array.isArray(itemListElementIdNin)) {
andConditions.push({
'itemListElement.id': {
$nin: itemListElementIdNin
}
});
}
const itemListElementIdAll = (_q = (_p = params.itemListElement) === null || _p === void 0 ? void 0 : _p.id) === null || _q === void 0 ? void 0 : _q.$all;
if (Array.isArray(itemListElementIdAll)) {
andConditions.push({
'itemListElement.id': {
$exists: true,
$all: itemListElementIdAll
}
});
}
const itemOfferedTypeOfEq = (_s = (_r = params.itemOffered) === null || _r === void 0 ? void 0 : _r.typeOf) === null || _s === void 0 ? void 0 : _s.$eq;
if (typeof itemOfferedTypeOfEq === 'string') {
andConditions.push({
'itemOffered.typeOf': {
$exists: true,
$eq: itemOfferedTypeOfEq
}
});
}
const itemOfferedServiceTypeCodeValueEq = (_v = (_u = (_t = params.itemOffered) === null || _t === void 0 ? void 0 : _t.serviceType) === null || _u === void 0 ? void 0 : _u.codeValue) === null || _v === void 0 ? void 0 : _v.$eq;
if (typeof itemOfferedServiceTypeCodeValueEq === 'string') {
andConditions.push({
'itemOffered.serviceType.codeValue': {
$exists: true,
$eq: itemOfferedServiceTypeCodeValueEq
}
});
}
const additionalPropertyElemMatch = (_w = params.additionalProperty) === null || _w === void 0 ? void 0 : _w.$elemMatch;
if (additionalPropertyElemMatch !== undefined && additionalPropertyElemMatch !== null) {
andConditions.push({
additionalProperty: {
$exists: true,
$elemMatch: additionalPropertyElemMatch
}
});
}
return andConditions;
}
save(params) {
return __awaiter(this, void 0, void 0, function* () {
let savedId;
let doc;
if (typeof params.id !== 'string' || params.id === '') {
const uniqid = yield Promise.resolve().then(() => require('uniqid'));
const newId = uniqid();
const { id, $unset } = params, creatingDoc = __rest(params, ["id", "$unset"]);
doc = yield this.offerCatalogModel.create(Object.assign(Object.assign({}, creatingDoc), { _id: newId }));
savedId = newId;
}
else {
const { id, identifier, itemOffered, project, typeOf, $unset } = params, updateFields = __rest(params, ["id", "identifier", "itemOffered", "project", "typeOf", "$unset"]); // 上書き禁止属性を除外
doc = yield this.offerCatalogModel.findOneAndUpdate({ _id: { $eq: params.id } }, Object.assign({ $set: updateFields }, (params.$unset !== undefined) ? { $unset: params.$unset } : undefined), { upsert: false, new: true, projection: { _id: 1 } })
.exec();
savedId = id;
}
if (doc === null) {
throw new factory.errors.NotFound(this.offerCatalogModel.modelName);
}
return { id: savedId };
});
}
/**
* プロジェクトとコードから冪等保管
*/
saveByIdentifier(params) {
return __awaiter(this, void 0, void 0, function* () {
const uniqid = yield Promise.resolve().then(() => require('uniqid'));
const newId = uniqid();
const { id, identifier, itemOffered, project, typeOf } = params, updateFields = __rest(params, ["id", "identifier", "itemOffered", "project", "typeOf"]);
const doc = yield this.offerCatalogModel.findOneAndUpdate({
identifier: { $eq: params.identifier },
'project.id': { $eq: params.project.id }
}, {
$set: updateFields,
$setOnInsert: { _id: newId, identifier, itemOffered, project, typeOf }
}, {
upsert: true,
new: true,
projection: { _id: 0, id: '$_id' }
})
.lean()
.exec();
return { id: doc.id };
});
}
/**
* コードをキーにして冪等置換(2023-12-14~)
*/
upsertManyByIdentifier(params
// options?: {
// }
) {
return __awaiter(this, void 0, void 0, function* () {
const uniqid = yield Promise.resolve().then(() => require('uniqid'));
const bulkWriteOps = [];
const queryFilters = [];
if (Array.isArray(params)) {
params.forEach(({ $set, $unset }) => {
const { id, identifier, project, typeOf } = $set, setFields = __rest($set, ["id", "identifier", "project", "typeOf"]);
if (typeof identifier !== 'string' || identifier.length === 0) {
throw new factory.errors.ArgumentNull('identifier');
}
// リソースのユニークネスを保証するfilter
const filter = {
'project.id': { $eq: project.id },
identifier: { $eq: identifier }
};
queryFilters.push({
'project.id': { $eq: project.id },
identifier: { $eq: identifier }
});
const newId = uniqid();
const setOnInsert = {
identifier,
project,
typeOf,
_id: newId
};
const updateOne = {
filter,
update: Object.assign({ $setOnInsert: setOnInsert, $set: setFields }, ($unset !== undefined) ? { $unset } : undefined),
upsert: true
};
bulkWriteOps.push({ updateOne });
});
}
if (bulkWriteOps.length > 0) {
const bulkWriteResult = yield this.offerCatalogModel.bulkWrite(bulkWriteOps, { ordered: false });
// const bulkWriteResult = await this.offerCatalogModel.bulkWrite(bulkWriteOps, { ordered: false });
// modifiedの場合upsertedIdsに含まれないので、idを検索する
const modifiedCatalogs = yield this.offerCatalogModel.find({ $or: queryFilters }, { _id: 1 })
.exec();
return { bulkWriteResult, modifiedCatalogs };
}
});
}
/**
* 同期日時を更新する
*/
updateDateSynced(params) {
return __awaiter(this, void 0, void 0, function* () {
yield this.offerCatalogModel.updateOne({ _id: params.id }, { $set: { dateSynced: params.dateSynced } })
.exec();
});
}
updateManyById(params) {
return __awaiter(this, void 0, void 0, function* () {
if (!Array.isArray(params.id.$in) || params.id.$in.length === 0) {
return;
}
const pushItemListElementIds = params.$push.itemListElement.$each.map((element) => element.id);
yield this.offerCatalogModel.updateMany(Object.assign({ _id: { $in: params.id.$in } }, (pushItemListElementIds.length > 0)
// itemListElementのユニークネスを保証する
? {
'itemListElement.id': {
$nin: pushItemListElementIds
}
}
: undefined), Object.assign(Object.assign({}, (pushItemListElementIds.length > 0)
? {
$push: {
itemListElement: {
$each: params.$push.itemListElement.$each,
$slice: params.$push.itemListElement.$slice
}
}
}
: undefined), (params.$pull.itemListElement.$elemMatch.id.$in.length > 0)
? {
$pull: {
itemListElement: {
id: { $in: params.$pull.itemListElement.$elemMatch.id.$in }
}
}
}
: undefined))
.exec();
});
}
pullItemListElement(params) {
return __awaiter(this, void 0, void 0, function* () {
if (params.$pull.itemListElement.$elemMatch.id.$in.length === 0) {
return;
}
return this.offerCatalogModel.updateMany({
'project.id': { $eq: params.project.id },
'itemListElement.id': {
$exists: true,
$in: params.$pull.itemListElement.$elemMatch.id.$in
}
}, {
$pull: {
itemListElement: {
id: { $in: params.$pull.itemListElement.$elemMatch.id.$in }
}
}
})
.exec();
});
}
projectFields(params, inclusion) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const conditions = OfferCatalogRepo.CREATE_MONGO_CONDITIONS(params);
const matchStages = conditions.map((condition) => {
return { $match: condition };
});
let positiveProjectionFields = AVAILABLE_PROJECT_FIELDS;
if (Array.isArray(inclusion) && inclusion.length > 0) {
positiveProjectionFields = inclusion.filter((key) => AVAILABLE_PROJECT_FIELDS.includes(key));
}
else {
// throw new factory.errors.ArgumentNull('inclusion', 'inclusion must be specified');
}
const projection = Object.assign({ _id: 0, id: '$_id' }, Object.fromEntries(positiveProjectionFields.map((key) => {
let value;
switch (key) {
case 'numberOfItems':
value = {
$cond: {
if: { $isArray: '$itemListElement' },
then: { $size: '$itemListElement' },
else: 0
}
};
break;
case 'itemListElementTypeOf':
value = { $first: '$itemListElement.typeOf' };
break;
default:
value = 1;
}
return [key, value];
})));
const aggregate = this.offerCatalogModel.aggregate([
...(((_a = params.sort) === null || _a === void 0 ? void 0 : _a.identifier) !== undefined) ? [{ $sort: { identifier: params.sort.identifier } }] : [],
...matchStages,
{ $project: projection }
]);
if (typeof params.limit === 'number' && params.limit > 0) {
const page = (typeof params.page === 'number' && params.page > 0) ? params.page : 1;
aggregate.limit(params.limit * page)
.skip(params.limit * (page - 1));
}
return aggregate.exec();
});
}
findItemListElementById(params) {
return __awaiter(this, void 0, void 0, function* () {
const doc = yield this.offerCatalogModel.findOne({
'project.id': { $eq: params.project.id },
_id: { $eq: params.id }
}, { 'itemListElement.id': 1, _id: 0 })
.lean() // lean(2024-09-24~)
.exec();
if (doc === null) {
throw new factory.errors.NotFound(this.offerCatalogModel.modelName);
}
return doc;
});
}
/**
* 一つ目のitemListElementを取得する
*/
findFirstItemListElementById(params) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const aggregate = this.offerCatalogModel.aggregate([
{
$match: { _id: { $eq: params.id } }
},
{
$project: {
firstElement: { $first: '$itemListElement' }
}
}
]);
const catalogs = yield aggregate.exec();
const firstElement = (_a = catalogs.shift()) === null || _a === void 0 ? void 0 : _a.firstElement;
if (firstElement === undefined) {
throw new factory.errors.NotFound(this.offerCatalogModel.modelName);
}
return firstElement;
});
}
/**
* カタログのitemListElementをpaging有で検索する
* reimplement as sliceItemListElementById(2025-05-06=)
*/
// public async searchItemListElementById(params: {
// id: string;
// limit: number;
// page: number;
// }): Promise<{ id: string; elementIndex: number }[]> {
// const page: number = (typeof params.page === 'number' && params.page > 0) ? params.page : 1;
// const matchStages: IMatchStage[] = [{ $match: { _id: { $eq: params.id } } }];
// return this.offerCatalogModel.aggregate<{ id: string; elementIndex: number }>([
// {
// $unwind: {
// path: '$itemListElement',
// includeArrayIndex: 'elementIndex'
// }
// },
// ...matchStages,
// {
// $project: {
// _id: 0,
// id: '$itemListElement.id',
// elementIndex: '$elementIndex'
// }
// }
// ])
// .limit(params.limit * page)
// .skip(params.limit * (page - 1))
// .exec();
// }
/**
* カタログのitemListElementをpaging有で検索する
* reimplement using $slice(2025-05-06=)
*/
sliceItemListElementById(params) {
return __awaiter(this, void 0, void 0, function* () {
const { id, limit, page } = params;
if (limit < 1 || limit > SLICE_ITEM_LIST_ELEMENT_MAX_LIMIT) {
throw new factory.errors.Argument('limit', `must be >=1 and <=${SLICE_ITEM_LIST_ELEMENT_MAX_LIMIT}`);
}
const pageMustBePositive = (typeof page === 'number' && page > 0) ? page : 1;
const skip = params.limit * (pageMustBePositive - 1);
const doc = yield this.offerCatalogModel.findOne({ _id: { $eq: id } }, {
_id: 1,
itemListElement: { $slice: [skip, limit] }
})
.lean()
.exec();
// console.log(doc);
return (doc !== null)
? doc.itemListElement.map((item, i) => ({ id: item.id, elementIndex: i + skip }))
: [];
});
}
/**
* カタログIDとアイテムIDからアイテムを全て検索する
*/
searchItemListElement(params) {
return __awaiter(this, void 0, void 0, function* () {
const matchStages = [
{ $match: { _id: { $in: params.id.$in } } },
{ $match: { 'itemListElement.id': { $exists: true, $in: params.itemListElement.id.$in } } }
];
return this.offerCatalogModel.aggregate([
{
$unwind: {
path: '$itemListElement'
}
},
...matchStages,
{
$group: {
_id: '$itemListElement.id'
}
},
{
$project: {
_id: 0,
id: '$_id'
}
}
])
.exec();
});
}
deleteById(params) {
return __awaiter(this, void 0, void 0, function* () {
yield this.offerCatalogModel.findOneAndDelete({ _id: { $eq: params.id } }, { projection: { _id: 1 } })
.exec();
});
}
deleteByProject(params) {
return __awaiter(this, void 0, void 0, function* () {
yield this.offerCatalogModel.deleteMany({
'project.id': { $eq: params.project.id }
})
.exec();
});
}
getCursor(conditions, projection) {
return this.offerCatalogModel.find(conditions, projection)
.sort({ identifier: factory.sortType.Descending })
.cursor();
}
unsetUnnecessaryFields(params) {
return __awaiter(this, void 0, void 0, function* () {
return this.offerCatalogModel.updateMany(params.filter, { $unset: params.$unset }, { timestamps: false })
.exec();
});
}
aggregateItemList(params) {
return __awaiter(this, void 0, void 0, function* () {
const matchConditions = {
'itemListElement.typeOf': { $eq: params.itemListElement.typeOf.$eq }
};
const aggregations = yield this.offerCatalogModel.aggregate([
{ $match: matchConditions },
{
$project: {
numItemListElement: { $size: '$itemListElement' }
}
},
{
$group: {
_id: '$typeOf',
maxNumItemListElement: { $max: '$numItemListElement' },
minNumItemListElement: { $min: '$numItemListElement' }
}
},
{
$project: {
_id: 0,
maxNumItemListElement: '$maxNumItemListElement',
minNumItemListElement: '$minNumItemListElement'
}
}
])
.exec();
if (aggregations.length === 0) {
return {
aggregation: {}
};
}
return { aggregation: aggregations[0] };
});
}
}
exports.OfferCatalogRepo = OfferCatalogRepo;