Python 如何使用count all elements和Group by one request进行pymongo聚合

Python 如何使用count all elements和Group by one request进行pymongo聚合,python,mongodb,pymongo,Python,Mongodb,Pymongo,我有一个包含如下字段的集合: { "response": [ { "tag": "location", "tag_list": [ { "count": 31, "phrase": "philadelphia" }, {

我有一个包含如下字段的集合:

{
    "response": [
        {
            "tag": "location",
            "tag_list": [
                {
                    "count": 31,
                    "phrase": "philadelphia"
                },
                {
                    "count": 15,
                    "phrase": "usa"
                }
             ]
        },
        {
            "tag": "organization",
            "tag_list": [ ... ]
        },
        {
            "tag": "person",
            "tag_list": [ ... ]
        },
    ]
}
db.collection.aggregate([
  { "$addFields": {
    "tags": { "$objectToArray": "$tags" }
  }},
  { "$unwind": "$tags" },
  { "$unwind": "$tags.v" },
  { "$group": {
    "_id": {
      "tag": "$tags.k",
      "phrase": "$tags.v.word"
    },
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.tag",
    "tag_list": {
      "$push": {
        "count": "$count",
        "phrase": "$_id.phrase"
      }
    }
  }}
])
{
“_id”:“5cf54857bbc85fd0ff5640ba”,
“图书id”:“5cf172220fb516f706d00591”,
“标签”:{
“人”:[
{“开始匹配”:209,“长度匹配”:6,“单词”:“kimmel”}
],
“组织”:[
{“开始匹配”:107,“长度匹配”:12,“单词”:“费城”},
{“开始匹配”:209,“长度匹配”:13,“单词”:“kimmel中心”}
],
“地点”:[
{“开始匹配”:107,“长度匹配”:12,“单词”:“费城”}
]
},
“已删除”:false
}
我想收集分类中的不同单词并进行计数。 因此,输出应如下所示:

{
    "response": [
        {
            "tag": "location",
            "tag_list": [
                {
                    "count": 31,
                    "phrase": "philadelphia"
                },
                {
                    "count": 15,
                    "phrase": "usa"
                }
             ]
        },
        {
            "tag": "organization",
            "tag_list": [ ... ]
        },
        {
            "tag": "person",
            "tag_list": [ ... ]
        },
    ]
}
db.collection.aggregate([
  { "$addFields": {
    "tags": { "$objectToArray": "$tags" }
  }},
  { "$unwind": "$tags" },
  { "$unwind": "$tags.v" },
  { "$group": {
    "_id": {
      "tag": "$tags.k",
      "phrase": "$tags.v.word"
    },
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.tag",
    "tag_list": {
      "$push": {
        "count": "$count",
        "phrase": "$_id.phrase"
      }
    }
  }}
])
这样的管道工作:

def管道功能(标记):
返回[
{'$replaceRoot':{'newRoot':'$tags'}},
{'$unwind':'${}'。格式(标记)},
{'$group':{''U id':'${}.word'。格式(标记),'count':{'$sum':1}},
{'$project':{'PHASE':'$\'id','count':1',\'id':0},
{$sort':{'count':-1}
]
但它会对每个标签发出请求。我想知道如何在一个请求中完成它。 感谢您的关注。

如前所述,问题数据与当前声明的管道流程稍有不匹配,因为只能在数组上使用,并且问题中显示的
标记不是数组

对于问题中提供的数据,您基本上需要这样的管道:

{
    "response": [
        {
            "tag": "location",
            "tag_list": [
                {
                    "count": 31,
                    "phrase": "philadelphia"
                },
                {
                    "count": 15,
                    "phrase": "usa"
                }
             ]
        },
        {
            "tag": "organization",
            "tag_list": [ ... ]
        },
        {
            "tag": "person",
            "tag_list": [ ... ]
        },
    ]
}
db.collection.aggregate([
  { "$addFields": {
    "tags": { "$objectToArray": "$tags" }
  }},
  { "$unwind": "$tags" },
  { "$unwind": "$tags.v" },
  { "$group": {
    "_id": {
      "tag": "$tags.k",
      "phrase": "$tags.v.word"
    },
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.tag",
    "tag_list": {
      "$push": {
        "count": "$count",
        "phrase": "$_id.phrase"
      }
    }
  }}
])
同样,根据注释,由于
标记
实际上是一个对象,因此您实际需要的是将其转换为一个项目数组,以便根据问题提出的子键收集数据

在您当前的管道中使用的似乎表明它在这里是合理使用的,因为它可以从MongoDB 3.4的后续补丁版本中获得,这是您现在应该在生产中使用的最低版本

这实际上与名字所说的差不多,并生成一个数组(或者更像python的“列表”)条目,这些条目被分解成键和值对。这些基本上是对象的“列表”(或“dict”条目),它们分别具有键
k
v
。第一个管道阶段的输出在提供的文档中如下所示:

{
  "book_id": "5cf172220fb516f706d00591",
  "tags": [
    {
      "k": "person",
      "v": [
        {
          "start_match": 209,
          "length_match": 6,
          "word": "kimmel"
        }
      ]
    }, {
      "k": "organization",
      "v": [
        {
          "start_match": 107,
          "length_match": 12,
          "word": "philadelphia"
        }, {
          "start_match": 209,
          "length_match": 13,
          "word": "kimmel center"
        }
      ]
    }, {
      "k": "location",
      "v": [
        {
          "start_match": 107,
          "length_match": 12,
          "word": "philadelphia"
        }
      ]
    }
  ],
  "deleted" : false
}
因此,您应该能够看到现在如何轻松访问这些
k
值并在分组中使用它们,当然
v
也是标准数组。所以它只是两个阶段,如图所示,然后是两个阶段。第一个用于通过组合键进行收集,第二个用于根据主分组键进行收集,同时将其他累积添加到该条目内的“列表”中

当然,上面清单的输出并不完全是您在问题中所要求的,但是数据基本上是存在的。您可以选择添加一个或阶段,将
\u id
键重命名为最终聚合阶段:

  { "$addFields": {
    "_id": "$$REMOVE",
    "tag": "$_id"
  }}
或者简单地做一些pythonic操作,在光标输出上进行一些列表理解:

cursor = db.collection.aggregate([
  { "$addFields": {
    "tags": { "$objectToArray": "$tags" }
  }},
  { "$unwind": "$tags" },
  { "$unwind": "$tags.v" },
  { "$group": {
    "_id": {
      "tag": "$tags.k",
      "phrase": "$tags.v.word"
    },
    "count": { "$sum": 1 }
  }},
  { "$group": {
    "_id": "$_id.tag",
    "tag_list": {
      "$push": {
        "count": "$count",
        "phrase": "$_id.phrase"
      }
    }
  }}
])

output = [{ 'tag': doc['_id'], 'tag_list': doc['tag_list'] } for doc in cursor]

print({ 'response': output });
最终输出为“列表”,可用于
响应

{
  "tag_list": [
    {
      "count": 1,
      "phrase": "philadelphia"
    }
  ],
  "tag": "location"
},
{
  "tag_list": [
    {
      "count": 1,
      "phrase": "kimmel"
    }
  ],
  "tag": "person"
},
{
  "tag_list": [
    {
      "count": 1,
      "phrase": "kimmel center"
    }, {
      "count": 1,
      "phrase": "philadelphia"
    }
  ],
  "tag": "organization"
}

注意,使用列表理解方法,您可以更好地控制作为输出的“键”的顺序,因为MongoDB本身只需在投影中添加新的键名,使现有键保持优先顺序。如果这类事情对你很重要,那就是。尽管它确实不应该是这样,因为所有对象/Dict类结构都不应该被认为具有任何设置的键顺序。这就是数组(或列表)的用途

“像这样的管道工作…”-实际上它不能像现在这样显示,因为在问题中,
标记
显示为对象
{}
,而不是数组
[]
。因此,
{$unwind:“$tags”}
将抛出一个错误(
$unwind
仅适用于实际数组)。我建议您的实际数据的结构稍有不同,或者您声称的管道尝试实际上不起作用。您需要清楚地指定这两个选项中哪一个是正确的。在这里对您的问题进行编辑是合适的。这很奇怪,但是当我删除
{$unwind:“$tags”}
时,输出不会改变。也许这是因为清单在里面。但你是对的<代码>{$unwind:“$tags”}
是不必要的。我把它从我的请求中删除,但我的回答仍然是真实的。所有的工作。这是一个绝妙的回答。没什么可补充的。谢谢你的快速回复+1.