Reputation: 785
Using Mongoose with MongoDB, my Schema is as follows:
var PartSchema = new Schema({
partcode: String,
children: [String]
});
And the data looks like the following:
[{"partcode":"A1","children":["B1","B2","B3","B4"]},
{"partcode":"B1","children":["C11","C21","C31","C41"]},
{"partcode":"B3","children":["C13","C23","C33","C43"]},
I can query for A1's children field by using the following static call:
PartSchema.static('getChildren', function (partcode, callback) {
var self = this;
self.findOne({ partcode: partcode }, childrenOnly)
.exec(function (err, doc) {
return self.find({"partcode": {"$in": doc.children} }, exclId, callback);
});
});
This returns (via express)
[{"partcode":"B1","children":["C11","C21","C31","C41"]},
{"partcode":"B3","children":["C13","C23","C33","C43"]}]
What I need is to return all children not found, for example:
[{"children":["B2","B4"}]
Upvotes: 2
Views: 99
Reputation: 103375
You could use the _.difference()
method from the lodash library to calculate the array set difference:
var _ = require("lodash");
PartSchema.static('getChildren', function (partcode, callback) {
var self = this;
self.findOne({ partcode: partcode }, childrenOnly)
.exec(function (err, doc) {
var promise = self.find({"partcode": {"$in": doc.children} }, exclId).lean().exec();
promise.then(function (res){
var codes = res.map(function (m) {
return m.children;
}),
obj = {"children": _.difference(doc.children, codes)},
result = [];
result.push(obj);
return result;
});
});
});
-- UPDATE --
With MongoDB's aggregation framework, you can achieve the desired result. Let's demonstrate this in mongoshell first.
Suppose you insert the following test documents in the parts collection:
db.part.insert([
{"partcode":"A1","children":["B1","B2","B3","B4"]},
{"partcode":"B1","children":["C11","C21","C31","C41"]},
{"partcode":"B3","children":["C13","C23","C33","C43"]}
])
The aggregation can be useful here given that you have an array of the children partcodes for a given partcode, say "A1"
, which is ["B1","B2","B3","B4"]
. In this instance, your aggregation pipeline would consist of the following aggregation pipeline stages:
$match
- You need this to filter those documents whose children partcodes are not in the ["B1","B2","B3","B4"]
array. This is achieved using the $nin
operator.
$group
- This groups all the documents from the previous stream and creates an additional array field that has the parent partcodes. Made possible by using the $addToSet
accumulator operator.
$project
- Reshapes each document in the stream by adding a new field partcode
(which will eventually become part of the result object) and suppresses the _id
field. This is where you can get the array difference between the parent partcode in the criteria and those not in the pipeline documents, made possible using the $setDifference
set operator.
Your final aggregation operator would look like this (using mongoshell):
var children = ["B1","B2","B3","B4"];
db.part.aggregate([
{
"$match": {
"children": { "$nin": children }
}
},
{
"$group": {
"_id": null,
"parents": {
"$addToSet": "$partcode"
}
}
},
{
"$project": {
"_id": 0,
"partcode": {
"$setDifference": [ children, "$parents" ]
}
}
}
]).pretty()
Output:
/* 0 */
{
"result" : [
{
"partcode" : ["B2","B4"]
}
],
"ok" : 1
}
Using the same concept in your Mongoose schema method:
PartSchema.static('getChildren', function (partcode, callback) {
var self = this;
self.findOne({ partcode: partcode }, childrenOnly)
.exec(function (err, doc) {
var pipeline = [
{
"$match": {
"children": { "$nin": doc.children }
}
},
{
"$group": {
"_id": null,
"parents": {
"$addToSet": "$partcode"
}
}
},
{
"$project": {
"_id": 0,
"partcode": {
"$setDifference": [ doc.children, "$parents" ]
}
}
}
],
self.aggregate(pipeline).exec(callback);
});
});
Or using Mongoose aggregation pipeline builder for a fluent call:
PartSchema.static('getMissedChildren', function (partcode, callback) {
var self = this;
self.findOne({ partcode: partcode }, childrenOnly)
.exec(function (err, doc) {
var promise = self.aggregate()
.match({"children": { "$nin": doc.children }})
.group({"_id": null,"parents": {"$addToSet": "$partcode"}})
.project({"_id": 0,"partcode": {"$setDifference": [ doc.children, "$parents" ]}})
.exec(callback);
});
});
Upvotes: 1