使文档密钥最接近搜索值

时间:2019-03-06 07:33:01

标签: mongodb aggregation-framework

我有以下收藏:

{
    "_id" : "Stats1",
    "minutes" : {
        "0" : [
            {
                "0" : {
                    "f" : 1,
                    "t" : 0,
                    "v" : "0"
                }
            }
        ],
        "22" : [
            {
                "2" : "1"
            }
        ],
        "29" : [
            {
                "32" : "2"
            }
        ],
        "38" : [
            {
                "40" : "3"
            }
        ]
    }
}

当我尝试时:

 db.stats.aggregate()
  .project({"_id":"1", "minArray": {"$objectToArray": "$minutes"}})

我收到错误消息:

“ $ objectToArray需要输入文档,找到:数组”

当我尝试时:

 db.stats.aggregate()
  .project({"_id":"1", "minArray": {"$arrayToObject": "$minutes"}})

我收到错误消息:

“ $ arrayToObject需要一个数组输入,找到了:object”

enter image description here

我想获取精确到或小于30分钟的最接近值:

{ "minute" : "29", "value" : [{ "32" : "2"}] }

1 个答案:

答案 0 :(得分:1)

所以出现错误是因为没有$match,您的管道正在尝试访问其他没有预期结构的文档。但这确实是需要分开的东西。

要从最终目的中真正回答您的问题,您需要一个这样的管道:

var _id = "Stats1";
var target = 30;

db.stats.aggregate([
  { "$match": { "_id" : _id } },
  { "$replaceRoot": {
    "newRoot": {
      "$let": {
        "vars": {
          "working": { 
            "$map": {
              "input": { "$objectToArray": "$minutes" },
              "in": {
                "k": { "$toInt": "$$this.k" },
                "v": "$$this.v",
                "diff": { "$abs": { "$subtract": [ target, { "$toInt": "$$this.k" }] } }
              }         
            }
          }
        },
        "in": {
          "$arrayToObject": {
            "$map": {
              "input": {
                "$filter": {
                  "input": {
                    "$objectToArray": {
                      "$arrayElemAt": [
                        "$$working",
                        { "$indexOfArray": [ "$$working.diff", { "$min": "$$working.diff" } ] }
                      ]
                    }
                  },
                  "cond": { "$ne": [ "$$this.k", "diff" ] }
                }
              },
              "in": {
                "k": { "$cond": [{ "$eq": [ "$$this.k", "k"] }, "minute", "value" ] },
                "v": { "$cond": [{ "$eq": [ "$$this.k", "k"] }, { "$toString": "$$this.v" }, "$$this.v" ] }
              }
            }
          }
        }
      }
    }
  }}
])

当然会返回所需的输出:

{ "minute" : "29", "value" : [ { "32" : "2" } ] }

依次执行您最初尝试的$objectToArray,但是随后您需要将该 key "k"值实际转换为数字以进行比较。您还需要根据要搜索的值(在这种情况下为30)计算出它们的差异。这将为您提供数组形式的数据的“工作”副本,这对于接下来的输入阶段很重要。

下一节基本上是从缩进级别向内阅读的,以最好地理解顺序。

首先,您基本上想从该工作数组中提取元素,其中差异(使用$abs,使正负相同)是$min的最小值。这样就给出了$indexOfArray中第一个匹配项的位置,并将其与$arrayElemAt一起使用来从工作数组中返回单个选定的元素。

我们不需要该对象中的所有字段,因此$objectToArray会将单个对象转换为"k""v"配对的对象,第一步是转到{{3} },其中 key difference 字段,并将其从该列表中删除。

接下来,您要重命名字段并更改某些数据格式,因此$filter迭代其余数组(仅两个条目),分配可读名称并设置"minute"的字符串格式。

最后,它可以作为最终输出返回到$map对象。由于我们想多次引用该"working"数组,因此我们在$arrayToObject中进行了声明,这使我们能够做到这一点。并且由于所有这些都是一个表达式,可以将您想要的内容输出为文档,因此您可以使用$let将其包装为“表达式”,因为它基本上是单个预期参数。