updated filtering code

This commit is contained in:
2025-05-02 16:27:52 +05:30
parent 4153bd2412
commit c84cd055d4
10 changed files with 160 additions and 211 deletions

View File

@@ -1,12 +1,12 @@
import mongoose from "mongoose";
import app from "./server";
import mongoose from 'mongoose';
import app from './server';
(async () => {
const PORT = parseInt(process.env.PORT ?? "8000");
const DB_URI = process.env.DB_URI ?? "";
const PORT = parseInt(process.env.PORT ?? '8000');
const DB_URI = process.env.DB_URI ?? '';
await mongoose.connect(DB_URI);
await app.listen({ port: PORT, host: "0.0.0.0" });
await app.listen({ port: PORT, host: '0.0.0.0' });
})().catch((err) => {
console.log(err);
process.exit(1);

View File

@@ -43,11 +43,11 @@ export async function listNotifications(
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, notificationFields);
const filterObj = getFilterObject(params, notificationFields);
const filterObj = getFilterObject(params);
const pipeline: any = [
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
];

View File

@@ -1,12 +1,12 @@
import { getFilterObject, getSortObject, PageQueryParams } from "../pagination";
import { ChangeEvent, dbEvents } from "../realtime";
import { generateId } from "../utils/id";
import { getFilterObject, getSortObject, PageQueryParams } from '../pagination';
import { ChangeEvent, dbEvents } from '../realtime';
import { generateId } from '../utils/id';
import {
CreateOrgInput,
orgFields,
orgModel,
UpdateOrgInput,
} from "./organization.schema";
} from './organization.schema';
export async function createOrg(input: CreateOrgInput, tenantId: string) {
const org = await orgModel.create({
@@ -17,13 +17,13 @@ export async function createOrg(input: CreateOrgInput, tenantId: string) {
});
dbEvents.emit(
"change",
'change',
{
type: "insert",
collection: "orgs",
type: 'insert',
collection: 'orgs',
document: org,
} as ChangeEvent,
["org:read"]
['org:read']
);
return org;
@@ -39,13 +39,13 @@ export async function listOrgs(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, orgFields);
const filterObj = getFilterObject(params, orgFields);
const filterObj = getFilterObject(params);
const orgs = await orgModel.aggregate([
{ $match: { $and: [{ tenantId: tenantId }, ...filterObj] } },
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
{
$facet: {
metadata: [{ $count: "count" }],
metadata: [{ $count: 'count' }],
data: [
{ $sort: sortObj },
{ $skip: (page - 1) * pageSize },
@@ -83,13 +83,13 @@ export async function updateOrg(
if (updateOrgResult) {
dbEvents.emit(
"change",
'change',
{
type: "update",
collection: "orgs",
type: 'update',
collection: 'orgs',
document: updateOrgResult,
} as ChangeEvent,
["org:read"]
['org:read']
);
}
@@ -103,15 +103,15 @@ export async function deleteOrg(orgId: string, tenantId: string) {
if (res.deletedCount > 0) {
dbEvents.emit(
"change",
'change',
{
type: "delete",
collection: "orgs",
type: 'delete',
collection: 'orgs',
document: {
pid: orgId,
},
} as ChangeEvent,
["org:read"]
['org:read']
);
}
@@ -122,15 +122,15 @@ export async function searchOrgs(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, orgFields);
const filterObj = getFilterObject(params, orgFields);
const filterObj = getFilterObject(params);
if (!params.searchToken)
return { orgs: [], metadata: { count: 0, page, pageSize } };
const regex = new RegExp(params.searchToken, "i");
const regex = new RegExp(params.searchToken, 'i');
const orgs = await orgModel.aggregate([
{ $match: { $and: [{ tenantId: tenantId }, ...filterObj] } },
{ $match: { $and: [{ tenantId: tenantId }, { ...filterObj }] } },
{
$match: {
$or: [{ name: { $regex: regex } }, { domain: { $regex: regex } }],
@@ -138,7 +138,7 @@ export async function searchOrgs(params: PageQueryParams, tenantId: string) {
},
{
$facet: {
metadata: [{ $count: "count" }],
metadata: [{ $count: 'count' }],
data: [
{ $sort: sortObj },
{ $skip: (page - 1) * pageSize },

View File

@@ -1,5 +1,5 @@
import { z } from "zod";
import { parse } from "./utils/queryParser";
import { z } from 'zod';
import { parse } from './utils/queryParser';
export const pageMetadata = z.object({
count: z.number(),
@@ -23,30 +23,23 @@ export function getSortObject(
) {
const sortObj: Record<string, 1 | -1> = {};
if (params.sort && params.sort != "") {
const sortOptions = params.sort.split(",");
if (params.sort && params.sort != '') {
const sortOptions = params.sort.split(',');
sortOptions.forEach((item) => {
const order = item.startsWith("-") ? -1 : 1;
const field = item.replace("-", "").trim();
const order = item.startsWith('-') ? -1 : 1;
const field = item.replace('-', '').trim();
if (validFields.includes(field)) sortObj[field] = order;
});
}
if (Object.keys(sortObj).length == 0) sortObj["createdAt"] = -1;
if (Object.keys(sortObj).length == 0) sortObj['createdAt'] = -1;
return sortObj;
}
export function getFilterObject(
params: PageQueryParams,
validFields: Array<string>
) {
const filterObj: Array<{}> = [];
if (params.filter && params.filter != "") {
filterObj.push(...parse(params.filter, validFields));
export function getFilterObject(params: PageQueryParams) {
if (params.filter && params.filter != '') {
return parse(params.filter.split(','));
}
return filterObj;
}

View File

@@ -59,11 +59,11 @@ export async function listPermits(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, permitFields);
const filterObj = getFilterObject(params, permitFields);
const filterObj = getFilterObject(params);
const permitsList = await permitModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
{
$lookup: {
@@ -205,7 +205,7 @@ export async function searchPermit(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, permitFields);
const filterObj = getFilterObject(params, permitFields);
const filterObj = getFilterObject(params);
if (!params.searchToken)
return { permits: [], metadata: { count: 0, page, pageSize } };
@@ -214,7 +214,7 @@ export async function searchPermit(params: PageQueryParams, tenantId: string) {
const permitsList = await permitModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
{
$match: {

View File

@@ -40,11 +40,11 @@ export async function listProcessedPermits(
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, processedFields);
const filterObj = getFilterObject(params, processedFields);
const filterObj = getFilterObject(params);
const pipeline: any = [
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
];

View File

@@ -4,13 +4,13 @@ import {
rtsModel,
UpdateRtsInput,
UploadRtsInput,
} from "./rts.schema";
import { AuthenticatedUser } from "../auth";
import { generateId } from "../utils/id";
import { getFilterObject, getSortObject, PageQueryParams } from "../pagination";
import { getUser } from "../user/user.service";
import { orgModel } from "../organization/organization.schema";
import { userModel } from "../user/user.schema";
} from './rts.schema';
import { AuthenticatedUser } from '../auth';
import { generateId } from '../utils/id';
import { getFilterObject, getSortObject, PageQueryParams } from '../pagination';
import { getUser } from '../user/user.service';
import { orgModel } from '../organization/organization.schema';
import { userModel } from '../user/user.schema';
export async function createRts(
input: CreateRtsInput,
@@ -53,43 +53,43 @@ export async function createRts(
export async function getRts(id: string, tenantId: string) {
return await rtsModel
.findOne({ pid: id, tenantId: tenantId })
.populate({ path: "county", select: "pid name avatar" })
.populate({ path: "client", select: "pid name avatar" })
.populate({ path: "createdBy", select: "pid name avatar" });
.populate({ path: 'county', select: 'pid name avatar' })
.populate({ path: 'client', select: 'pid name avatar' })
.populate({ path: 'createdBy', select: 'pid name avatar' });
}
export async function listRts(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, rtsFields);
const filterObj = getFilterObject(params, rtsFields);
const filterObj = getFilterObject(params);
const rtsList = await rtsModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
{
$lookup: {
from: "organizations",
localField: "county",
foreignField: "_id",
as: "countyRec",
from: 'organizations',
localField: 'county',
foreignField: '_id',
as: 'countyRec',
},
},
{
$lookup: {
from: "organizations",
localField: "client",
foreignField: "_id",
as: "clientRec",
from: 'organizations',
localField: 'client',
foreignField: '_id',
as: 'clientRec',
},
},
{
$lookup: {
from: "users",
localField: "createdBy",
foreignField: "_id",
as: "createdRec",
from: 'users',
localField: 'createdBy',
foreignField: '_id',
as: 'createdRec',
},
},
{
@@ -101,36 +101,36 @@ export async function listRts(params: PageQueryParams, tenantId: string) {
createdAt: 1,
county: {
$let: {
vars: { county: { $arrayElemAt: ["$countyRec", 0] } },
vars: { county: { $arrayElemAt: ['$countyRec', 0] } },
in: {
_id: "$$county._id",
pid: "$$county.pid",
name: "$$county.name",
type: "$$county.type",
avatar: "$$county.avatar",
_id: '$$county._id',
pid: '$$county.pid',
name: '$$county.name',
type: '$$county.type',
avatar: '$$county.avatar',
},
},
},
client: {
$let: {
vars: { client: { $arrayElemAt: ["$clientRec", 0] } },
vars: { client: { $arrayElemAt: ['$clientRec', 0] } },
in: {
_id: "$$client._id",
pid: "$$client.pid",
name: "$$client.name",
type: "$$client.type",
avatar: "$$client.avatar",
_id: '$$client._id',
pid: '$$client.pid',
name: '$$client.name',
type: '$$client.type',
avatar: '$$client.avatar',
},
},
},
createdBy: {
$let: {
vars: { created: { $arrayElemAt: ["$createdRec", 0] } },
vars: { created: { $arrayElemAt: ['$createdRec', 0] } },
in: {
_id: "$$created._id",
pid: "$$created.pid",
name: "$$created.name",
avatar: "$$created.avatar",
_id: '$$created._id',
pid: '$$created.pid',
name: '$$created.name',
avatar: '$$created.avatar',
},
},
},
@@ -138,7 +138,7 @@ export async function listRts(params: PageQueryParams, tenantId: string) {
},
{
$facet: {
metadata: [{ $count: "count" }],
metadata: [{ $count: 'count' }],
data: [
{ $sort: sortObj },
{ $skip: (page - 1) * pageSize },
@@ -168,9 +168,9 @@ export async function updateRts(
) {
const updatedRts = await rtsModel
.findOneAndUpdate({ pid: id, tenantId: tenantId }, input, { new: true })
.populate({ path: "createdBy", select: "pid name avatar" })
.populate({ path: "county", select: "pid name avatar" })
.populate({ path: "client", select: "pid name avatar" });
.populate({ path: 'createdBy', select: 'pid name avatar' })
.populate({ path: 'county', select: 'pid name avatar' })
.populate({ path: 'client', select: 'pid name avatar' });
return updatedRts;
}
@@ -202,12 +202,12 @@ export async function getUniqueValuesRTS(field: string, tenenatId: string) {
let values = await rtsModel.distinct(field, { tenantId: tenenatId });
let matchedValues = [];
if (field === "county") {
matchedValues = await orgModel.find().where("_id").in(values).exec();
} else if (field === "client") {
matchedValues = await orgModel.find().where("_id").in(values).exec();
} else if (field === "createdBy") {
matchedValues = await userModel.find().where("_id").in(values).exec();
if (field === 'county') {
matchedValues = await orgModel.find().where('_id').in(values).exec();
} else if (field === 'client') {
matchedValues = await orgModel.find().where('_id').in(values).exec();
} else if (field === 'createdBy') {
matchedValues = await userModel.find().where('_id').in(values).exec();
}
if (matchedValues.length > 0) {

View File

@@ -59,11 +59,11 @@ export async function listTasks(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, taskFields);
const filterObj = getFilterObject(params, taskFields);
const filterObj = getFilterObject(params);
const taskList = await taskModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
{
$lookup: {
@@ -145,13 +145,13 @@ export async function searchTasks(params: PageQueryParams, tenantId: string) {
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, taskFields);
const filterObj = getFilterObject(params, taskFields);
const filterObj = getFilterObject(params);
const regex = new RegExp(params.searchToken, 'i');
const taskList = await taskModel.aggregate([
{
$match: { $and: [{ tenantId: tenantId }, ...filterObj] },
$match: { $and: [{ tenantId: tenantId }, { ...filterObj }] },
},
{
$match: {

View File

@@ -1,111 +1,67 @@
import mongoose from 'mongoose';
type ParsedQuery = Array<
Record<string, Record<'$eq' | '$ne' | '$in' | '$nin', string | Date | Object>>
>;
type MongoFilter = Record<string, any>;
function convertValue(value: any) {
if (mongoose.Types.ObjectId.isValid(value)) {
return mongoose.Types.ObjectId.createFromHexString(value);
function formulaToMongoFilter(formula: string): MongoFilter {
// Split the formula into parts
const operatorMatch = formula.match(/([^!<>=]+)([!<>=]+)(.+)/);
if (!operatorMatch) {
throw new Error(`Invalid formula format: ${formula}`);
}
const [, field, operator, value] = operatorMatch;
const trimmedField = field.trim();
const trimmedValue = value.trim();
if (trimmedField === 'tenantId') return {};
// Convert value to appropriate type (date or number if possible)
let parsedValue: any = trimmedValue;
if (/^\d{4}-\d{2}-\d{2}$/.test(trimmedValue)) {
// Check if it's a date (ISO format)
parsedValue = new Date(trimmedValue);
} else if (!isNaN(Number(trimmedValue))) {
// Check if it's a number
parsedValue = Number(trimmedValue);
} else if (
!isNaN(parseInt(value)) &&
parseInt(value).toString().length == value.toString().length
typeof parsedValue === 'string' &&
mongoose.Types.ObjectId.isValid(parsedValue)
) {
return parseInt(value);
} else if (!isNaN(Date.parse(value))) {
return new Date(value);
} else if (value === 'true') {
return true;
} else if (value === 'false') {
return false;
} else {
return value;
parsedValue = new mongoose.Types.ObjectId(parsedValue);
} else if (parsedValue === 'true') {
parsedValue = true;
} else if (parsedValue === 'false') {
parsedValue = false;
} else if (parsedValue === 'null') {
parsedValue = null;
}
// Convert operator to MongoDB operator
switch (operator) {
case '=':
return { [trimmedField]: { $eq: parsedValue } };
case '!=':
return { [trimmedField]: { $ne: parsedValue } };
case '>':
return { [trimmedField]: { $gt: parsedValue } };
case '<':
return { [trimmedField]: { $lt: parsedValue } };
case '>=':
return { [trimmedField]: { $gte: parsedValue } };
case '<=':
return { [trimmedField]: { $lte: parsedValue } };
default:
throw new Error(`Unsupported operator: ${operator}`);
}
}
export function parse(query: string, validFields: Array<string>): ParsedQuery {
const result = [];
export function parse(formulas: string[]): MongoFilter {
const parsedQuery: MongoFilter = formulas.reduce((filter, formula) => {
const newFilter = formulaToMongoFilter(formula);
return { ...filter, ...newFilter };
}, {});
let currentStage = 'field';
let token = '';
let field = '';
let op = '';
let value = '';
let valueArr = [];
for (let i = 0; i < query.length; i++) {
let char = query[i];
if (currentStage === 'field') {
if (char === '=' || char === '!') {
field = token;
token = '';
currentStage = 'value';
if (char === '=') {
op = '$eq';
} else {
op = '$ne';
i++;
}
continue;
}
token += char;
}
if (currentStage === 'value') {
if (char === '[') {
currentStage = 'valueArr';
continue;
}
if (char === ',' || i == query.length - 1) {
if (i == query.length - 1) token += char;
value = token;
result.push({ [field]: { [op]: convertValue(value) } });
token = '';
field = '';
op = '';
currentStage = 'field';
continue;
}
token += char;
}
if (currentStage === 'valueArr') {
if (char === ',') {
valueArr.push(convertValue(token));
token = '';
continue;
}
if (char === ']') {
valueArr.push(convertValue(token));
result.push({
[field]: { [op === '$eq' ? '$in' : '$nin']: valueArr },
});
token = '';
field = '';
op = '';
valueArr = [];
currentStage = 'field';
i++;
continue;
}
token += char;
}
}
console.log(result);
return result;
return parsedQuery;
}

View File

@@ -33,13 +33,13 @@ export async function listViews(
const page = params.page || 1;
const pageSize = params.pageSize || 10;
const sortObj = getSortObject(params, viewFields);
const filterObj = getFilterObject(params, viewFields);
const filterObj = getFilterObject(params);
return await viewModel.find({
$and: [
{ tenantId: user.tenantId },
{ createdBy: user.userId },
...filterObj,
{ ...filterObj },
],
});
}