Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ardalanamini committed Dec 4, 2018
1 parent 5da9f1f commit 6113bf5
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 34 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@

---

## [v0.6.0](https://github.com/foxifyjs/odin/releases/tag/v0.6.0) - *(2018-12-04)*

- :zap: Added `has` to model queries (use with caution since it may have a negative impact on your performance)

## [v0.5.4](https://github.com/foxifyjs/odin/releases/tag/v0.5.4) - *(2018-12-03)*

- :zap: Added `Collection` to create collection and indexes
Expand Down
26 changes: 13 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@foxify/odin",
"version": "0.5.4",
"version": "0.6.0",
"description": "Active Record Model",
"author": "Ardalan Amini <ardalanamini22@gmail.com> [https://github.com/ardalanamini]",
"contributors": [
Expand Down Expand Up @@ -44,8 +44,8 @@
"dependencies": {
"@types/graphql": "^0.13.4",
"@types/graphql-iso-date": "^3.3.1",
"@types/mongodb": "^3.1.15",
"@types/node": "^10.12.11",
"@types/mongodb": "^3.1.17",
"@types/node": "^10.12.12",
"async": "^2.6.1",
"caller-id": "^0.1.0",
"deasync": "^0.1.14",
Expand Down
14 changes: 6 additions & 8 deletions src/DB/Join.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ class Join<T = any> extends Filter {

return {
$lookup: {
as: this._as,
from: this._collection,
let: this._let,
from: this._collection,
pipeline: this._pipeline,
as: this._as,
},
};
}
Expand Down Expand Up @@ -54,10 +54,9 @@ class Join<T = any> extends Filter {

keys.push(prepareKey(keys.pop() as string));

const key = keys.join(".");
const pivotKey = `pivot_${key}`;
const pivotKey = `pivot_${keys.join("_ODIN_")}`;

this._let[pivotKey] = `$${key}`;
this._let[pivotKey] = `$${keys.join(".")}`;

return this._push_filter("and", {
$expr: {
Expand All @@ -75,10 +74,9 @@ class Join<T = any> extends Filter {

keys.push(prepareKey(keys.pop() as string));

const key = keys.join(".");
const pivotKey = `pivot_${key}`;
const pivotKey = `pivot_${keys.join("_ODIN_")}`;

this._let[pivotKey] = `$${key}`;
this._let[pivotKey] = `$${keys.join(".")}`;

return this._push_filter("or", {
$expr: {
Expand Down
7 changes: 7 additions & 0 deletions src/Relation/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace Relation {
name: string;
relations: Relation[];
}

export interface RelationCount {
name: string;
relation?: RelationCount;
}
}

abstract class Relation<T extends Odin = Odin, A = undefined> {
Expand Down Expand Up @@ -38,6 +43,8 @@ abstract class Relation<T extends Odin = Odin, A = undefined> {

public abstract load(query: DB<T> | Join<T>, relations: Relation.Relation[]): DB<T> | Join<T>;

public abstract loadCount(query: DB<T> | Join<T>): DB<T> | Join<T>;

/****************************** With Relations ******************************/

public with(...relations: string[]): Query<T>;
Expand Down
19 changes: 17 additions & 2 deletions src/Relation/EmbedMany.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Odin from "..";
import * as DB from "../DB";
import Join from "../DB/Join";
import { makeCollectionId } from "../utils";
import { array, makeCollectionId } from "../utils";
import Relation from "./Base";
import HasMany from "./HasMany";

Expand Down Expand Up @@ -34,10 +34,25 @@ class EmbedMany<T extends Odin = Odin> extends HasMany<T> {
},
q.whereIn(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`)
),
// q => q.whereIn(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`),
this.as
);
}

public loadCount(query: DB<T> | Join<T>) {
return query
.join(
this.relation.toString(),
q => q
.whereIn(this.foreignKey, `${this.model.constructor.toString()}.data.${this.localKey}`),
"relation"
)
.pipeline({
$project: {
data: 1,
count: { $size: "$relation" },
},
});
}
}

export default EmbedMany;
17 changes: 16 additions & 1 deletion src/Relation/HasMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,25 @@ class HasMany<T extends Odin = Odin> extends Relation<T> {
},
q.where(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`)
),
// q => q.where(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`),
this.as
);
}

public loadCount(query: DB<T> | Join<T>) {
return query
.join(
this.relation.toString(),
q => q
.where(this.foreignKey, `${this.model.constructor.toString()}.data.${this.localKey}`),
"relation"
)
.pipeline({
$project: {
data: 1,
count: { $size: "$relation" },
},
});
}
}

export default HasMany;
20 changes: 17 additions & 3 deletions src/Relation/HasOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,29 @@ class HasOne<T extends Odin = Odin> extends Relation<T, "HasOne"> {
q.where(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`)
.limit(1)
),
// q => q
// .where(this.foreignKey, `${this.model.constructor.toString()}.${this.localKey}`)
// .limit(1),
name
).pipeline({
$unwind: { path: `$${name}`, preserveNullAndEmptyArrays: true },
});
}

public loadCount(query: DB<T> | Join<T>) {
return query
.join(
this.relation.toString(),
q => q
.where(this.foreignKey, `${this.model.constructor.toString()}.data.${this.localKey}`)
.limit(1),
"relation"
)
.pipeline({
$project: {
data: 1,
count: { $size: "$relation" },
},
});
}

public insert(items: T[]): Promise<undefined>;
public insert(items: T[], callback: DB.Callback<undefined>): void;
public async insert(items: T[], callback?: DB.Callback<undefined>) {
Expand Down
19 changes: 19 additions & 0 deletions src/Relation/MorphMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ class MorphMany<T extends Odin = Odin> extends MorphBase<T> {
name
);
}

public loadCount(query: DB<T> | Join<T>) {
const constructor = this.model.constructor;

return query
.join(
this.relation.toString(),
q => q
.where(this.foreignKey, `${constructor.toString()}.data.${this.localKey}`)
.where(`${this.type}_type`, constructor.name),
"relation"
)
.pipeline({
$project: {
data: 1,
count: { $size: "$relation" },
},
});
}
}

export default MorphMany;
20 changes: 20 additions & 0 deletions src/Relation/MorphOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ class MorphOne<T extends Odin = Odin> extends MorphBase<T, "MorphOne"> {
});
}

public loadCount(query: DB<T> | Join<T>) {
const constructor = this.model.constructor;

return query
.join(
this.relation.toString(),
q => q
.where(this.foreignKey, `${constructor.toString()}.data.${this.localKey}`)
.where(`${this.type}_type`, constructor.name)
.limit(1),
"relation"
)
.pipeline({
$project: {
data: 1,
count: { $size: "$relation" },
},
});
}

public insert(items: T[]): Promise<undefined>;
public insert(items: T[], callback: DB.Callback<undefined>): void;
public async insert(items: T[], callback?: DB.Callback<undefined>) {
Expand Down
12 changes: 8 additions & 4 deletions src/base/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,25 @@ class Query<T extends object = {}> extends DB<T> {

/****************************** Has & WhereHas ******************************/

// TODO: join relation's count
public has(relation: string, operator: DB.Operator = ">", count: number = 0) {
if (!(this._model as any)._relations.includes(relation))
throw new Error(`Relation '${relation}' does not exist on '${this._model.name}' Model`);

this.pipeline({
$project: {
_id: 0,
data: "$$ROOT",
},
});

// join relation
(this._model.prototype as any)[relation]().loadCount(this);

return this.pipeline(
{
relation: {
[OPERATORS[operator]]: count, // filter data according to count and operator
$match: {
count: {
[`$${OPERATORS[operator]}`]: count, // filter data according to count and operator
},
},
},
{
Expand Down
6 changes: 6 additions & 0 deletions src/base/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class QueryBuilder<T extends object = {}> extends Base<T> {
return this.query().lean();
}

/****************************** Has & WhereHas ******************************/

public static has(relation: string, operator?: DB.Operator, count?: number) {
return this.query().has(relation, operator, count);
}

/****************************** With Relations ******************************/

public static with<T extends object>(...relations: string[]): Query<T>;
Expand Down
11 changes: 11 additions & 0 deletions test/relations/HasMany.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as Odin from "../../src";
import { array } from "../../src/utils";

declare global {
namespace NodeJS {
Expand Down Expand Up @@ -207,3 +208,13 @@ test("Model.with deep", async () => {

expect(results4.map((item: any) => item.toJSON())).toEqual(items);
});

test("Model.has", async () => {
expect.assertions(1);

const items = CHATS.filter(chat => array.any(MESSAGES, message => message.chatname === chat.name));

const results = await Chat.has("messages").lean().get();

expect(results).toEqual(items);
});
Loading

0 comments on commit 6113bf5

Please sign in to comment.