respond-framework
Version:
create as fast you think
159 lines (155 loc) • 4.04 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _dateStringToDate = require("../utils/dateStringToDate.js");
var _toFromObjectIds = require("../helpers/toFromObjectIds.js");
const join = ({
...spec
}, parentSelector, collection) => {
spec.localField = (0, _toFromObjectIds.resolveId)(spec.localField);
if (spec.inner) {
return joinInner(spec, parentSelector, collection);
}
return joinSum(spec, parentSelector);
};
var _default = exports.default = join;
const joinSum = (spec, parentSelector) => {
const {
name,
from,
foreignField,
localField = '_id',
$sum = 1
} = spec;
const $match = createDateRangeMatch(spec);
return [{
$lookup: {
from: from,
// eg: user
localField,
// eg: _id
foreignField,
// eg: cityId -- similar to: db.user.findMany({ cityId: city.id })
as: name,
// eg: userCount (this is the name we give to the array containing joined docs, eg: city.userCount, which can be filtered by the parentSelector -- the real values we want are at city.userCount.0.userCount)
pipeline: [...($match ? [{
$match
}] : []),
// additional filter on joined collection, eg { pro: true } || { pro: true, createdDate: { $gte: Date } }
{
$group: {
_id: null,
[name]: {
// eg: userCount
$sum // $sum: 1 is the equivalent of $count helper
}
}
}, {
$project: {
_id: 0
}
}]
}
}, {
$unwind: {
path: '$' + name,
preserveNullAndEmptyArrays: true
}
},
// preserveNullAndEmptyArrays keeps parent rows that have 0 values for their $sums
{
$replaceRoot: {
newRoot: {
$mergeObjects: ['$$ROOT', {
[name]: 0
}, '$' + name] // merge $sum object with main doc, default to 0 if empty values found
}
}
}, ...(!parentSelector[name] ? [] : [{
$match: {
[name]: parentSelector[name]
}
}]) // filter by summed field itself
];
};
const joinInner = (spec, parentSelector, collection) => {
const {
name,
from,
foreignField = 'userId',
localField = '_id',
filterField: f
} = spec;
const sel = parentSelector[name]; // `name` is not a filter client is currently passing
const hasSelector = sel !== undefined;
if (!hasSelector && f) return []; // when filterField is provided, only explicitly filter if selector sent from client
const $match = spec.$match ? hasSelector && f ? {
...spec.$match,
[f]: sel
} : spec.$match : hasSelector && f ? {
[f]: sel
} : undefined; // if matching sel sent from client, honor it regardless of whether filterField provided
return [{
$lookup: {
from: from,
// eg: user
localField,
// eg: _id
foreignField,
// eg: cityId -- similar to: db.user.findMany({ cityId: city.id }
as: name,
pipeline: $match ? [{
$match: (0, _toFromObjectIds.toObjectIdsSelector)($match)
}] : undefined
}
}, {
$match: {
[name]: {
$ne: []
} // join must have some, eg, users in post.users to be a match
}
}, {
$unset: [name]
}];
};
const createDateRangeMatch = ({
startDate,
endDate,
$match
}) => {
// filter joined collection aggregate counts based on a date range
if (startDate && endDate) {
const $and = $match?.$and || [];
$and.push({
createdAt: {
$gte: (0, _dateStringToDate.default)(startDate)
}
});
$and.push({
createdAt: {
$lt: (0, _dateStringToDate.default)(endDate)
}
});
return {
...$match,
$and
};
} else if (startDate) {
return {
...$match,
createdAt: {
$gte: (0, _dateStringToDate.default)(startDate)
}
};
} else if (endDate) {
return {
...$match,
createdAt: {
$lt: (0, _dateStringToDate.default)(endDate)
}
};
}
return $match;
};