u-mongoose-expressapi
Version:
Common functions for Express and Mongoose API
387 lines (348 loc) • 9.95 kB
JavaScript
const mongoose = require('mongoose');
const { parseDate } = require('../helpers/reuseFunctions');
const moment = require('moment');
const lookupUnwindStage = (from, localField, foreignField, as) => {
return [
{
$lookup: {
from: from,
localField: localField,
foreignField: foreignField,
as: as
}
},
{
$unwind: {
path: `$${as}`,
preserveNullAndEmptyArrays: true
}
}
];
};
// const lookupUnwindStage = (from, localField, foreignField, as, projectFields = null) => {
// const stage = {
// $lookup: {
// from,
// let: { localVal: `$${localField}` },
// pipeline: [
// {
// $match: {
// $expr: {
// $eq: [`$${foreignField}`, `$$localVal`]
// }
// }
// }
// ],
// as
// }
// };
// if (projectFields) {
// stage.$lookup.pipeline.push({ $project: projectFields });
// }
// return [
// stage,
// {
// $unwind: {
// path: `$${as}`,
// preserveNullAndEmptyArrays: true
// }
// }
// ];
// };
const lookupStage = (from, localField, foreignField, as) => {
return {
$lookup: {
from: from,
localField: localField,
foreignField: foreignField,
as: as
}
};
};
const generateConditions = (filters, numericSearchTerms) => {
return (
filters &&
filters?.length > 0 &&
filters?.map(column => {
const condition = {};
// handle date column search filter
if (
column.id === 'createdAt' ||
column.id === 'updatedAt' ||
column.id === 'date' ||
column.id === 'visaExpiryDate'
) {
if (column.id === 'visaExpiryDate') {
const date = parseDate(column.value);
condition[column.id] = {
$lt: moment(date).endOf('day').toDate()
};
} else {
// Handle as date
const date = parseDate(column.value);
condition[column.id] = {
$gte: date,
$lte: moment(date).endOf('day').toDate() // Cover the entire day
};
}
} else if (
!isNaN(Number(column.value)) &&
numericSearchTerms &&
numericSearchTerms.includes(column.id)
) {
// Handle as numeric
condition[column.id] = Number(column.value);
} else if (mongoose.Types.ObjectId.isValid(column.value)) {
condition[column.id] = new mongoose.Types.ObjectId(column.value);
} else if (Array.isArray(column.value)) {
condition[column.id] = { $in: column.value };
} else {
// Handle as regex (string)
condition[column.id] = { $regex: column.value, $options: 'i' };
}
return condition;
})
);
};
const generateConditionsNotEqual = (filters) => {
return (
filters &&
filters?.length > 0 &&
filters?.map(column => {
const condition = {};
condition[column.id] = { $ne: column.value };
return condition;
})
);
};
const generateConditionsNotExist = (filters) => {
return (
filters &&
filters?.length > 0 &&
filters?.map(column => {
const condition = {};
condition[column.id] = { $exists: column.value };
return condition;
})
);
};
const generateConditionsAndOrNotMixed = (filters) => {
if (!filters || filters.length === 0) return [];
const orConditions = [];
filters.forEach(column => {
const isBoolean = typeof column.value === 'boolean';
if (isBoolean) {
// Only exact match for boolean fields
orConditions.push({ [column.id]: column.value });
} else {
// Match value or field not existing
orConditions.push({ [column.id]: column.value });
orConditions.push({ [column.id]: { $exists: false } });
}
});
return [{ $or: orConditions }];
};
const dateStartToEnd = (
fromDate = null,
toDate = null,
matchStage,
fieldDate = 'createdAt'
) => {
const fromDateParsed = parseDate(fromDate);
const toDateParsed = parseDate(toDate);
matchStage.$and = matchStage.$and || [];
console.log(fromDateParsed, toDateParsed, fieldDate);
if (fromDateParsed && toDateParsed) {
// If both fromDate and toDate are provided, filter by the date range
matchStage.$and.push(
{ [fieldDate]: { $gte: moment.utc(fromDateParsed).startOf('day').toDate() } },
{ [fieldDate]: { $lte: moment.utc(toDateParsed).endOf('day').toDate() } }
);
} else if (fromDateParsed) {
// If only fromDate is provided, filter for dates equal to or greater than fromDate
matchStage.$and.push({
[fieldDate]: {
$gte: moment.utc(fromDateParsed).startOf('day').toDate()
}
});
} else if (toDateParsed) {
// If only toDate is provided, filter for dates equal to or less than toDate
matchStage.$and.push({
[fieldDate]: {
$lte: moment.utc(toDateParsed).endOf('day').toDate()
}
});
}
};
const checkBranch = (matchStage, branch) => {
matchStage.$and = matchStage.$and || [];
if (mongoose.Types.ObjectId.isValid(branch)) {
matchStage.$and.push({
$or: [
{ branch: new mongoose.Types.ObjectId(branch) },
{ shareBranch: new mongoose.Types.ObjectId(branch) }
]
});
} else {
console.log('Branch Is Invalid');
}
};
const createAggregationPipeline = ({
skip = 0,
limit = 1000000,
searchTerm = '',
columnFilters = [],
columnFiltersOr = [],
columnFiltersNotExist = [],
columnFiltersAndOrMixed = [],
columnFiltersNotEqual = [],
deleted = 'false',
sortField = 'updatedAt',
sortOrder = -1,
ids = [],
customParams,
branch = '65c336d6355c2fc50b106bd0' // it is fake id, without branch id it does not work
}) => {
const {
projectionFields,
searchTerms,
numericSearchTerms,
fromDate,
toDate,
fieldDate
} = customParams;
// if (columnFilters) {
// columnFilters = columnFilters.map(column => {
// return column.id === 'by' ? { ...column, id: 'agent.fullName' } : column;
// });
// }
if (columnFilters) {
const byFound = columnFilters.map(column => {
// return column.id === 'by' ? { ...column, id: 'agent.fullName' } : column;
if (column.id === 'by') {
return column;
}
}).filter(column => column !== undefined);
if (byFound?.length > 0) {
// console.log("entered byFound", byFound);
columnFilters = columnFilters.filter(item => item.id !== 'by');
columnFiltersOr.push(
{
id: 'agent.fullName',
value: byFound[0].value
},
{
id: 'client.fullName',
value: byFound[0].value
},
{
id: 'company.companyName',
value: byFound[0].value
}
)
}
}
const lookup = customParams.lookup ? customParams.lookup : [];
const searching = field => {
if (mongoose.Types.ObjectId.isValid(searchTerm)) {
return { [field]: new mongoose.Types.ObjectId(searchTerm) };
}
return {
[field]: { $regex: searchTerm, $options: 'i' }
};
};
let matchStage = {};
matchStage = {
...matchStage,
...(searchTerm && {
$or: [
...(numericSearchTerms && numericSearchTerms.length > 0
? numericSearchTerms.map(search => {
const condition = {};
condition[search] = Number(searchTerm);
return condition;
})
: []),
...(searchTerms.length > 0
? searchTerms.map(search => {
return searching(search);
})
: [])
]
}),
deleted: deleted
};
if (!matchStage.$and) matchStage.$and = [];
if (columnFilters && columnFilters.length) {
matchStage.$and.push(...generateConditions(columnFilters, numericSearchTerms));
}
if (columnFiltersNotEqual && columnFiltersNotEqual.length) {
matchStage.$and.push(...generateConditionsNotEqual(columnFiltersNotEqual));
}
if (columnFiltersNotExist && columnFiltersNotExist.length) {
matchStage.$and.push(...generateConditionsNotExist(columnFiltersNotExist));
}
if (columnFiltersOr && columnFiltersOr.length) {
matchStage.$or = generateConditions(columnFiltersOr, numericSearchTerms);
}
if (columnFiltersAndOrMixed && columnFiltersAndOrMixed.length) {
matchStage.$and.push(...generateConditionsAndOrNotMixed(columnFiltersAndOrMixed));
}
//
if (fromDate || toDate) {
dateStartToEnd(fromDate, toDate, matchStage, fieldDate);
}
if (branch) {
checkBranch(matchStage, branch);
}
// data
let dataPipeline = [];
// console.log('matchstage', matchStage);
if (lookup) {
dataPipeline = dataPipeline.concat(...lookup);
}
let countPipeline = [{ $count: 'total' }];
dataPipeline = dataPipeline.concat([
{ $match: matchStage },
{
$facet: {
totalRecords: countPipeline,
extra: [{ $group: { _id: null, startDate: { $min: '$updatedAt' } } }],
data: [
{
$match: {
_id:
ids.length > 0
? { $in: ids.map(id => new mongoose.Types.ObjectId(id)) }
: { $exists: true }
}
},
{
$project: projectionFields
},
{ $sort: { [sortField]: sortOrder } },
{ $skip: skip },
{ $limit: limit }
]
}
},
{ $unwind: '$totalRecords' },
{ $unwind: '$extra' },
{
$project: {
total: '$totalRecords.total',
data: '$data',
extra: '$extra'
}
}
]);
return dataPipeline;
};
module.exports = {
createAggregationPipeline,
lookupStage,
lookupUnwindStage,
dateStartToEnd,
generateConditions,
checkBranch
};