【问题标题】:Sort Embedded Documents in Array and fetch specifics对数组中的嵌入文档进行排序并获取详细信息
【发布时间】:2016-06-17 10:36:18
【问题描述】:

我有一个像这样的猫鼬模型:

var activityItem = mongoose.Schema({
    timestampValue: Number,
    xabc: String,
    full: Boolean,
    comp: Boolean
});

var ABC = mongoose.Schema({
    activity: [activityItem],
    user: {
        type: mongoose.Schema.ObjectId,
        ref: 'User'
    },
    username: String
});

我想获取 timestampValue 小于特定值的 activityItem 数组元素。另外,我想先按照timestampValueactivity数组进行排序

这是我目前拥有的代码。而且它不起作用。

UserActivity.findOne({
    'user': current_user,
    'activity' : {
        $all: [
            {
                "$elemMatch": {
                    timestampValue: {
                        $lte: time
                    }
                }
            }
        ]
    }
},

function(err, user){

})

示例文档结构:

{
    "_id" : ObjectId("56d5e88adfd14baf1848a7c6"),
    "user" : ObjectId("56bf225342e662f4277ded73"),
    "notifications" : [],
    "completed" : [],
    "activity" : [ 
        {
            "timestampValue": 1456902600000,
            "xabc": "Some value",
            "full": true,
            "comp": false,
            "_id" : ObjectId("56d5e88adfd14baf1848a7d2")
        }, 
        {
            "timestampValue": 1456702600000,
            "xabc": "Some other value",
            "full": true,
            "comp": false,
            "_id" : ObjectId("56d5e88adfd14baf1848a7d3")
        }
    ],
    "__v" : 1
}

POST 调用具有以下参数

hash: "2e74aaaf42aa5ea733be963cb61fc5ff"
time: 1457202600000

hash 出现在我从 mongo 获得文档后 time 是一个 unix 时间戳值。

它不是只返回小于time 值的元素,而是返回所有数组元素。我在查询之前尝试了聚合框架对数组进行排序,但无法掌握它。

任何帮助将不胜感激。

【问题讨论】:

  • 添加到帖子中的示例输入文档。

标签: node.js mongodb mongoose aggregation-framework


【解决方案1】:

请尝试通过aggregation进行如下操作

ABS.aggregate([
    // filter the document by current_user
    {$match: {user: ObjectId(current_user)}},
    // unwind the activity array
    {$unwind: '$activity'},
    // filter the timestampValue less than time
    {$match: {'activity.timestampValue': {$lte: time}}},
    // sort activity by timestampValue in ascending order
    {$sort: {'activity.timestampValue': 1}},
    // group by _id, and assemble the activity array.
    {$group: {_id: '$_id', user: {$first: '$user'},activity: {$push: '$activity'}}}
], function(err, results){
    if (err)
        throw err;

    // populate user to get details of user information if needed
    //ABS.populate( results, { "path": "user" }, function(err, rets) {
        //
    //});
});

【讨论】:

  • 这很好用。但它不会查询用户。如何为current_user 进行此聚合。非常感谢。
  • 我想我的问题可能不够清楚。对此表示歉意。在我根据用户字段选择特定文档后,应该聚合活动数组。请查看问题中的代码示例。再次感谢:)
  • @amit,也许我误解了你?然而,在你的第一条评论中,你提到它运作良好......这让我很困惑......
  • @amit,用样本数据和我的代码测试,我得到的结果是{ "_id" : ObjectId("56dd7c0745d1c0a7f3c11634"), "user" : "123456", "activity" : [ { "timestampValue" : 20000, "full" : true, "comp" : false }, { "timestampValue" : 30000, "full" : true, "comp" : true } ] }
  • 它可以很好地从活动数组中提取值。问题是假设有多个具有不同 ID 的文档,就像 ObjectId("56dd7c0745d1c0a7f3c11634") 具有不同的唯一 user 字段。我想先获取文档,然后搜索嵌入文档的activity数组。
【解决方案2】:

嗯,除非你有 MongoDB 3.2,否则使用 MongoDb 聚合管道似乎有点棘手,但你绝对可以 借助 map-reduce 实现您的结果。

例如

MongoDB 版本

var findActivities = function (time) {
    db.col.mapReduce(function () {
        var item = Object.assign({}, this);
        delete item.activity;
        item.activity = [];

        for (var i = 0; i < this.activity.length; i++) {
            if (this.activity[i].timestampValue <= time) {
                item.activity.push(this.activity[i]);
            }
        }

        emit(item._id, item);
    }, function (k, v) {
        return {items: v};
    }, {
        out: {"inline": true},
        scope: {time: time}
    }).results.forEach(function (o) {
        printjson(o); // Or perform action as appropriate 
    });
};

在调用findActivities(1456802600000) 时基于您的示例数据,它将仅查找并返回那些符合条件的文档。

{ 
    "_id" : ObjectId("56d5e88adfd14baf1848a7c6"), 
    "value" : {
        "_id" : ObjectId("56d5e88adfd14baf1848a7c6"), 
        "user" : ObjectId("56bf225342e662f4277ded73"), 
        "notifications" : [

        ], 
        "completed" : [

        ], 
        "__v" : NumberInt(1), 
        "activity" : [
            {
                "timestampValue" : NumberLong(1456702600000), 
                "xabc" : "Some other value", 
                "full" : true, 
                "comp" : false, 
                "_id" : ObjectId("56d5e88adfd14baf1848a7d3")
            }
        ]
    }
}

MongoDB 版本 3.2+

db.col.aggregate([
    {$project:{user:1, notifications:1, completed:1, activity:{
      $filter:{input: "$activity", as: "activity", cond:{
        $lte: ["$$activity.timestampValue", 1456802600000]}}}}}
])

两种解决方案将具有相同的输出。

【讨论】:

    猜你喜欢
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2011-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多