Changeset View
Changeset View
Standalone View
Standalone View
lib/datastore/query.test.js
- This file was added.
const Query = require("./query.js"); | |||||
const assert = require("assert"); | |||||
describe("Query", () => { | |||||
describe("Query.And", () => { | |||||
it("Returns pipeline stages in correct order for simple case", () => { | |||||
const queries = []; | |||||
let query = new Query(); | |||||
kuba-orlik: Aby uniknąć powtórzeń, tutaj możemy korzystać z przeniesionej do `Query` metody statycznej | |||||
const L1 = { | |||||
from: "authors", | |||||
localField: "author", | |||||
foreignField: "_id", | |||||
}; | |||||
const L1_id = query.lookup(L1); | |||||
Done Inline Actionsbeka :D kuba-orlik: beka :D | |||||
const M2 = { | |||||
[`${L1_id}.last_name`]: { $in: ["Christie", "Rowling"] }, | |||||
}; | |||||
query.match(M2); | |||||
queries.push(query); | |||||
const M3 = { | |||||
title: { $ne: "The Joy of PHP" }, | |||||
}; | |||||
queries.push(Query.fromSingleMatch(M3)); | |||||
let and = new Query.And(...queries); | |||||
assertStagesAreCorrectlyOrdered([M3, L1, M2], and.toPipeline()); | |||||
}); | |||||
function assertStagesAreCorrectlyOrdered( | |||||
expectedRawPipeline, | |||||
actualPipeline | |||||
) { | |||||
const query = new Query(); | |||||
for (let i = 0; i < expectedRawPipeline.length; ++i) { | |||||
const stage = expectedRawPipeline[i]; | |||||
if (stage instanceof Query) { | |||||
query.stages = query.stages.concat(stage.toPipeline()); | |||||
} else if (stage.from) { | |||||
query.lookup(stage); | |||||
} else { | |||||
for (let step of Object.keys(stage)) { | |||||
query.match({ [step]: stage[step] }); | |||||
} | |||||
} | |||||
} | |||||
assert.deepEqual(actualPipeline, query.toPipeline()); | |||||
} | |||||
it("Returns pipeline stages in correct order for complex case", () => { | |||||
const queries = []; | |||||
let query = new Query(); | |||||
const L1 = { | |||||
from: "authors", | |||||
localField: "author", | |||||
foreignField: "_id", | |||||
}; | |||||
const L1_id = query.lookup(L1); | |||||
const L2 = { | |||||
Done Inline ActionsHm, myślę i myślę i nie mogę zrozumieć, co tu się dzieje :< Jakbyś to opisał własnymi słowami? kuba-orlik: Hm, myślę i myślę i nie mogę zrozumieć, co tu się dzieje :< Jakbyś to opisał własnymi słowami? | |||||
Done Inline ActionsTak jak mówi komentarz - transformujemy pipeline z którego stworzyliśmy Query, aby był tym czego oczekujemy - czyli pola as przyjmą wartość hashy, a matche złożone, które mają więcej niż 1 krok rozbijamy na takie z pojedynczym krokiem piotr-ptaszynski: Tak jak mówi komentarz - transformujemy pipeline z którego stworzyliśmy `Query`, aby był tym… | |||||
Done Inline ActionsMyślę, że w tym wypadku lepiej podać bezpośrednio obiekt w takiej formie, w jakiej go oczekujemy - w postaci zwykłego, statycznego obiektu expected_json = JSON.stringify([{$match: ...}, ...]); Coprawda będzie to trochę redundantne, ale pomoże czytającemu zrozumieć, co się dzieje (widzimy efekt przed i po) kuba-orlik: Myślę, że w tym wypadku lepiej podać bezpośrednio obiekt w takiej formie, w jakiej go… | |||||
from: "publisher", | |||||
localField: `${L1_id}.publisher`, | |||||
foreignField: "publisher_id", | |||||
}; | |||||
const L2_id = query.lookup(L2); | |||||
const M3_4 = { | |||||
$or: [ | |||||
{ [`${L1_id}.first_name`]: "Ann" }, | |||||
{ [`${L2_id}.income`]: { $gt: 1000 } }, | |||||
], | |||||
[`${L2_id}.city`]: { $in: ["A", "B"] }, | |||||
}; | |||||
query.match(M3_4); | |||||
queries.push(query); | |||||
query = new Query(); | |||||
const M5 = { | |||||
title: { $ne: "The Joy of PHP" }, | |||||
}; | |||||
query.match(M5); | |||||
queries.push(query); | |||||
let subquery1 = new Query(); | |||||
const O6_L1 = { | |||||
from: "libraries", | |||||
localField: "first_library", | |||||
foreignField: "library_id", | |||||
}; | |||||
const O6_L1_id = subquery1.lookup(O6_L1); | |||||
const O6_M1 = { | |||||
[`${O6_L1_id}.street`]: { $in: ["A street", "B street"] }, | |||||
[`${O6_L1_id}.open_at_night`]: { $eq: true }, | |||||
}; | |||||
subquery1.match(O6_M1); | |||||
const O6_M2 = { | |||||
books_count: { $lte: 30 }, | |||||
}; | |||||
let subquery2 = Query.fromSingleMatch(O6_M2); | |||||
const O6 = new Query.Or(subquery1, subquery2); | |||||
queries.push(O6); | |||||
const O7_M1 = { | |||||
title: { | |||||
$in: ["PHP - Python Has Power", "The Good Parts of JS"], | |||||
}, | |||||
}; | |||||
const O7_M2 = O6_M2; | |||||
const O7 = new Query.Or( | |||||
Query.fromSingleMatch(O7_M1), | |||||
Query.fromSingleMatch(O7_M2) | |||||
); | |||||
queries.push(O7); | |||||
query = new Query(); | |||||
const L8 = { | |||||
from: "cover_types", | |||||
localField: "cover", | |||||
foreignField: "cover_type_id", | |||||
}; | |||||
const L8_id = query.lookup(L8); | |||||
const M9 = { | |||||
[`${L8_id}.name`]: { $ne: "hard" }, | |||||
}; | |||||
query.match(M9); | |||||
queries.push(query); | |||||
query = new Query(); | |||||
// check if hashing is order insensitive | |||||
const L10 = { | |||||
localField: "cover", | |||||
from: "cover_types", | |||||
foreignField: "cover_type_id", | |||||
}; | |||||
const L10_id = query.lookup(L10); | |||||
const M11 = { | |||||
[`${L10_id}.name`]: { $ne: "no_cover" }, | |||||
}; | |||||
query.match(M11); | |||||
queries.push(query); | |||||
let and = new Query.And(...queries); | |||||
assertStagesAreCorrectlyOrdered( | |||||
[M5, O7, L8, M9, M11, L1, L2, M3_4, O6], | |||||
and.toPipeline() | |||||
); | |||||
}); | |||||
}); | |||||
}); |
Aby uniknąć powtórzeń, tutaj możemy korzystać z przeniesionej do Query metody statycznej