Skip to content

Commit

Permalink
Merge pull request #11 from gang-of-fork/0.0.10
Browse files Browse the repository at this point in the history
fix order of pagination and search
  • Loading branch information
lumaghg authored Apr 23, 2024
2 parents 3cb488a + a41dcbf commit bd4a60b
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 134 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "odatafy-mongodb",
"version": "0.0.10",
"version": "0.0.11",
"description": "convert oData requests through odatafy to MongoDB queries ",
"main": "./dist/index.js",
"keywords": [
Expand Down
292 changes: 159 additions & 133 deletions src/mongodbGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,66 @@
import url from 'url';
import url from "url";

import { generateMatchFromFilterExpr } from './filterGenerator';
import { generateLimitFromTopExpr } from './limitGenerator';
import { generateSkipFromSkipExpr } from './skipGenerator';
import { generateSortFromOrderbyExpr } from './sortGenerator';
import { generateProjectFromSelectExpr } from './selectGenerator';
import { generateLookupFromExpand, CollectionMap } from './lookupGenerator';
import { generateComputeStageFromComputedExpr } from './computeGenerator';
import { generateSearchFromSearchExpr } from './searchGenerator';
import { generateMatchFromFilterExpr } from "./filterGenerator";
import { generateLimitFromTopExpr } from "./limitGenerator";
import { generateSkipFromSkipExpr } from "./skipGenerator";
import { generateSortFromOrderbyExpr } from "./sortGenerator";
import { generateProjectFromSelectExpr } from "./selectGenerator";
import { generateLookupFromExpand, CollectionMap } from "./lookupGenerator";
import { generateComputeStageFromComputedExpr } from "./computeGenerator";
import { generateSearchFromSearchExpr } from "./searchGenerator";

import { oDataParameters } from 'odatafy-parser';
import { Document } from 'mongodb';
import { oDataParameters } from "odatafy-parser";
import { Document } from "mongodb";

export type MongoDBODatafyOpts = {
expandMapping?: CollectionMap,
returnEmptyPipeline?: boolean,
regexSearchFields?: string[],
returnDataCountQuery?: boolean,
returnCountOnly?: boolean
}
expandMapping?: CollectionMap;
returnEmptyPipeline?: boolean;
regexSearchFields?: string[];
returnDataCountQuery?: boolean;
returnCountOnly?: boolean;
};

