如何使用$ setUnion和$ group从mongoose获得独特的结果?

时间:2017-06-02 20:11:47

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

sample documents of mongodb

我尝试了类似下面的内容,但是dint得到了预期的结果,如果字段不是数组,这应该有效

 // union of includes and excludes as excludesAndExcludes
    getIncludesAndExcludes: (req, res)=>{
        console.log('called setunion');
        experienceModel.aggregate([
            { $group: {_id : {includes:"$includes", excludes:"$excludes"}}},            
            { $project: { includesAndExcludes: { $setUnion: [ "$_id.includes", "$_id.excludes" ] }, _id:0 } }  
        ], (err, data) => {
            if (err) {
                res.status(500).send(error); 
            } else {
                res.json(data);
            }         
        })
    },

1 个答案:

答案 0 :(得分:0)

如果两个数组都没有通过$exists的任何元素,则最有效地排除文档,最重要的是,您需要$ifNull替换为数组不存在的null

experienceModel.aggregate([
  // Don't include documents that have no arrays
  { "$match": { 
    "$or": [
      { "includes.0": { "$exists": true } },
      { "excludes.0": { "$exists": true } }
    ]
  },
  // Join the arrays and exclude the nulls
  { "$project": {
    "_id": 0,
    "list": {
      "$setDifference": [
        { "$setUnion": [ 
          { "$ifNull": [ "$includes", [null] ] },
          { "$ifNull": [ "$excludes", [null] ] }
        ]},
        [null]
      ]
  }},

  // Unwind. By earlier conditions the array must have some entries
  { "$unwind": "$list" },

  // And $group on the list values as the key to produce distinct results
  { "$group": { "_id": "$list" }
],(err, data) => {
   // rest of code
})

所以第一个$match是要过滤的,这样至少有一个数组必须作为逻辑规则存在,这也可能加快速度。接下来,使用$setUnion加入数组,如果字段不存在,请小心使用$ifNull替换为单个元素[null]的数组。如果您没有这样做,那么任何$setUnion结果都将是null,而不是列出任一数组的条目。所以这非常重要。

由于$setUnion的输出可能在其列表中包含null项,因此您可以使用$setDifference删除该项,这是最短的形式过滤器,您可以编写并使用" sets"。

真正剩下的就是"去标准化"使用[$unwind][6]将每个文档中的数组转换为每个元素的单个文档,并且我们不需要像preserveNullAndEmptyArrays这样的新选项,因为上面的所有逻辑都已经开始了。然后,最后$group就是在这些值上完成,以便产生" unique"输出,这是$group语句中_id键的用途。

如果您愿意,那么您甚至可以使用.map()将结果从聚合中删除到您的回复的简单字符串列表中:

data = data.map( d => d._id );

然后你的所有服务返回都是一个字符串数组,没有嵌入式结构。

[
  "Breakfast"
  "Airport Pickup"
  "Dinner"
  "Accomodation"
]