查询过滤器的Pymango聚合?

2024-10-02 22:28:09 发布

您现在位置:Python中文网/ 问答频道 /正文

所以我发现类似的问题,但不完全回答我要找的。如果这是复制品,请随意给我指一个合适的地方。你知道吗

我有一个收集这是一个“来源的真相”一些相当大的文件。我想做一些预过滤使用查询引擎之前,我得到我的主要分析。你知道吗

查询1:

仅检索以下位置的文档document.financials.entrycount文件gte 4美元。所以基本上,在一个文档中,我有一个子文档用于财务。我想用这个做过滤器。我只想返回条目数大于4的文档。你知道吗

问题2:

能够做数学,并将其与检索的数字进行比较。你知道吗

例如:

(totalAssets + totalCash) / (totalDebt + totalLiabilities) < .5 

这些数字在子文档中。你知道吗

终于可以把这些结合起来了。你知道吗

下面是一个示例文件,预计只包括季度财务数据。你知道吗

{
  "symbol": "AAWW",
  "quarterly_financials": {
    "2017-09-30": {
      "cashChange": -106467000,
      "cashFlow": 82299000,
      "costOfRevenue": 439135000,
      "currentAssets": 449776000,
      "currentCash": 176280000,
      "currentDebt": 196509000,
      "grossProfit": 96613000,
      "netIncome": -24162000,
      "operatingExpense": 43690000,
      "operatingGainsLosses": 378000,
      "operatingIncome": 52923000,
      "operatingRevenue": 535748000,
      "researchAndDevelopment": None,
      "shareholderEquity": 1575169000,
      "totalAssets": 4687302000,
      "totalCash": 175926000,
      "totalDebt": 2105344000,
      "totalLiabilities": None,
      "totalRevenue": 535748000
    },
    "2017-12-31": {
      "cashChange": 115584000,
      "cashFlow": 136613000,
      "costOfRevenue": 474565000,
      "currentAssets": 587586000,
      "currentCash": 291864000,
      "currentDebt": 218013000,
      "grossProfit": 153387000,
      "netIncome": 209448000,
      "operatingExpense": 46628000,
      "operatingGainsLosses": -95000,
      "operatingIncome": 106759000,
      "operatingRevenue": 627952000,
      "researchAndDevelopment": None,
      "shareholderEquity": 1789856000,
      "totalAssets": 4955462000,
      "totalCash": 294413000,
      "totalDebt": 2226999000,
      "totalLiabilities": None,
      "totalRevenue": 627952000
    },
    "2018-03-31": {
      "cashChange": -161460000,
      "cashFlow": 69125000,
      "costOfRevenue": 498924000,
      "currentAssets": 433193000,
      "currentCash": 130404000,
      "currentDebt": 223308000,
      "grossProfit": 91090000,
      "netIncome": 9612000,
      "operatingExpense": 50521000,
      "operatingGainsLosses": None,
      "operatingIncome": 40569000,
      "operatingRevenue": 590014000,
      "researchAndDevelopment": None,
      "shareholderEquity": 1792299000,
      "totalAssets": 5016832000,
      "totalCash": 136421000,
      "totalDebt": 2270870000,
      "totalLiabilities": None,
      "totalRevenue": 590014000
    },
    "2018-06-30": {
      "cashChange": 97525000,
      "cashFlow": 106786000,
      "costOfRevenue": 548491000,
      "currentAssets": 565191000,
      "currentCash": 227929000,
      "currentDebt": 245322000,
      "grossProfit": 117654000,
      "netIncome": -21150000,
      "operatingExpense": 47334000,
      "operatingGainsLosses": None,
      "operatingIncome": 70320000,
      "operatingRevenue": 664531000,
      "researchAndDevelopment": None,
      "shareholderEquity": 1776073000,
      "totalAssets": 5348343000,
      "totalCash": 234280000,
      "totalDebt": 2501488000,
      "totalLiabilities": None,
      "totalRevenue": 666145000
    }
  }
}

Tags: 文档nonecashflowtotaldebttotalcashnetincomegrossprofitcostofrevenue
1条回答
网友
1楼 · 发布于 2024-10-02 22:28:09

坦率地说,这里的主要问题是文档结构。归根结底,对于任何形式的数据库来说,通常都不能处理名为“by key”的“子文档”,这包括MongoDB。你知道吗

当在客户端代码中使用单个文档时,“按键查找”可能更有效,但是对于这样的自然“列表”,MongoDB使用类似“数组”或“集合”的结构工作得更好。你知道吗

