Mongodb 匹配最接近搜索值的文档键

Mongodb 匹配最接近搜索值的文档键,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我有以下收藏: { "_id" : "Stats1", "minutes" : { "0" : [ { "0" : { "f" : 1, "t" : 0, "v" : "0" } } ], "22" :

我有以下收藏:

{
    "_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"}})
 db.stats.aggregate()
  .project({"_id":"1", "minArray": {"$arrayToObject": "$minutes"}})
我收到错误消息:

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

当我尝试时:

 db.stats.aggregate()
  .project({"_id":"1", "minArray": {"$objectToArray": "$minutes"}})
 db.stats.aggregate()
  .project({"_id":"1", "minArray": {"$arrayToObject": "$minutes"}})
我收到错误消息:

$arrayToObject需要数组输入,找到:object

我希望获得精确或低于30分钟的最接近值:

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

因此,出现错误的原因是,如果没有文件,您的管道正在尝试访问其他没有预期结构的文档。不过,这确实是需要单独解决的问题

要从最终目标实际回答您的问题,您需要这样的管道:

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" } ] }
按顺序执行最初尝试的操作,但随后需要将该键或
“k”
值实际转换为数值进行比较。您还需要计算与正在搜索的值之间的差值,在本例中为
30
。这将以数组形式为您提供数据的“工作”副本,这对于下一个输入阶段非常重要

下一节基本上是从缩进层次向内阅读,以便更好地理解顺序

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

我们不需要该对象中的所有字段,因此将单个对象转换为
“k”
“v”
成对对象,第一步是确定该键是差异字段的位置,并将其从该列表中删除

接下来,您需要重命名字段并更改一些数据格式,以便迭代剩余的数组(只有两个条目),为
“分钟”
分配可读名称并设置字符串格式


最后,这可以返回到一个对象作为最终输出。因为我们想多次引用该
“working”
数组,所以我们在其中声明允许我们这样做。因为所有这些都是一个表达式,它输出你想要的作为一个文档,你用它来包装“表达式”基本上是它的一个预期参数。

根据消息说,
$objectToArray
期望一个文档
{}
作为操作符的输入,但是你提供了一个数组作为输入。你没有告诉我们任何事情。如前所述,由于这不是
$objectToArray
的有效输入,因此可能会出现错误,您希望发生什么情况?如果您实际显示了预期的输出,这将非常有用。@NeilLunn为什么“分钟”不是有效的对象输入?这些错误基本上意味着集合中的其他文档不共享相同的数据类型。如果您对特定的
\u id
进行
$match
,则可以,但您需要查看收藏的其余部分,看看哪里是错误的。@NeilLunn thanx,您是对的