使用pymongo根据一个值查询两个字段的总和

Posted

技术标签:

【中文标题】使用pymongo根据一个值查询两个字段的总和【英文标题】:Query Sum of two fields based on a value using pymongo 【发布时间】:2022-01-15 07:25:10 【问题描述】:

基本上,我有一个带有嵌套对象数组的对象数组。我想根据"userID""flashcardReversed.value" + "flashcardReversed.count"之和查询数据。

用户将提供所需的用户 ID 和(值 + 计数)值。

这是我的数据:

[
  
    "_id": "608642db80a36336946620aa",
    "userID": "user1",
    "title": "title2",
    "flashcardReversed": [
      
        "_id": "608d5b290e635ece6828141X",
        "front": "2front",
        "back": "2back",
        "value": 1,
        "count": 2
      ,
      
        "_id": "608t5b290e635ece6828141Y",
        "front": "2frontReversed",
        "back": "2backReversed",
        "value": 2,
        "count": 3
      ,
      
        "_id": "608a5b31a3f9806de253726X",
        "front": "2front2",
        "back": "2back2",
        "value": 3,
        "count": 4
      ,
      
        "_id": "608a5b31a3f9806de253726Y",
        "front": "2frontReversed2",
        "back": "2backReversed2",
        "value": 4,
        "count": 5
      
    ]
  ,
  
    "_id": "608642db80a36336946620aa",
    "userID": "user1",
    "title": "title3",
    "flashcardReversed": [
      
        "_id": "608d5b290e635ece6828142X",
        "front": "2front",
        "back": "2back",
        "value": 12,
        "count": 6
      ,
      
        "_id": "608t5b290e635ece6828143Y",
        "front": "2frontReversed",
        "back": "2backReversed",
        "value": 21,
        "count": 7
      ,
      
        "_id": "608a5b31a3f9806de253727X",
        "front": "2front2",
        "back": "2back2",
        "value": 34,
        "count": 8
      ,
      
        "_id": "608a5b31a3f9806de253729Y",
        "front": "2frontReversed2",
        "back": "2backReversed2",
        "value": 42,
        "count": 9
      
    ]
  ,
  
    "_id": "608642db80a36336946620aa",
    "userID": "user2",
    "title": "title4",
    "flashcardReversed": [
      
        "_id": "608d5b290e635ece6828131X",
        "front": "2front",
        "back": "2back",
        "value": 41,
        "count": 10
      ,
      
        "_id": "608t5b290e635ece6828161Y",
        "front": "2frontReversed",
        "back": "2backReversed",
        "value": 54,
        "count": 11
      ,
      
        "_id": "608a5b31a3f9806de253526X",
        "front": "2front2",
        "back": "2back2",
        "value": 63,
        "count": 12
      ,
      
        "_id": "608a5b31a3f9806de253326Y",
        "front": "2frontReversed2",
        "back": "2backReversed2",
        "value": 29,
        "count": 13
      
    ]
  
]

例如查询应该是这样的:


  "userID": "$eq": provided_user_id
  "flashcardReversed.value" + "flashcardReversed.count": "$eq": provided_value_plus_count
    

是否可以使用 pymongo 来实现这一点?

【问题讨论】:

输出已损坏(字段后缺少一些逗号)但_id 对于所有三个文档都是相同的——假设它们应该是集合中的三个独立文档而不是数组1 个文档中的 3 个项目。另外:您是要匹配为用户找到的所有文档的总和,还是每个文档(您的数据表明 userID 可以显示在多个文档中)。请澄清一下。 json 无效,如果您可以使用 this 之类的东西来检查它 【参考方案1】:

假设这里的唯一性是userID+title,并且我们希望在flashcardReversed 字段和多个title 之间进行聚合,即为用户获取一个数字,那么这是一个解决方案。

var targetUser = "user1";
var targetAmt = 163;

c = db.foo.aggregate([
    $match: "userID": targetUser // will fetch one OR MORE records...                                       
    ,$addFields: 
        X: $reduce: 
            input: "$flashcardReversed",
            initialValue: 0,
                // $$value is the running result of the reduce operation ($add)
                // $$this is the current object from the array; it just so
                // happens that a field in your object is named "value" so
                // do not confuse the two.  The expression below translates
                // to:  n = n + oneitem.value + oneitem.count
                in:$add: [ "$$value", "$$this.value", "$$this.count" ] 
        
    

    // Now, add up X over multiple docs.  We can group by null because we                                       
    // know ONLY docs where userID = targetUser are coming through:                                             
    ,$group: _id: null, n: $sum: "$X"

    // ...and match against input:                                                                              
    ,$match: n: targetAmt                                                                                 
]);

上述解决方案演示了单独的 $reduce$group 行为,但这些行为可以组合使用。另外,让我们探讨一下要为目标数量获取多个用户需要做什么:

var targetUser = ["user1","user2"];
var targetAmt = 163;

c = db.foo.aggregate([
    $match: "userID": $in: targetUser                                 

    // Combine the $group and $reduce together.  Saves a stage!  Also, since                                    
    // more than one userID can come through, we must change _id to group                                       
    // by $userID instead of null:                                                                              
    ,$group: _id: "$userID", n: $sum: $reduce: 
            input: "$flashcardReversed",
            initialValue: 0,
                in:$add: [ "$$value", "$$this.value", "$$this.count" ] 
       

    // Probably want to use >= or <= instead of exact match sometimes                                           
    // so here is an example of that:                                                                           
    ,$match: $expr: $gte: ["$n", targetAmt] 
]);

 "_id" : "user1", "n" : 163 
 "_id" : "user2", "n" : 233 

【讨论】:

【参考方案2】:

查询

匹配用户 ID 过滤数组并只保留(= (+ value count) 3)的成员 您可以将 3 替换为变量或任何数字 如果过滤后数组为空,则拒绝文档 在最终结果中,我们只在数组中保留通过的那些,并且只保留至少有 1 个通过的文档

*不确定你是否想要这个

Test code here

aggregate(
["$match": "userID": "$eq": "user1",
 "$set":  
   "flashcardReversed": 
     "$filter": 
       "input": "$flashcardReversed",
        "cond": 
        "$eq": ["$add": ["$$this.value", "$$this.count"], 3],
 "$match": "$expr": "$ne": ["$flashcardReversed", []]])

【讨论】:

嗨@Takis_,是的,该解决方案工作正常。但是,它只从其中一个对象返回。假设有两个 userID 为 user1 的对象,它们都有一些 flashcardReversed 对象,其值和计数总和为 3。您的查询仅从其中一个对象返回 flashcardReversed 对象。这里cmql.org/play/?q=1639239808949,你可以看到我想说的。 我明白了你的意思,但是在你的示例数据中你有相同的_id,这在 MongoDB 中不起作用(只插入了 1 个),我在修复它后重新运行它,并且我拿了this,看起来不错,有两个对象,如果你不喜欢这个,你可以$unwind还有数组,如果你被卡住发送预期的输出,所以我们知道你到底需要什么 谢谢@Takis_ :)

以上是关于使用pymongo根据一个值查询两个字段的总和的主要内容,如果未能解决你的问题,请参考以下文章

PyMongo - 使用变量来 sql 'LIKE' 查询

pymongo 存取

如何使用正则表达式查询 pymongo 以获取仅包含数字的值

pymongo bugfix后记

根据来自另一个模型的异步查询在模型中设置虚拟字段

聚合查询中由另一个字段分组的两个 int 数组的总和