/**
* Get MongoDB aggregation query from a given url can be obtained by nodes req.url
* @param oDataUrl - the url format ?param=value
* @param opts options for getting the url
* @returns MongoDB aggregation query
*/
export function getQueryFromUrl(oDataUrl: string, opts?: MongoDBODatafyOpts): Document[] {
const query = url.parse(oDataUrl, true).query;
const validParams = ['filter', 'orderby', 'skip', 'top', 'expand', 'compute', 'select', 'search'];
const params = Object.keys(query);

let parseParameters: oDataParameters = {}

validParams.forEach((param: string) => {
//check if url
if (params.includes(param) && params.includes('$' + param)) {
throw new Error(`Malformed oData url, cannot contain param: ${param} and param: $${param}`)
}

if (params.includes(param) || params.includes('$' + param)) {
parseParameters[param as keyof oDataParameters] = query[(params.includes(param) ? param : '$' + param)] as string
}
});

return getQuery(parseParameters, opts);
export function getQueryFromUrl(
oDataUrl: string,
opts?: MongoDBODatafyOpts
): Document[] {
const query = url.parse(oDataUrl, true).query;
const validParams = [
"filter",
"orderby",
"skip",
"top",
"expand",
"compute",
"select",
"search",
];
const params = Object.keys(query);

let parseParameters: oDataParameters = {};

validParams.forEach((param: string) => {
//check if url
if (params.includes(param) && params.includes("$" + param)) {
throw new Error(
`Malformed oData url, cannot contain param: ${param} and param: $${param}`
);
}

if (params.includes(param) || params.includes("$" + param)) {
parseParameters[param as keyof oDataParameters] = query[
params.includes(param) ? param : "$" + param
] as string;
}
});

return getQuery(parseParameters, opts);
}

/**
Expand All @@ -53,100 +69,110 @@ export function getQueryFromUrl(oDataUrl: string, opts?: MongoDBODatafyOpts): Do
* @param opts options for getting the url
* @returns MongoDB aggregaion pipeline
*/
export function getQuery(parameters: oDataParameters, opts?: MongoDBODatafyOpts): Document[] {
const pipeline: Document[] = [];
let countPipeline: Document[] = [];

if (parameters.expand) {
pipeline.push(...generateLookupFromExpand(parameters.expand, opts?.expandMapping? opts.expandMapping: {}));
}

if (parameters.compute) {
pipeline.push(generateComputeStageFromComputedExpr(parameters.compute));
}

if (parameters.filter) {
pipeline.push(generateMatchFromFilterExpr(parameters.filter));
}

/* copy to count query */
if(opts?.returnDataCountQuery || opts?.returnCountOnly) {
countPipeline = [...pipeline];
}


if (parameters.orderby) {
pipeline.push(generateSortFromOrderbyExpr(parameters.orderby));
}

if (parameters.skip) {
pipeline.push(generateSkipFromSkipExpr(parameters.skip));
}

if (parameters.top) {
pipeline.push(generateLimitFromTopExpr(parameters.top));
}

if(parameters.select) {
pipeline.push(generateProjectFromSelectExpr(parameters.select));
}

if(parameters.search) {
const searchExpr = generateSearchFromSearchExpr(parameters.search, opts?.regexSearchFields);

pipeline.push(searchExpr);
countPipeline.push(searchExpr);
}

//add default steps if pipline must not be empty - i.e. in mongoose an empty pipeline returns an error
if(!opts?.returnEmptyPipeline && pipeline.length == 0) {
pipeline.push(
{
$addFields: {
odatafyMongoDBTempField: ""
}
},
{
$project: {
odatafyMongoDBTempField: 0
}
}
)

countPipeline = [...pipeline];
}

countPipeline.push({
$count: "count"
});

if(opts?.returnCountOnly) {
return countPipeline
}

if(opts?.returnDataCountQuery) {
return [
{
"$facet": {
"data": pipeline,
"countTmp": countPipeline
}
},
{
"$addFields": {
"countTmp2": {
"$first": "$countTmp"
}
}
},
{
"$project": {
"data": "$data",
"count": "$countTmp2.count"
}
}
]
}

return pipeline;
}
export function getQuery(
parameters: oDataParameters,
opts?: MongoDBODatafyOpts
): Document[] {
const pipeline: Document[] = [];
let countPipeline: Document[] = [];

if (parameters.expand) {
pipeline.push(
...generateLookupFromExpand(
parameters.expand,
opts?.expandMapping ? opts.expandMapping : {}
)
);
}

if (parameters.compute) {
pipeline.push(generateComputeStageFromComputedExpr(parameters.compute));
}

if (parameters.filter) {
pipeline.push(generateMatchFromFilterExpr(parameters.filter));
}

/* copy to count query */
if (opts?.returnDataCountQuery || opts?.returnCountOnly) {
countPipeline = [...pipeline];
}

if (parameters.search) {
const searchExpr = generateSearchFromSearchExpr(
parameters.search,
opts?.regexSearchFields
);

pipeline.push(searchExpr);
countPipeline.push(searchExpr);
}

if (parameters.skip) {
pipeline.push(generateSkipFromSkipExpr(parameters.skip));
}

if (parameters.top) {
pipeline.push(generateLimitFromTopExpr(parameters.top));
}

if (parameters.select) {
pipeline.push(generateProjectFromSelectExpr(parameters.select));
}

if (parameters.orderby) {
pipeline.push(generateSortFromOrderbyExpr(parameters.orderby));
}

//add default steps if pipline must not be empty - i.e. in mongoose an empty pipeline returns an error
if (!opts?.returnEmptyPipeline && pipeline.length == 0) {
pipeline.push(
{
$addFields: {
odatafyMongoDBTempField: "",
},
},
{
$project: {
odatafyMongoDBTempField: 0,
},
}
);

countPipeline = [...pipeline];
}

countPipeline.push({
$count: "count",
});

if (opts?.returnCountOnly) {
return countPipeline;
}

if (opts?.returnDataCountQuery) {
return [
{
$facet: {
data: pipeline,
countTmp: countPipeline,
},
},
{
$addFields: {
countTmp2: {
$first: "$countTmp",
},
},
},
{
$project: {
data: "$data",
count: "$countTmp2.count",
},
},
];
}

return pipeline;
}

0 comments on commit bd4a60b

Please sign in to comment.