缺少 Children

Missing Children

将 Mongoose 与 MongoDB 结合使用,我的 Schema 如下:

var PartSchema = new Schema({
    partcode: String,
    children: [String]
});

数据如下所示:

[{"partcode":"A1","children":["B1","B2","B3","B4"]},
 {"partcode":"B1","children":["C11","C21","C31","C41"]},
 {"partcode":"B3","children":["C13","C23","C33","C43"]},

我可以使用以下静态调用查询 A1 的 children 字段:

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);
    });
});

这 return 秒(通过快递)

 [{"partcode":"B1","children":["C11","C21","C31","C41"]},
 {"partcode":"B3","children":["C13","C23","C33","C43"]}]

我需要的是return全部children没有找到,例如:

[{"children":["B2","B4"}]

您可以使用 lodash 库中的 _.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;
        });
    });
});

-- 更新--

用MongoDB的aggregation framework就可以达到想要的效果。让我们先在 mongoshell 中演示一下。

假设您在部件集合中插入以下测试文档:

db.part.insert([
    {"partcode":"A1","children":["B1","B2","B3","B4"]},
    {"partcode":"B1","children":["C11","C21","C31","C41"]},
    {"partcode":"B3","children":["C13","C23","C33","C43"]}
])

聚合在这里很有用,因为您有一个给定零件代码的子代码数组,比如 "A1",即 ["B1","B2","B3","B4"]。在这种情况下,您的聚合管道将包含以下聚合管道阶段:

  1. $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 运算符.

  2. $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 累加器运算符。

  3. $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 设置运算符.

您最终的聚合运算符将如下所示(使用 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()

输出:

/* 0 */
{
    "result" : [ 
        {
            "partcode" : ["B2","B4"]
        }
    ],
    "ok" : 1
}

在您的 Mongoose 模式方法中使用相同的概念:

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);                
    });
});

或者使用 Mongoose aggregation pipeline builder 进行流畅的调用:

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);
            });
    });