在投影中将子字段提升到顶级而不列出所有键

时间:2017-06-26 03:58:20

标签: javascript mongodb aggregation-framework

我有{'a': 1, 'z': {'b': 2, 'c': 3,}}等文件。

我想要{'a': 1, 'b': 2, 'c': 3}

我可以用

做到这一点
aggregate({'$project': {'b': '$z.b', 'c': '$z.c'}})

是否可以在不手动列出子文档中的所有键的情况下执行此操作?

1 个答案:

答案 0 :(得分:3)

使用MongoDB 3.4,您可以将$objectToArray$arrayToObject$replaceRoot一起使用以更改此内容:

db.wish.aggregate([
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": {
        "$concatArrays": [
          [{ "k": "a", "v": "$a" }],
          { "$objectToArray": "$z" }
        ]
      }
    }
  }}
])

甚至这个长咒语甚至没有指定"a"属性:

db.wish.aggregate([
  { "$replaceRoot": {
    "newRoot": {
      "$arrayToObject": {
        "$reduce": {
          "input": {
            "$filter": {
              "input": { "$objectToArray": "$$ROOT" },
              "as": "r",
              "cond": { "$ne": [ "$$r.k", "_id" ] }
            }
          },
          "initialValue": [],
          "in": {
            "$concatArrays": [
              "$$value",
              { "$cond": {
                "if": {  "$gt": [ "$$this.v", {} ] },
                "then": { "$objectToArray": "$$this.v" },
                "else": ["$$this"]
              }}
            ]
          }  
        } 
      }
    }
  }}
])  

两者都产生:

{ "a" : 1, "b" : 2, "c" : 3 }

在将来的版本中不一定有必要使用$concatArrays,因为会有一个$mergeObjects运算符可以使它更清晰。

但你可以基本上只是在客户端代码中做同样的事情。例如,在shell的JavaScript中:

db.wish.find().map( doc => (
  Object.assign({ a: doc.a }, doc.z )
))

或者再次没有"a"的版本:

db.wish.find().map( doc => 
  Object.keys(doc).filter(k => k !== '_id').map(k => 
   ( typeof(doc[k]) === "object" ) ? 
     Object.keys(doc[k]).map(i => ({ [i]: doc[k][i] }))
       .reduce((acc, curr) => Object.assign(acc,curr),{})
     : { [k]: doc[k] }
  ).reduce((acc,curr) => Object.assign(acc,curr),{})
)

生成相同的输出

{ "a" : 1, "b" : 2, "c" : 3 }