聚合表达式

另一种可行的方法是使用聚合运算符(如^{})将此表单“强制”到“自然列表”中进行处理,以便您可以按列表中的条目数进行筛选:

collection.aggregate([
  { "$match": {
    "$expr": {
      "$gte": [
        { "$size": { "$objectToArray": "$quarterly_financials" } },
        4
      ]
    }
  }}
])

注意,这是使用mongodb3.6及更高版本的^{}。如果您没有那个支持版本,但是仍然有mongodb3.4更高版本的^{}(尽管文档说3.6实际上是在那些更高版本中),那么您可以使用^{}之类的东西来代替^{}或常规的find()。你知道吗

附加的计算表达式也是如此。底线是您仍然需要“数组转换”,以便实际处理和遍历那些“列表元素”。因此,如果只有那些满足条件的子条目加起来等于所需的四个,那么您将在数组元素上使用^{}条件进行更改:

collection.aggregate([
  { "$match": {
      "$expr": {
        "$gte": [
          { "$size": { 
            "$filter": {
              "input": { "$objectToArray": "$quarterly_financials" },
              "cond": {
                "$lt": [
                  { "$divide": [
                    { "$add": [ "$$this.v.totalAssets", "$$this.v.totalCash" ] },
                    { "$add": [ 
                      "$$this.v.totalDebt",
                      { "$ifNull": [ "$$this.v.totalLiabilities", 0 ] }
                    ]}
                  ]},
                  .5
                ]
              }
            }
          }},
          4
        ]
      }
  }}
])

因此,在强制一个数组之后,用^{}检查每个列表项,以确定使用^{}^{}等运算符的数学表达式在何处满足^{}的逻辑条件,然后使用^{}运算符考虑剩余的过滤数组的“长度”。你知道吗

另请注意,^{}本质上是将每个子文档转换为具有以下形式的列表:

   {
        "k" : "2018-06-30",
        "v" : {
            "cashChange" : 97525000,
            "cashFlow" : 106786000,
            "costOfRevenue" : 548491000,
            "currentAssets" : 565191000,
            "currentCash" : 227929000,
            "currentDebt" : 245322000,
            "grossProfit" : 117654000,
            "netIncome" : -21150000,
            "operatingExpense" : 47334000,
            "operatingGainsLosses" : null,
            "operatingIncome" : 70320000,
            "operatingRevenue" : 664531000,
            "researchAndDevelopment" : null,
            "shareholderEquity" : 1776073000,
            "totalAssets" : 5348343000,
            "totalCash" : 234280000,
            "totalDebt" : 2501488000,
            "totalLiabilities" : null,
            "totalRevenue" : 666145000
        }
    }

这意味着您要查找的所有内容都位于新转换的“list”中每个文档的"v"属性(或“value”)下。"k"属性当然是您用“sub document”表单命名的“key”。你知道吗

另外,^{}是处理属性的null(或Python的None)值的要求,或者在适当的情况下处理“missing”属性。你知道吗

JavaScript评估

不建议这样做,但是MongoDB不支持较新的操作符(如^{})的另一种替代方法是在服务器上使用JavaScript求值,如处理此类逻辑的^{}^{}。你知道吗

同样的原则也适用于必须先“强制”到数组形式中。(请原谅这里的“shell form”速记示例。所有其他语言的字符串):

collection.find(function() {
  var quarter = this.quarterly_financials;
  return Object.keys(quarter).filter( k => 
    ( 
      ( quarter[k].totalAssets + quarter[k].totalCash ) /
      ( quarter[k].totalDebt + ( quarter[k].totalLiabilites || 0 ) )
    ) < .5
  ).length >= 4
})

虽然“噪音更小”这并不是真正的最佳选择,因为在服务器上评估JavaScript表达式的“成本”远远高于自然语言聚合表达式。还有一种可能性是,某些环境和服务器配置实际上根本不允许您使用这种JavaScript表达式。你知道吗

另请注意,如果您确实希望在稍后的分析阶段进行“聚合”,则需要将该逻辑考虑到mapReduce,因为在聚合管道中不可能使用^{}查询表达式。你知道吗

交替结构

最后,由于一切都依赖于从您的“命名键”创建列表,因此更好的方法通常是首先以这种方式构造数据(排除extended JSON format):

