每个文档级别的聚合 mongodb
Posted
技术标签:
【中文标题】每个文档级别的聚合 mongodb【英文标题】:Aggregation at each document level mongodb 【发布时间】:2020-03-19 20:55:22 【问题描述】:我有一个这样的文件列表
[
"_id": "5dbc95f921d7625303fe2369",
"name": "John",
"itemsPurchased": [
"offer": "o1",
"items": ["p1"]
,
"offer": "o1",
"items": ["p1"]
,
"offer": "o1",
"items": ["p2"]
,
"offer": "o2",
"items": ["p1"]
,
"offer": "o7",
"items": ["p1"]
]
,
"_id": "zbc95f921d7625303fe2363",
"name": "Doe",
"itemsPurchased": [
"offer": "o1",
"items": ["p11"]
,
"offer": "o1",
"items": ["p11"]
,
"offer": "o2",
"items": ["p13"]
,
"offer": "o1",
"items": ["p22"]
,
"offer": "o2",
"items": ["p11"]
,
"offer": "o3",
"items": ["p11"]
]
]
我正在尝试计算每个客户对独特产品的独特报价,期望结果如下:
[
"_id": "5dbc95f921d7625303fe2369",
"name": "John",
"offersAndProducts":
"o1":2,
"o2":2,
"o3":1
,
"_id": "zbc95f921d7625303fe2363",
"name": "Doe",
"offersAndProducts":
"o1":2,
"o2":1,
"o7":1
]
我想对每个文档应用聚合,在对 itemsPurchased 执行 $unwind 后,在 items 上应用 $group,然后 on offer 以消除重复:
"$group" :
"_id" :
"item" :
"$arrayElemAt" : [
"$itemsPurchased.item",
0.0
]
,
"count" :
"$sum" : 1.0
,
"offer" : "$itemsPurchased.offer"
那么,
"$group" :
"_id" : "$_id.offer",
"count" :
"$sum" : 1.0
这给出了所有文档的产品和报价数组:
[
o1:4,o2:3,o3:1,o7:1
]
但我在文档级别需要它。
尝试了$addFeild
,但$unwind
和$match 运算符给出了无效错误。
还有其他方法可以实现吗?
【问题讨论】:
我不了解输入和所需的输出。暂时只关注John
,您如何从输入中获得"o1":2,"o2":2,"o3":1
的输出? o7
怎么了?
@BuzzMoschetti:抱歉打错了。预期输出为 [ "_id": "zbc95f921d7625303fe2363", "name": "Doe", "offersAndProducts": "o1": 2, "o2": 2, "o3": 1 , "_id" :“5dbc95f921d7625303fe2369”,“名称”:“约翰”,“offersAndProducts”:“o1”:2,“o2”:1,“o7”:1]
【参考方案1】:
您需要运行$unwind 和$group 两次。要仅计算唯一的 items
,您可以使用 $addToSet。要动态构建密钥,您需要使用 $arrayToObject:
db.collection.aggregate([
$unwind: "$itemsPurchased"
,
$unwind: "$itemsPurchased.items"
,
$group:
_id:
_id: "$_id",
offer: "$itemsPurchased.offer"
,
name: $first: "$name" ,
items: $addToSet: "$itemsPurchased.items"
,
$group:
_id: "$_id._id",
name: $first: "$name" ,
offersAndProducts: $push: k: "$_id.offer", v: $size: "$items"
,
$project:
_id: 1,
name: 1,
offersAndProducts: $arrayToObject: "$offersAndProducts"
])
Mongo Playground
【讨论】:
非常感谢您的查询,我注意到的一件事是,忽略了包含空 itemsPurchased 的文档,为了使其正常工作,我使用空 offer 和 item "_id": "zbc95f921d7625303fe2334", "name": "Doe", "itemsPurchased": [ "offer": "", "items": [ "" ] ] 返回: "_id": "zbc95f921d7625303fe2334", "name ": "Doe", "offersAndProducts": "": 1 并忽略 "" 键。我们还有其他方法可以包含这些文档吗? @user2033575 试试这个:docs.mongodb.com/manual/reference/operator/aggregation/unwind/… 我试过了 :) 这里是 sn-p:mongoplayground.net/p/ofl4h-dzwlu,它因为空数组没有提供键而失败,但我尝试用一些随机键 'q 填充值', 有效。如果值为 0 或 keynot null,MongoDB 是否允许条件推送? $unwind and $group back on_id
是一种反模式,因为您正在重新创建原始文档,而且效率很低。请参阅我的解决方案,了解如何通过直接处理数组来完成此操作。
@user2033575 你可以接受另一个答案,如果你觉得更好,因为我的投票被否决了【参考方案2】:
一般来说,$unwind
是一个数组,然后是 $group
在原始 _id
上的反模式,因为大多数操作可以在一个阶段直接在数组上完成。这是这样一个阶段的样子:
$addFields:
offers:$arrayToObject:
$map:
input:$setUnion:"$itemsPurchased.offer",
as:"o",
in:[
"$$o",
$size:$setUnion:$let:
vars:items:$filter:
input:"$itemsPurchased",
cond:$eq:["$$this.offer","$$o"]
,
in:$reduce:
input:"$$items",
initialValue:[],
in:$concatArrays:["$$value","$$items.items"]
]
这样做是创建一个数组,其中每个元素都是一个双元素数组(这是$arrayToObject
可以转换为第一个元素是键名,第二个是值的对象的语法)并且输入是唯一的集合报价,我们为每个产品累积一系列产品,去除重复项(使用$setUnion
),然后获得结果的大小。这对您的输入产生的结果是:
"offers" :
"o1" : 2,
"o2" : 2,
"o3" : 1
【讨论】:
以上是关于每个文档级别的聚合 mongodb的主要内容,如果未能解决你的问题,请参考以下文章
论文速递NAACL2022- 文档级事件论元抽取的双流AMR增强模型
如何在 mlab 中解决此事务错误? [MongoError: Transaction numbers are ... 支持文档级锁定]
如何在 mlab 中解决此事务错误? [MongoError: Transaction numbers are ... 支持文档级锁定]