@chevre/domain
Version:
Chevre Domain Library for Node.js
678 lines (677 loc) • 31.7 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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PendingReservationRepo = void 0;
const createDebug = require("debug");
const errorHandler_1 = require("../errorHandler");
const factory = require("../factory");
const settings_1 = require("../settings");
const pendingReservation_1 = require("./mongoose/schemas/pendingReservation");
const debug = createDebug('chevre-domain:repo:pendingReservation');
/**
* 保留予約リポジトリ
*/
class PendingReservationRepo {
constructor(connection) {
// this.aggregateReservationModel = connection.model(modelName, createSchema());
this.pendingReservationModel = connection.model(pendingReservation_1.modelName, (0, pendingReservation_1.createSchema)());
}
static CREATE_FILTER_QUERY(params) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
const filterQueries = [
// { numSeats: { $gte: 1 } }
];
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') {
filterQueries.push({ 'project.id': { $eq: projectIdEq } });
}
const providerIdEq = (_d = (_c = params.provider) === null || _c === void 0 ? void 0 : _c.id) === null || _d === void 0 ? void 0 : _d.$eq;
if (typeof providerIdEq === 'string') {
filterQueries.push({ 'provider.id': { $eq: providerIdEq } });
}
const reservationForIdEq = (_f = (_e = params.reservationFor) === null || _e === void 0 ? void 0 : _e.id) === null || _f === void 0 ? void 0 : _f.$eq;
if (typeof reservationForIdEq === 'string') {
filterQueries.push({ 'reservationFor.id': { $eq: reservationForIdEq } });
}
const reservationNumberEq = (_g = params.reservationNumber) === null || _g === void 0 ? void 0 : _g.$eq;
if (typeof reservationNumberEq === 'string') {
filterQueries.push({ reservationNumber: { $eq: reservationNumberEq } });
}
const subReservationIdentifierEq = (_j = (_h = params.subReservation) === null || _h === void 0 ? void 0 : _h.identifier) === null || _j === void 0 ? void 0 : _j.$eq;
if (typeof subReservationIdentifierEq === 'string') {
filterQueries.push({ 'subReservation.identifier': { $eq: subReservationIdentifierEq } });
}
return filterQueries;
}
static offer2identifier(params, hasTicketedSeat) {
var _a, _b;
if (hasTicketedSeat) {
return `${params.seatSection}:${params.seatNumber}`;
}
else {
// 予約IDをfieldにする場合
const serviceOutputId = (_b = (_a = params.itemOffered) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.id;
if (typeof serviceOutputId === 'string' && serviceOutputId !== '') {
return serviceOutputId;
}
else {
throw new factory.errors.Internal('offer2identifier requires itemOffered.serviceOutput.id');
}
}
}
// private static lockKey2aggregateReservation(lockKey: ILockKey): IAggregateReservation {
// const { eventId, startDate, holder, offers, expires, hasTicketedSeat } = lockKey;
// if (!(startDate instanceof Date)) {
// throw new factory.errors.Argument('startDate', 'must be Date');
// }
// const reservations = offers.map((offer) => {
// const reservationIdentifier = PendingReservationRepo.offer2identifier(offer, hasTicketedSeat);
// const reservationId = `${holder}:${reservationIdentifier}`;
// return {
// identifier: reservationIdentifier,
// reservationId
// };
// });
// // check uniqueness
// const reservationIdentifiers = reservations.map(({ identifier }) => identifier);
// const uniqueIdentifiers = [...new Set(reservationIdentifiers)];
// if (uniqueIdentifiers.length !== reservationIdentifiers.length) {
// throw new factory.errors.Argument('offers', 'offers must be unique');
// }
// return {
// project: { id: lockKey.project.id, typeOf: factory.organizationType.Project },
// typeOf: 'AggregateReservation',
// expires,
// reservationCount: 0,
// reservationFor: { id: eventId, startDate },
// reservationIds: reservations.map(({ reservationId }) => reservationId)
// };
// }
static lockKey2reservationPackage(lockKey) {
const { eventId, startDate, holder, offers, expires, hasTicketedSeat, bookingTime } = lockKey;
if (!(startDate instanceof Date)) {
throw new factory.errors.Argument('startDate', 'must be Date');
}
const subReservation = offers.map((offer) => {
return { identifier: PendingReservationRepo.offer2identifier(offer, hasTicketedSeat) };
});
return {
project: { id: lockKey.project.id, typeOf: factory.organizationType.Project },
provider: { id: lockKey.provider.id, typeOf: factory.organizationType.Corporation },
typeOf: factory.reservationType.ReservationPackage,
bookingTime,
expires,
numSeats: offers.length,
reservationFor: { id: eventId, startDate },
reservationNumber: holder,
subReservation
};
}
// public async lockIfNotLimitExceeded(lockKey: ILockKey, maximumReservationCount: number): Promise<void> {
// const aggregateReservation = PendingReservationRepo.lockKey2aggregateReservation(lockKey);
// const reservationPackage = PendingReservationRepo.lockKey2reservationPackage(lockKey);
// // まずcreate document
// await this.createIfNotExist(aggregateReservation);
// await this.increaseReservationCount(aggregateReservation, { maximumReservationCount });
// try {
// await this.createReservationPackageIfPossible(reservationPackage);
// } catch (error) {
// await this.decreaseReservationCountByLockKey(lockKey);
// throw error;
// }
// }
// public async lock(lockKey: ILockKey): Promise<void> {
// const aggregateReservation = PendingReservationRepo.lockKey2aggregateReservation(lockKey);
// const reservationPackage = PendingReservationRepo.lockKey2reservationPackage(lockKey);
// // まずcreate document
// await this.createIfNotExist(aggregateReservation);
// await this.increaseReservationCount(aggregateReservation, {});
// try {
// await this.createReservationPackageIfPossible(reservationPackage);
// } catch (error) {
// await this.decreaseReservationCountByLockKey(lockKey);
// throw error;
// }
// }
lockIfNotLimitExceeded(lockKey, maximumReservationCount) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId } = lockKey;
const reservationPackage = PendingReservationRepo.lockKey2reservationPackage(lockKey);
// lock前でもcapacity確認
// dateCreated < bookingTimeの集計
const currentReservationCountBeforeLock = yield this.aggregateNumSeats({
bookingTime: { $lte: reservationPackage.bookingTime },
dateCreated: { $lt: reservationPackage.bookingTime },
reservationFor: { id: eventId },
limit: maximumReservationCount
});
const remainingAttendeeCapacity = maximumReservationCount - currentReservationCountBeforeLock;
if (remainingAttendeeCapacity <= 0) {
throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
}
yield this.createReservationPackageIfPossible(reservationPackage);
try {
// dateCreated >= bookingTimeの集計
const reservationCountAfterAggregate = yield this.aggregateNumSeats({
bookingTime: { $lte: reservationPackage.bookingTime },
dateCreated: { $gte: reservationPackage.bookingTime },
reservationFor: { id: eventId },
limit: remainingAttendeeCapacity
});
// console.log('reservationCountAfterAggregate:', reservationCountAfterAggregate, remainingAttendeeCapacity);
if (reservationCountAfterAggregate > remainingAttendeeCapacity) {
throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
}
}
catch (error) {
// 最大数超過であればreservationPackageを削除
yield this.deleteReservationPackage(reservationPackage);
throw error;
}
});
}
lock(lockKey) {
return __awaiter(this, void 0, void 0, function* () {
const reservationPackage = PendingReservationRepo.lockKey2reservationPackage(lockKey);
yield this.createReservationPackageIfPossible(reservationPackage);
});
}
// public async unlock(params: IUnlockKey): Promise<void> {
// await this.decreaseReservationCount(params);
// await this.deleteReservationIfExists(params);
// }
unlock(params) {
return __awaiter(this, void 0, void 0, function* () {
yield this.deleteReservationIfExists(params);
});
}
// public async countUnavailableOffers(params: {
// event: {
// id: string;
// startDate: Date;
// hasTicketedSeat: boolean;
// };
// }): Promise<number> {
// const { event } = params;
// const doc = await this.aggregateReservationModel.findOne(
// { 'reservationFor.id': { $eq: event.id } },
// {
// _id: 0,
// reservationCount: 1
// }
// )
// .setOptions({ maxTimeMS: MONGO_MAX_TIME_MS })
// .lean<Pick<IAggregateReservation, 'reservationCount'>>()
// .exec();
// return (doc !== null) ? doc.reservationCount : 0;
// }
/**
* 現時点での保留予約数を集計する
*/
countUnavailableOffers(params) {
return __awaiter(this, void 0, void 0, function* () {
const { event } = params;
return this.aggregateNumSeats({
bookingTime: { $lte: new Date() }, // 指定有無での性能は対して変わらないか(2025-04-25)
reservationFor: { id: event.id }
});
});
}
getHolder(params) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId, offer, hasTicketedSeat } = params;
const reservationIdentifier = PendingReservationRepo.offer2identifier(offer, hasTicketedSeat);
const doc = yield this.pendingReservationModel.findOne({
'reservationFor.id': { $eq: eventId },
'subReservation.identifier': { $exists: true, $eq: reservationIdentifier }
}, { _id: 0, reservationNumber: 1 })
.setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.lean()
.exec();
return (doc !== null) ? doc.reservationNumber : undefined;
});
}
searchHolders(params) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId, offers, hasTicketedSeat } = params;
const reservationIdentifiers = offers.map((offer) => PendingReservationRepo.offer2identifier(offer, hasTicketedSeat));
const aggregate = this.pendingReservationModel.aggregate([
// unwind,matchの順序
// unwind->matchでは遅い?
// match->unwind->matchにする
{
$match: {
'reservationFor.id': { $eq: eventId },
'subReservation.identifier': { $exists: true, $in: reservationIdentifiers }
}
},
{
$unwind: {
path: '$subReservation'
}
},
{
$match: {
// 'subReservation.identifier': { $exists: true, $in: reservationIdentifiers }
'subReservation.identifier': { $in: reservationIdentifiers } // $exists不要か?
}
}, // unwind後にもmatchがないと、reservationIdentifiers以外のsubReservationもprojectされる
{
$project: {
_id: 0,
// reservationNumber: '$reservationNumber',
identifier: '$subReservation.identifier'
}
}
]);
const docs = yield aggregate
.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
debug('searchHolders: aggregated.', docs, docs.length, 'docs');
// reservationNumberを正確に返す必要はなく、存在しているかどうかだけ分かればよい(stringを返せばよい)
return reservationIdentifiers.map((reservationIdentifier) => {
const doc = docs.find(({ identifier }) => identifier === reservationIdentifier);
// tslint:disable-next-line:no-null-keyword
// return (doc !== undefined) ? doc.reservationNumber : null;
// tslint:disable-next-line:no-null-keyword
return (doc !== undefined) ? doc.identifier : null;
});
});
}
// public async searchHolders2(params: {
// project: { id: string };
// eventId: string;
// startDate: Date;
// hasTicketedSeat: boolean;
// offers: IOffer[];
// }): Promise<IGetHolderResult[]> {
// const { eventId, offers, hasTicketedSeat } = params;
// const reservationIdentifiers = offers.map((offer) => PendingReservationRepo.offer2identifier(offer, hasTicketedSeat));
// const aggregate = this.pendingReservationModel.aggregate<{
// // reservationNumber: string;
// identifier: string;
// }>([
// // unwind,matchの順序
// // unwind->matchでは遅い?
// // match->limit->unwind->matchにする
// {
// $unwind: {
// path: '$subReservation'
// }
// },
// {
// $match: {
// 'reservationFor.id': { $eq: eventId },
// 'subReservation.identifier': { $exists: true, $in: reservationIdentifiers }
// }
// },
// { $limit: reservationIdentifiers.length },
// {
// $project: {
// _id: 0,
// // reservationNumber: '$reservationNumber',
// identifier: '$subReservation.identifier'
// }
// }
// ]);
// const docs = await aggregate
// .option({ maxTimeMS: MONGO_MAX_TIME_MS })
// .exec();
// debug('searchHolders: aggregated.', docs, docs.length, 'docs');
// // reservationNumberを正確に返す必要はなく、存在しているかどうかだけ分かればよい(stringを返せばよい)
// return reservationIdentifiers.map((reservationIdentifier) => {
// const doc = docs.find(({ identifier }) => identifier === reservationIdentifier);
// // tslint:disable-next-line:no-null-keyword
// // return (doc !== undefined) ? doc.reservationNumber : null;
// // tslint:disable-next-line:no-null-keyword
// return (doc !== undefined) ? doc.identifier : null;
// });
// }
// public async searchHoldersByDistinct(params: {
// project: { id: string };
// eventId: string;
// startDate: Date;
// hasTicketedSeat: boolean;
// offers: IOffer[];
// }) {
// const { eventId, offers, hasTicketedSeat } = params;
// const reservationIdentifiers = offers.map((offer) => PendingReservationRepo.offer2identifier(offer, hasTicketedSeat));
// const doc = await this.pendingReservationModel.distinct(
// 'subReservation.identifier',
// {
// 'reservationFor.id': { $eq: eventId },
// 'subReservation.identifier': { $exists: true, $in: reservationIdentifiers }
// }
// )
// .setOptions({ maxTimeMS: MONGO_MAX_TIME_MS })
// .exec();
// debug('searchHolders: distinct.', doc);
// return doc;
// }
// public async getSize(params: Omit<IUnlockKey, 'holder' | 'offer'>) {
// const { eventId } = params;
// const aggregate = this.aggregateReservationModel.aggregate([
// {
// $match: {
// 'reservationFor.id': { $eq: eventId }
// }
// },
// {
// $project: {
// typeOf: 1,
// objectSize: { $bsonSize: '$$ROOT' }
// }
// }
// ]);
// return aggregate
// .option({ maxTimeMS: MONGO_MAX_TIME_MS })
// .exec();
// }
// public getCursor(conditions: FilterQuery<any>, projection: any) {
// return this.aggregateReservationModel.find(conditions, projection)
// .sort({ bookingTime: factory.sortType.Ascending })
// .cursor();
// }
docExists(params) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId } = params;
const doc = yield this.pendingReservationModel.findOne({ 'reservationFor.id': { $eq: eventId } }, { _id: 0, typeOf: 1 })
.setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.lean()
.exec();
return doc !== null;
});
}
deleteExpiredMany(params) {
return __awaiter(this, void 0, void 0, function* () {
const { expires } = params;
return this.pendingReservationModel.deleteMany({
expires: { $lte: expires.$lte }
})
.exec();
});
}
projectFields(params) {
return __awaiter(this, void 0, void 0, function* () {
const { limit, page } = params;
const filterQueries = PendingReservationRepo.CREATE_FILTER_QUERY(params);
const matchStage = (filterQueries.length > 0) ? { $match: { $and: filterQueries } } : undefined;
let limitStage;
let skipStage;
if (typeof limit === 'number' && limit > 0) {
const pageMustBePositive = (typeof page === 'number' && page > 0) ? page : 1;
skipStage = { $skip: limit * (pageMustBePositive - 1) };
limitStage = { $limit: limit };
}
const projectStage = {
$project: {
_id: 0,
typeOf: '$typeOf',
bookingTime: '$bookingTime',
dateCreated: '$dateCreated',
dateModified: '$dateModified',
expires: '$expires',
numSeats: '$numSeats',
reservationFor: '$reservationFor',
reservationNumber: '$reservationNumber',
subReservationCount: { $size: '$subReservation' }
}
};
const pipeline = [
...(matchStage !== undefined) ? [matchStage] : [],
{ $sort: { bookingTime: factory.sortType.Descending } },
...(skipStage !== undefined) ? [skipStage] : [],
...(limitStage !== undefined) ? [limitStage] : [],
projectStage
];
return this.pendingReservationModel.aggregate(pipeline)
.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
});
}
projectSubReservationByReservationNumber(params) {
return __awaiter(this, void 0, void 0, function* () {
const matchStage = {
$match: {
'project.id': { $eq: params.project.id.$eq },
reservationNumber: { $eq: params.reservationNumber.$eq }
}
};
const projectStage = {
$project: {
_id: 0,
identifier: '$subReservation.identifier',
index: `$reservationIndex`
}
};
const unwindStage = {
$unwind: {
path: '$subReservation',
includeArrayIndex: 'reservationIndex'
}
};
const pipeline = [
matchStage,
unwindStage,
projectStage
];
return this.pendingReservationModel.aggregate(pipeline)
.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
});
}
/**
* expiresを最新の情報に同期する
*/
syncEvent2expires(params) {
return __awaiter(this, void 0, void 0, function* () {
const { expires, reservationFor } = params;
if (!(expires instanceof Date)) {
throw new factory.errors.Argument('expires', 'must be Date');
}
return this.pendingReservationModel.updateMany({
'reservationFor.id': { $eq: reservationFor.id },
numSeats: { $gte: 1 } // numSeats:0についてはもはや不要なドキュメントなので除外
}, {
$set: { expires }
})
.exec();
});
}
aggregateNumSeats(params) {
return __awaiter(this, void 0, void 0, function* () {
const { bookingTime, dateCreated, reservationFor, limit } = params;
const bookingTimeLte = bookingTime === null || bookingTime === void 0 ? void 0 : bookingTime.$lte;
const dateCreatedGte = dateCreated === null || dateCreated === void 0 ? void 0 : dateCreated.$gte;
const dateCreatedLt = dateCreated === null || dateCreated === void 0 ? void 0 : dateCreated.$lt;
const matchStage = {
$match: Object.assign(Object.assign({ 'reservationFor.id': { $eq: reservationFor.id },
// 'subReservation.identifier': { $exists: true },
numSeats: { $gte: 1 } }, (bookingTimeLte instanceof Date) ? { bookingTime: { $lte: bookingTimeLte } } : undefined), (dateCreatedGte instanceof Date || dateCreatedLt instanceof Date)
? {
dateCreated: Object.assign(Object.assign({}, (dateCreatedGte instanceof Date) ? { $gte: dateCreatedGte } : undefined), (dateCreatedLt instanceof Date) ? { $lt: dateCreatedLt } : undefined)
}
: undefined)
};
let limitStage;
if (typeof limit === 'number' && limit > 0) {
limitStage = { $limit: limit };
}
const aggregate = this.pendingReservationModel.aggregate([
matchStage,
...(limitStage !== undefined)
? [limitStage]
: [],
{
$project: {
numSeats: '$numSeats'
}
},
{
$group: {
// tslint:disable-next-line:no-null-keyword
_id: null,
totalNumSeats: { $sum: '$numSeats' }
}
},
{
$project: {
_id: 0,
numSeats: '$totalNumSeats'
}
}
]);
const aggregations = yield aggregate.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
debug('countUnavailableOffers:', aggregations);
if (aggregations.length === 0) {
return 0;
}
return aggregations[0].numSeats;
});
}
// private async createIfNotExist(aggregateReservation: IAggregateReservation): Promise<void> {
// try {
// const { expires, project, reservationFor, typeOf } = aggregateReservation;
// await this.aggregateReservationModel.updateOne(
// {
// 'reservationFor.id': { $eq: aggregateReservation.reservationFor.id }
// },
// {
// $setOnInsert: {
// expires, project, reservationFor, typeOf,
// reservationCount: 0, reservationIds: []
// }
// },
// {
// upsert: true
// }
// )
// .exec();
// } catch (error) {
// let throwsError = true;
// if (await isMongoError(error)) {
// // すでに存在する場合ok
// if (error.code === MongoErrorCode.DuplicateKey) {
// throwsError = false;
// }
// }
// if (throwsError) {
// throw error;
// }
// }
// }
createReservationPackageIfPossible(reservationPackage) {
return __awaiter(this, void 0, void 0, function* () {
var _a, _b;
try {
const result = yield this.pendingReservationModel.insertMany(Object.assign(Object.assign({}, reservationPackage), { dateCreated: new Date() }), {
rawResult: true
});
const id = (_b = (_a = result.insertedIds) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.toHexString();
if (typeof id !== 'string') {
throw new factory.errors.Internal('pending reservationPackage not saved');
}
}
catch (error) {
if (yield (0, errorHandler_1.isMongoError)(error)) {
if (error.code === errorHandler_1.MongoErrorCode.DuplicateKey) {
throw new factory.errors.AlreadyInUse(factory.reservationType.EventReservation, ['ticketedSeat'], 'Already hold');
}
}
}
});
}
deleteReservationPackage(reservationPackage) {
return __awaiter(this, void 0, void 0, function* () {
const { reservationNumber } = reservationPackage;
yield this.pendingReservationModel.deleteOne({ reservationNumber: { $eq: reservationNumber } })
.exec();
});
}
// private async decreaseReservationCountByLockKey(
// lockKey: ILockKey
// ): Promise<void> {
// const { project, eventId, startDate, offers, hasTicketedSeat, holder } = lockKey;
// for (const offer of offers) {
// await this.decreaseReservationCount({
// project: { id: project.id },
// eventId,
// startDate,
// hasTicketedSeat,
// offer,
// holder
// });
// }
// }
// private async increaseReservationCount(
// aggregateReservation: IAggregateReservation,
// options: {
// maximumReservationCount?: number;
// }
// ): Promise<void> {
// const { maximumReservationCount } = options;
// const newReservationCount = aggregateReservation.reservationIds.length;
// let reservationCountLte: number | undefined;
// if (typeof maximumReservationCount === 'number') {
// reservationCountLte = maximumReservationCount - newReservationCount;
// if (reservationCountLte < 0) {
// throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
// }
// }
// const doc = await this.aggregateReservationModel.findOneAndUpdate(
// {
// 'reservationFor.id': { $eq: aggregateReservation.reservationFor.id },
// // 'reservations.identifier': { $nin: reservationIdentifiers }, // pendingReservationsで重複回避済の前提で実行される
// ...(typeof reservationCountLte === 'number')
// ? { reservationCount: { $lte: reservationCountLte } }
// : undefined
// // : { reservationCount: { $exists: true } } // 必ず条件内になるように
// },
// {
// $inc: { reservationCount: newReservationCount },
// $push: {
// reservationIds: { $each: aggregateReservation.reservationIds }
// },
// $set: { expires: aggregateReservation.expires }
// },
// {
// new: false, // trueである必要はない
// projection: { typeOf: 1 }
// }
// )
// .setOptions({ maxTimeMS: MONGO_MAX_TIME_MS })
// .lean<Pick<IAggregateReservation, 'typeOf'>>()
// .exec();
// if (doc === null) {
// throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
// }
// }
deleteReservationIfExists(params) {
return __awaiter(this, void 0, void 0, function* () {
const { eventId, offer, hasTicketedSeat, holder } = params;
const reservationIdentifier = PendingReservationRepo.offer2identifier(offer, hasTicketedSeat);
yield this.pendingReservationModel.updateOne({
'reservationFor.id': { $eq: eventId },
reservationNumber: { $eq: holder },
'subReservation.identifier': { $exists: true, $eq: reservationIdentifier }
}, {
$inc: { numSeats: -1 },
$pull: { subReservation: { identifier: reservationIdentifier } },
$set: { dateModified: new Date() }
})
.setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
.exec();
// debug('unlocked', pendingReservationDoc);
});
}
}
exports.PendingReservationRepo = PendingReservationRepo;