【问题标题】:Query Sum of two fields based on a value using pymongo使用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 之类的东西来检查它

标签: python mongodb mongodb-query pymongo


【解决方案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_ :)
    猜你喜欢
    • 2019-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多