Skip to content

Commit

Permalink
🧱 add aggregation method to odm
Browse files Browse the repository at this point in the history
  • Loading branch information
hemedani committed Apr 23, 2023
1 parent a3db1b3 commit 8e69d15
Show file tree
Hide file tree
Showing 6 changed files with 323 additions and 7 deletions.
126 changes: 126 additions & 0 deletions src/odm/aggregation/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { generateProjection } from "./generateProjection.ts";
import { Projection, ProjectionPip } from "./type.ts";

const projection: Projection = {
name: 1,
family: 1,
country: {
name: 1,
abb: 1,
states: {
name: 1,
abb: 1,
cities: {
name: 1,
abb: 1,
},
},
flag: {
name: 1,
desc: 1,
path: 1,
},
},

ware: {
name: 1,
price: 1,
wareModel: {
name: 1,
desc: 1,
wareType: {
name: 1,
desc: 1,
wareGroup: {
name: 1,
desc: 1,
},
},
},
tags: {
name: 1,
desc: 1,
relatedWare: {
name: 1,
price: 1,
},
},
},

creator: {
name: 1,
},
};

const sample: ProjectionPip = [
{
"$lookup": {
"from": "roots",
"localField": "root",
"foreignField": "_id",
"as": "root",
},
},
{
"$unwind": {
"path": "$root",
"preserveNullAndEmptyArrays": true,
},
},
{
"$lookup": {
"from": "elixirs",
"localField": "root.elixir",
"foreignField": "_id",
"as": "root.elixir",
},
},
{
"$unwind": {
"path": "$root.elixir",
"preserveNullAndEmptyArrays": true,
},
},
{
"$lookup": {
"from": "procedures",
"localField": "root.elixir.procedures",
"foreignField": "_id",
"as": "root.elixir.procedures",
},
},
{
"$project": {
"title": 1,
"description": 1,
"root": {
"name": 1,
"question": 1,
"elixir": {
"name": 1,
"title": 1,
"description": 1,
"procedures": {
"name": 1,
"description": 1,
"index": 1,
},
},
},
},
},
];

const testProjec = generateProjection(projection);

/*
* @LOG @DEBUG @INFO
* This log written by ::==> {{ syd }}
*
* Please remove your log after debugging
*/
console.log(" ============= ");
console.log();
console.info({ testProjec: JSON.stringify(testProjec, null, 2) }, " ------ ");
console.log();
console.log(" ============= ");
111 changes: 111 additions & 0 deletions src/odm/aggregation/generateProjection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { InRelation } from "../../mod.ts";
import { ISchema } from "../../models/mod.ts";
import { getRelation } from "../../models/relation/getRelation.ts";
import { Projection, ProjectionPip } from "./type.ts";

export const checkNotLastObj = (
projection: Projection,
) => {
let notLast = false;

for (const prop in projection) {
typeof projection[prop] === "object" &&
(notLast = true);
}

return notLast ? true : false;
};

export const generateProjection = (
projection: Projection,
schemasObj: ISchema,
collectionName: string,
) => {
const returnPip: ProjectionPip = [];

const createLookup = (
projection: Projection,
localField: string,
collectionName: string,
propName: string,
) => {
let foundAsInrelation = null;
let foundAsOutrelation = null;

const schemaInrel = getRelation(schemasObj, collectionName, "inrelation");
const schemaOutrel = getRelation(schemasObj, collectionName, "outrelation");

for (const inrelProp in schemaInrel) {
(inrelProp === propName) &&
(foundAsInrelation = schemaInrel[inrelProp]);
}

for (const outrelProp in schemaOutrel) {
(outrelProp === propName) &&
(foundAsOutrelation = schemaOutrel[outrelProp]);
}

const pushLockupPip = (
from: string,
localField: string,
unwind: boolean,
) => {
returnPip.push({
"$lookup": {
from,
localField: `${localField}._id`,
"foreignField": `_id`,
"as": localField,
},
});

unwind && returnPip.push({
"$unwind": {
path: `$${localField}`,
preserveNullAndEmptyArrays: true,
},
});
};

foundAsInrelation
? pushLockupPip(
foundAsInrelation.schemaName,
localField,
(foundAsInrelation as InRelation).type === "one" ? true : false,
)
: foundAsOutrelation
? pushLockupPip(foundAsOutrelation.schemaName, localField, false)
: null;

if (foundAsInrelation || foundAsOutrelation) {
for (const prop in projection) {
typeof projection[prop] === "object" &&
checkNotLastObj(
projection[prop] as Projection,
) &&
createLookup(
projection[prop] as Projection,
`${localField}.${prop}`,
foundAsInrelation
? foundAsInrelation.schemaName
: foundAsOutrelation!.schemaName,
prop,
);
}
}
};

for (const prop in projection) {
typeof projection[prop] === "object" &&
checkNotLastObj(
projection[prop] as Projection,
) &&
createLookup(projection[prop] as Projection, prop, collectionName, prop);
}

returnPip.push({
"$project": { ...projection },
});

return returnPip;
};
2 changes: 2 additions & 0 deletions src/odm/aggregation/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./generateProjection.ts";
export * from "./type.ts";
27 changes: 27 additions & 0 deletions src/odm/aggregation/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export type Projection = { [key: string]: number | Projection };

export interface Lookup {
from: string;
localField: string;
foreignField: string;
as: string;
}

export interface Unwind {
path: string;
preserveNullAndEmptyArrays: true;
}

export type LookupObj = {
$lookup: Lookup;
};

export type UnwindObj = {
$unwind: Unwind;
};

export type ProjectionObj = {
$project: Projection;
};

export type ProjectionPip = (LookupObj | UnwindObj | ProjectionObj)[];
Loading

0 comments on commit 8e69d15

Please sign in to comment.