javascript - 如何比较数组并计算匹配项

标签 javascript mongodb mongodb-query aggregation-framework

我有一个包含以下文档的集合:

 {
   _id: ObjectId("000000000000000000059734"),
   locations: ["A", "B", "C"]  
 },

 {
   _id: ObjectId("000000000000000000059735"),
   locations: ["A", "D", "K"]  
 },

 {
   _id: ObjectId("000000000000000000059736"),
   locations: ["1", "3", "C"]  
 }

现在我想要的是根据以下数组项计算文档总数:

let array = ['A', 'B', '1'];

我想要的结果是:

{
  'A': 2,
  'B': 1,
  '1': 1
}

我尝试过的:

db.getCollection('mycollection').aggregate([
  {$group: {
   "_id": { 
       "location": { 
        "A": { "$sum": { "$cond": [{ "$in": [ "A", "$locations" ] },1,0] } },
        "B": { "$sum": { "$cond": [{ "$in": [ "B", "$locations" ] },1,0] } },     
        "1": { "$sum": { "$cond": [{ "$in": [ "1", "$locations" ] },1,0] } },
       }
    }    
  }}
])

但是我的查询结果格式和我想要的不一样。

感谢任何帮助和指导。

最佳答案

如果你至少有 MongoDB 3.4.4,那么你可以这样做:

var array = ['A', 'B', '1'];

db.getCollection('mycollection').aggregate([
  { "$project": {
    "locations": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$locations",
            "cond": { "$in": [ "$$this", array ] }
          }
        },
        "in": { "k": "$$this", "v": 1 }
      }
    }
  }},
  { "$unwind": "$locations" },
  { "$group": {
    "_id": "$locations.k",
    "v": { "$sum": "$locations.v" }
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
     "_id": null,
     "obj": { "$push": { "k": "$_id", "v": "$v" } } 
  }},
  { "$replaceRoot": {
    "newRoot": { "$arrayToObject": "$obj" }  
  }}
])

对于没有类似 $arrayToObject 的旧版本,您将在从服务器返回结果“之后”转换结果,如下所示:

var array = ['A', 'B', '1'];

db.getCollection('mycollection').aggregate([
  { "$project": {
    "locations": {
      "$map": {
        "input": {
          "$filter": {
            "input": "$locations",
            "cond": {
              // "$in": [ "$$this", array ]
              "$or": array.map(a => ({ "$eq": [ "$$this", a ] }) )
            }
          }
        },
        "in": { "k": "$$this", "v": 1 }
      }
    }
  }},
  { "$unwind": "$locations" },
  { "$group": {
    "_id": "$locations.k",
    "v": { "$sum": "$locations.v" }
  }},
  { "$sort": { "_id": 1 } },
  { "$group": {
     "_id": null,
     "obj": { "$push": { "k": "$_id", "v": "$v" } } 
  }},
  /*
  { "$replaceRoot": {
    "newRoot": { "$arrayToObject": "$obj" }  
  }}
  */
]).map(d => 
  d.obj.reduce((acc,curr) => Object.assign(acc,{ [curr.k]: curr.v }),{})
)

无论哪种情况,第一步都是到 $project$map为了查看文档数组中的每个值并将其与比较数组进行比较。事实上我们使用 $filter只返回“匹配项”和 $map返回值 1 以计算每次出现的次数。

“过滤”有两种基本方法,要么使用 $in对于支持运算符的版本,或使用 $or在引入之前的旧版本中。

坦率地说,可以简单地使用 $setIntersection只要您的文档数据是“唯一的”,就可以获得匹配项,因为没有文档数组包含多次出现的值。所以我在这里玩安全游戏 $filter因为我不知道你的数据。选择任何花色。

 // If the "locations" content is meant to be "unique"
 { "$project": {
    "locations": {
      "$map": {
        "input": {
          "$setIntersection": [ "$locations", array ]
        },
        "in": { "k": "$$this", "v": 1 }
      }
    }
  }},

注意 $mapkv 属性形式输出。这将作为一种模式继续贯穿管道的其余部分。

因为您想“聚合”数组项中的 k 值,所以我们使用 $unwind所以我们可以跨文档将它们加在一起。然后将其输入 $groupk 的值上并使用 $sum在每个 v 上有效地“计算”出现次数。

$sort是完全可选的,实际上您不应该关心单个输出文档中键的顺序。请注意与您的“期望”的区别,但这只是一个明显的事实,即 “1” 在词汇上“小于”“A”。所以你无法抗拒它,这就是世界运转的方式。

下一阶段就是$group到单个文档。在这里,我们继续重构为包含 kv 的对象的“数组”。

之所以这样,是因为最后的处理。无论你有一个带有 $arrayToObject 的 MongoDB支持(实际上从 3.4.4 开始就包含了,尽管文档声称是 3.6)。在你这样做的地方,我们只是在 $replaceRoot 中提供生成的“数组”作为输入阶段以返回最终输出。

如果您没有该功能,您可以处理游标结果(此处使用 shell Cursor.map() 显示)并在进一步处理之前转换文档。任何迭代器方法都可以,而且大多数驱动程序都有一个 Cursor.map() .在这里并不是那么重要,因为在这种情况下聚合管道会生成一个文档。

在现代 shell 版本中工作的 JavaScript 方式是简单地应用 .reduce()在数组上并将输出对象转换为所需的对象输出。它基本上与服务器执行的操作完全相同,但只是在客户端代码中。

两种形式都返回所需的结果:

{
    "1" : 1.0,
    "A" : 2.0,
    "B" : 1.0
}

关于javascript - 如何比较数组并计算匹配项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49964519/

相关文章:

node.js - MEAN 堆栈安装给出 304 和 404s

arrays - MongoDB聚合匹配非空数组

java - 尝试查询 MongoDB 数据时出现问题

javascript - React (NextJS) 函数未定义

javascript - 在克隆字段上添加删除按钮

javascript - jQuery 点击被触发两次

javascript - 聚合将文档键展开为新文档

javascript - Bootstrap Datepicker - 使用多日期设置开始日期

node.js - 如何使用node.js正确地将新元素推送到mongodb数组

mongodb - mongodb 中的集合扫描是什么?