Promote subfields to top level in projection without listing all keys

前端 未结 1 1377
说谎
说谎 2020-12-11 10:24

I have documents like {\'a\': 1, \'z\': {\'b\': 2, \'c\': 3,}}.

I want {\'a\': 1, \'b\': 2, \'c\': 3}.

I can do this with

相关标签:
1条回答
  • 2020-12-11 11:15

    With MongoDB 3.4 you can use $objectToArray and $arrayToObject with $replaceRoot in order to change this:

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

    Or even this long incantation without even specifying the "a" property:

    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"]
                  }}
                ]
              }  
            } 
          }
        }
      }}
    ])  
    

    Both produce:

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

    The funny use of $concatArrays should not be necessary in future versions since there will be a $mergeObjects operator which will make that a bit cleaner.

    But you can basically just do the same thing in client code pretty simply. For example in JavaScript for the shell:

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

    Or the version without the "a" again:

    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),{})
    )
    

    Produces the same output

    { "a" : 1, "b" : 2, "c" : 3 }
    
    0 讨论(0)
提交回复
热议问题