2

I am writing an application to consume the data in my Mongo database, but I need to process the data in a flat format. My document has a few arrays in it that I would like to turn into a string array, but can't find a way to do so with a mongo query.

Document:

{
  name: 'Hello World',
  tags: ['simple', 'easy']
}

Desired Output:

{
  name: 'Hello World',
  tags: 'simple,easy',
}
3
  • 1
    I think you'd need to use mapReduce to do that. Probably better to do that client-side if possible. Commented Feb 16, 2013 at 21:30
  • 1
    Why not just do it as you retrieve the documents? Commented Feb 16, 2013 at 21:32
  • What @WiredPrairie said. Just use toString(). Commented Feb 16, 2013 at 22:05

3 Answers 3

4

It can be done natively in MongoDB aggregation with $reduce:

db.myCollection.aggregate([{
  $set: {
     tags: {
       $reduce: {
         input: "$tags",
         initialValue: "",
         in: {
           $concat : ["$$value", "," ,"$$this"]
         }
       }
     }
  }, {
  $project: {
     name: 1,
     tags: { $substr: [ "$tags", 1, -1 ] }
  }
}]);

There is no way to avoid extra separator (leading or trailing) when using $concat, so there is additional step with $substr to remove it.

  1. $concat : ["$$value", "," ,"$$this"] produces string ",a,b,c,d"
  2. $substr: [ "$tags", 1, -1 ] makes substring starting at index 1 with length -1, which is "as much as possible" in this context.

Also see documentation for $reduce, $concat, $substr.

Sign up to request clarification or add additional context in comments.

Comments

1

This is very doable with one stage. addFields will overwrite the $tags element since it already exists. Mongo Playground

{
"$addFields": {
  "tags": {
    "$reduce": {
      "input": "$tags",
      "initialValue": "",
      "in": {
        "$concat": [
          //add the running concatination of this array to the front
          "$$value",
          //add the current value after that
          "$$this",
          {
            //comma. if current index is the last element, don't add a comma to the end.
            $cond: {
              if: {
                "$eq": [
                  {
                    $subtract: [
                      {
                        $size: "$tags"
                      },
                      1
                    ]
                  },
                  {
                    $indexOfArray: [
                      "$tags",
                      "$$this"
                    ]
                  }
                ]
              },
              then: "",
              else: ","
            }
          }
        ]
      }
    }
  }
}

}

Comments

0

The only way I can think of achieve this using a aggregation query is below but its very static.

db.test.aggregate([
    { $unwind: "$tags" },
    { $group: { _id: "$_id", name: {$first: "$name"}, firstTag: {$first: "$tags"}, lastTag: {$last: "$tags"} } },
    { $project: { name: "$name", tags: { $concat: [ "$firstTag", ",", "$lastTag" ] } } }
]);

You can however achive this with a mapReduce:

db.test.mapReduce(
   function() {emit(this._id, this);},
   function(key, value) {return value}, {   
      out:"tags",
      finalize: function(key, reducedVal){
            reducedVal.tags = reducedVal.tags.join();
            return reducedVal;
      }
   }
)

> db.tags.find()
{ "_id" : ObjectId("5849a9f6a4db9c5811299d08"), "value" : { "name" : "Hello World", "tags" : "simple,easy" } }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.