{
  "symbol": "AAWW",
  "quarterly_financials": [
    { 
      "tranDate": { "$date": "2017-09-30T00:00:00Z"},
      "cashChange": -106467000,
      "cashFlow": 82299000,
      "costOfRevenue": 439135000,
      "currentAssets": 449776000,
      "currentCash": 176280000,
      "currentDebt": 196509000,
      "grossProfit": 96613000,
      "netIncome": -24162000,
      "operatingExpense": 43690000,
      "operatingGainsLosses": 378000,
      "operatingIncome": 52923000,
      "operatingRevenue": 535748000,
      "researchAndDevelopment": null,
      "shareholderEquity": 1575169000,
      "totalAssets": 4687302000,
      "totalCash": 175926000,
      "totalDebt": 2105344000,
      "totalLiabilities": null,
      "totalRevenue": 535748000
    },
    {
      "tranDate": { "$date": "2017-12-31T00:00:00Z" },
      "cashChange": 115584000,
      "cashFlow": 136613000,
      "costOfRevenue": 474565000,
      "currentAssets": 587586000,
      "currentCash": 291864000,
      "currentDebt": 218013000,
      "grossProfit": 153387000,
      "netIncome": 209448000,
      "operatingExpense": 46628000,
      "operatingGainsLosses": -95000,
      "operatingIncome": 106759000,
      "operatingRevenue": 627952000,
      "researchAndDevelopment": null,
      "shareholderEquity": 1789856000,
      "totalAssets": 4955462000,
      "totalCash": 294413000,
      "totalDebt": 2226999000,
      "totalLiabilities": null,
      "totalRevenue": 627952000
    },
    { 
      "tranDate": { "$date": "2018-03-31T00:00:00Z" },
      "cashChange": -161460000,
      "cashFlow": 69125000,
      "costOfRevenue": 498924000,
      "currentAssets": 433193000,
      "currentCash": 130404000,
      "currentDebt": 223308000,
      "grossProfit": 91090000,
      "netIncome": 9612000,
      "operatingExpense": 50521000,
      "operatingGainsLosses": null,
      "operatingIncome": 40569000,
      "operatingRevenue": 590014000,
      "researchAndDevelopment": null,
      "shareholderEquity": 1792299000,
      "totalAssets": 5016832000,
      "totalCash": 136421000,
      "totalDebt": 2270870000,
      "totalLiabilities": null,
      "totalRevenue": 590014000
    },
    { 
      "tranDate": { "$date": "2018-06-30T00:00:00Z" },
      "cashChange": 97525000,
      "cashFlow": 106786000,
      "costOfRevenue": 548491000,
      "currentAssets": 565191000,
      "currentCash": 227929000,
      "currentDebt": 245322000,
      "grossProfit": 117654000,
      "netIncome": -21150000,
      "operatingExpense": 47334000,
      "operatingGainsLosses": null,
      "operatingIncome": 70320000,
      "operatingRevenue": 664531000,
      "researchAndDevelopment": null,
      "shareholderEquity": 1776073000,
      "totalAssets": 5348343000,
      "totalCash": 234280000,
      "totalDebt": 2501488000,
      "totalLiabilities": null,
      "totalRevenue": 666145000
    }
  ]
}

由于它已经是一个“列表”,您只需跳过^{}部分(或类似的JavaScript):

collection.aggregate([
  { "$match": {
    "$expr": {
      "$gte": [
        { "$size": { 
          "$filter": {
            "input": "$quarterly_financials",
            "cond": {
              "$lt": [
                { "$divide": [
                  { "$add": [ "$$this.totalAssets", "$$this.totalCash" ] },
                  { "$add": [ 
                    "$$this.totalDebt",
                    { "$ifNull": [ "$$this.totalLiabilities", 0 ] }
                  ]}
                ]},
                .5
              ]
            }
          }
        }},
        4
      ]
    }
  }}
])

使用这种结构还有很多优点,其中大多数甚至可以完全避免计算表达式,并且能够使用自然查询表达式。事实上,如果你这里的条件不是需要依赖于这样一个“filtered”条件,那么这只是一个自然查询,如果您已经在存储时预先计算了每个列表项中的数学表达式。你知道吗

因此,要获得最佳的查询性能,必须考虑“结构”,因为以任何形式评估“”都会导致集合扫描,而且成本非常高。你知道吗

因此,“使用列表”在事物是列表,而计算是“静态”的情况下,则提前执行并存储它们,而不是在运行时进行计算。你知道吗

相关问题 更多 >