【问题标题】:How to include empty array when filtering an array过滤数组时如何包含空数组
【发布时间】:2018-02-28 22:52:57
【问题描述】:

这是我的物品模型。

const itemSchema = new Schema({
  name: String,
  category: String,
  occupied: [Number],
  active: { type: Boolean, default: true },
});

我想过滤“占用”数组。所以我使用聚合并展开“占用”字段。

所以我应用匹配查询。并按_id分组。 但如果过滤后的 'occupied' 数组为空,则该项消失。

这是我的代码

Item.aggregate([
  { $match: {
    active: true
  }},
  { $unwind:
    "$occupied",
  },
  { $match: { $and: [
    { occupied: { $gte: 100 }},
    { occupied: { $lt: 200 }}
  ]}},
  { $group : {
    _id: "$_id",
    name: { $first: "$name"},
    category: { $first: "$category"},
    occupied: { $addToSet : "$occupied" }
  }}
], (err, items) => {
  if (err) throw err;
  return res.json({ data: items });
}); 

这里是示例数据集

{ 
    "_id" : ObjectId("59c1bced987fa30b7421a3eb"),
    "name" : "printer1",
    "category" : "printer",
    "occupied" : [ 95, 100, 145, 200 ],
    "active" : true
},
{
  "_id" : ObjectId("59c2dbed992fb91b7421b1ad"),
   "name" : "printer2",
   "category" : "printer",
   "occupied" : [ ],
   "active" : true
}

上面查询的结果

[
  { 
    "_id" : ObjectId("59c1bced987fa30b7421a3eb"),
    "name" : "printer1",
    "category" : "printer",
    "occupied" : [ 100, 145 ],
    "active" : true
  }
]

和我想要的结果

[
  { 
    "_id" : ObjectId("59c1bced987fa30b7421a3eb"),
    "name" : "printer1",
    "category" : "printer",
    "occupied" : [ 100, 145 ],
    "active" : true
  },
  { 
    "_id" : ObjectId("59c2dbed992fb91b7421b1ad"),
    "name" : "printer2",
    "category" : "printer",
    "occupied" : [ ],
    "active" : true
  }
]

我该怎么做??

提前致谢。

【问题讨论】:

  • 虽然您最好为数据样本找到“漂亮的打印”选项(现在已经完成了漂亮),但只要提供您拥有的数据的清晰示例,您想要的结果以及您为实现它所做的尝试,很多人可以向您学习如何在这里实际提出问题。这正是应该提出问题的方式。切中要害,圆满。坚持下去。
  • @NeilLunn 感谢您的评论。我会记住的。

标签: node.js mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

在最简单的形式中,您只需首先不使用$unwind 就可以保留它。您应用的条件意味着您正在寻找与特定值匹配的“唯一集”。

为此,您首先使用$filter 和“集合运算符”(如$setUnion)将输入值减少为“集合”:

Item.aggregate([
  { "$match": { "active": true } },
  { "$project": {
    "name": 1,
    "category": 1,
    "occupied": { 
      "$filter": {
        "input": { "$setUnion": [ "$occupied", []] },
        "as": "o",
        "cond": {
          "$and": [
            { "$gte": ["$$o", 100 ] },
            { "$lt": ["$$o", 200] }
          ]
        }
      }
    }
  }}
], (err, items) => {
  if (err) throw err;
  return res.json({ data: items });
});

自 MongoDB v3 以来,两者都已存在,因此以这种方式做事是很常见的做法。

如果由于某种原因您仍在使用 MongoDB 2.6,那么您可以改用 $map$setDifference

Item.aggregate([
  { "$match": { "active": true } },
  { "$project": {
    "name": 1,
    "category": 1,
    "occupied": { 
      "$setDifference": [
        { "$map": {
          "input": "$occupied",
          "as": "o",
          "in": {
            "$cond": {
              "if": {
                "$and": [
                  { "$gte": ["$$o", 100 ] },
                  { "$lt": ["$$o", 200] }
                ]
              },
              "then": "$$o",
              "else": false
            }
          }
        }},
        [false]
      ]
    }
  }}
], (err, items) => {
  if (err) throw err;
  return res.json({ data: items });
});

这与将数组分开、过滤项目并将其与$addToSet 重新组合在一起的结果相同。不同之处在于它的效率要高得多,并且可以毫无问题地保留(或生成)一个空数组。

【讨论】:

  • 我现在使用的是 MongoDB 2.6。但我会更新 v3 并尝试您的解决方案。感谢您的详细解释。
  • @125487 如答案所述,第二种方法是使用与 MongoDB 2.6 兼容的运算符。需要注意的是,如果您需要官方支持,请that version is past end of life for support,因此升级可能符合您的最佳利益。
  • 我将 MongoDB 更新到 v3.4。你的第一种方法非常有效。感谢您的评论。
猜你喜欢
  • 1970-01-01
  • 2021-12-12
  • 2023-02-22
  • 2019-07-30
  • 2019-08-15
  • 2022-11-28
  • 1970-01-01
  • 2011-02-16
  • 2021-12-10
相关资源
最近更新 更多