将数组索引与提供的对象变量匹配

时间:2017-08-30 22:09:26

标签: node.js mongodb aggregation-framework

我有这样的外部对象:

var obj = {
    a: 2,
    b: 0,
    c: 1
}

此外,我收集了以obj为参考的字段“ref”。 obj对象中的值被视为集合中数组字段的索引。

{
    ref: "a",
    array: ["xyz", "abc", "def"]
}
{
    ref: "b",
    array: ["sde", "xda", "era"]
}
{
    ref: "c",
    array: ["wwe", "aea", "fre"]
}

我想聚合集合,只根据obj中指定的索引从数组中获取一个值。

.aggregate([
{
    $project: {
        code: $arrayElemAt: ["$array", { $let: {
            vars: { obj: obj },
            in: "$$obj.$ref" //Field path used as variable field name of object
        }}]
    }
}])

但我得到以下错误:

MongoError: FieldPath field names may not start with '$'.

我认为以下内容是相关的:in: "$$obj.$ref"

汇总的理想结果是:

{
    ref: "a",
    code: "def"
}
{
    ref: "b",
    code: "sde"
}
{
    ref: "c",
    code: "aea"
}

1 个答案:

答案 0 :(得分:1)

你有点走在正确的轨道上,但是你在错误的地方使用$let。因此,一般的想法是应用$let“围绕”表达式来评估而不是“内部”它们。

另外,在将作为输入值输入管道之前,您确实应该使对象与“数组”匹配。运算符使用“数组”,并且不能很好地处理由变量名称查找“对象”。

所以这可能是最短的,因为$indexOfArray$arrayElemAt都可用,以便匹配和提取值:{/ p>

var obj = {
    a: 2,
    b: 0,
    c: 1
};

var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));
// Outputs as
// [{ "k" : "a", "v" : 2 }, { "k" : "b", "v" : 0  }, { "k" : "c", "v" : 1 }]

db.collection.aggregate([
  { "$addFields": {
    "array": {
      "$let": {
        "vars": { "obj": input },
        "in": {
          "$arrayElemAt": [
            "$array",
            { "$arrayElemAt": [
              "$$obj.v",
              { "$indexOfArray": [ "$$obj.k", "$ref" ] }
            ]}
          ]
        }
      }
    }  
  }}
])

如果您实际拥有MongoDB 3.4.4或更高版本,那么您“可以”在内部应用$objectToArray,但它并没有真正添加任何值(它只是真的炫耀):

var obj = {
    a: 2,
    b: 0,
    c: 1
};

//var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));

db.collection.aggregate([
  { "$addFields": {
    "array": {
      "$let": {
        "vars": { "obj": { "$objectToArray": obj } },
        "in": {
          "$arrayElemAt": [
            "$array",
            { "$arrayElemAt": [
              "$$obj.v",
              { "$indexOfArray": [ "$$obj.k", "$ref" ] }
            ]}
          ]
        }
      }
    }  
  }}
])

如果您的MongoDB不支持$indexOfArray,那么您始终可以应用$map$filter$arrayElemAt从这些组合语句中提取“第一个匹配项”:< / p>

var obj = {
    a: 2,
    b: 0,
    c: 1
};

var input = Object.keys(obj).map(k => ({ k: k, v: obj[k] }));

db.collection.aggregate([
  { "$addFields": {
    "array": {
      "$arrayElemAt": [
        "$array",
        { "$arrayElemAt": [
          { "$map": {
            "input": {
              "$filter": {
                "input": input,
                "cond": { "$eq": [ "$$this.k", "$ref" ] }
              }
            },
            "in": "$$this.v"
          }},
          0
        ]}
      ]
    }  
  }}
])

这也删除了$let的任何用法,因为我们没有直接针对除"input"$filter以外的任何内容提供变量。

无论应用哪个,都会使用输入obj中提供的索引位置(作为数组强制为input)并返回该数组条目的相同输出:

/* 1 */
{
    "_id" : ObjectId("59a76d6fad465e105d9136dc"),
    "ref" : "a",
    "array" : "def"
}

/* 2 */
{
    "_id" : ObjectId("59a76d6fad465e105d9136dd"),
    "ref" : "b",
    "array" : "sde"
}

/* 3 */
{
    "_id" : ObjectId("59a76d6fad465e105d9136de"),
    "ref" : "c",
    "array" : "aea"
}