skip to Main Content

I have a document in a collection which have values in array.

{
  "name": "ABC",
  "details": [
     {"color": "red", "price": 20000}, 
     {"color": "black", "price": 22000}, 
     {"color": "blue", "price": 21000}
  ]
},
{
  "name": "XYZ",
  "details": [
     {"color": "yellow", "price": 10000}, 
     {"color": "black", "price": 12000}, 
     {"color": "green", "price": 11000}
  ]
},
{
  "name": "CBD",
  "details": [
     {"color": "red", "price": 30000}, 
     {"color": "pink", "price": 32000}, 
     {"color": "blue", "price": 31000}
  ]
}

I want to filter data for color red and blue and need output like

{"name": "ABC", "color": "red", "price": 20000},
{"name": "ABC", "color": "blue", "price": 21000},
{"name": "CBD", "color": "red", "price": 30000},
{"name": "CBD", "color": "blue", "price": 31000}

what will the query for this…

I am able to get

{
  "name": "ABC",
  "details": [
     {"color": "red", "price": 20000}, 
     {"color": "blue", "price": 21000}
  ]
},
{
  "name": "CBD",
  "details": [
     {"color": "red", "price": 30000}, 
     {"color": "blue", "price": 31000}
  ]
}

but I want like

{"name": "ABC", "color": "red", "price": 20000},
{"name": "ABC", "color": "blue", "price": 21000},
{"name": "CBD", "color": "red", "price": 30000},
{"name": "CBD", "color": "blue", "price": 31000}

let me know the MongoDB query or JS/TS code to simplify array object…

6

Answers


  1. You can use flatMap to get desired formatted data like below:

    const data = [
      {
        "name": "ABC",
        "details": [
           {"color": "red", "price": 20000}, 
           {"color": "blue", "price": 21000}
        ]
      },
      {
        "name": "CBD",
        "details": [
           {"color": "red", "price": 30000}, 
           {"color": "blue", "price": 31000}
        ]
      }
    ];
    
    const result = data.flatMap(entry => entry.details.map(detail => ({name: entry.name, ...detail})));
    
    console.log(result);
    Login or Signup to reply.
  2. You can run the below js code (if you don’t mind the performance):

    let initialArr = [{
        "name": "ABC",
        "details": [
           {"color": "red", "price": 20000}, 
           {"color": "blue", "price": 21000}
        ]
      },
    
      {
        "name": "CBD",
        "details": [
           {"color": "red", "price": 30000}, 
           {"color": "blue", "price": 31000}
        ]
      }
    ]
    let resultArr = []
    for (let i = 0; i < initialArr.length; i++) {
        for (let k = 0; k < initialArr[i].details.length; k++) {
            resultArr.push({name: initialArr[i].name, ...initialArr[i].details[k]})
        }
    }
    
    Login or Signup to reply.
  3. Using Array.flatMap() can do it ,below is reference for you,you can check at flattening-an-array-of-complex-objects-with-an-array-attribute

    let data = [
    {
      "name": "ABC",
      "details": [
         {"color": "red", "price": 20000}, 
         {"color": "black", "price": 22000}, 
         {"color": "blue", "price": 21000}
      ]
    },
    {
      "name": "XYZ",
      "details": [
         {"color": "yellow", "price": 10000}, 
         {"color": "black", "price": 12000}, 
         {"color": "green", "price": 11000}
      ]
    },
    {
      "name": "CBD",
      "details": [
         {"color": "red", "price": 30000}, 
         {"color": "pink", "price": 32000}, 
         {"color": "blue", "price": 31000}
      ]
    }
    ]
    
    data = data.filter(d1 => d1.details.some(d2 => d2.color == "red" || d2.color == "blue"))
    data = data.flatMap(({ name,details }) => details.map(d => ({ name, ...d })))
    
    console.log(data)
    Login or Signup to reply.
  4. MongoDB’s $unwind operator can help –

    Deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.

    Here’s an aggregate query –

    db.getCollection("your_collection").aggregate([
      // creates a document for each element in `details` array
      { $unwind : "$details" },
    
      // filters document with color red or blue
      { $match: { color: { $in: ["red", "blue"] } } }
    ])
    
    1. This is how unwounded data would look like –
    [
      {"name": "ABC", "color": "red", "price": 20000},
      {"name": "ABC", "color": "blue", "price": 22000},
      {"name": "ABC", "color": "blue", "price": 21000},
      {"name": "XYZ", "color": "yellow", "price": 10000},
      {"name": "XYZ", "color": "black", "price": 12000},
      ...
    ]
    
    1. Filtered, unwounded data (only red & blue)-
    [
      {"name": "ABC", "color": "red", "price": 20000},
      {"name": "ABC", "color": "blue", "price": 21000},
      {"name": "CBD", "color": "red", "price": 30000},
      {"name": "CBD", "color": "blue", "price": 31000}
    ]
    
    Login or Signup to reply.
  5. I think, if you like to get good performance then this would be the best option:

    db.collection.aggregate([
       { $match: { "details.color": { $in: ["red", "blue"] } } },
       {
          $set: {
             details: {
                $filter: {
                   input: "$details",
                   cond: { $in: ["$$this.color", ["red", "blue"]] }
                }
             }
          }
       },
       { $unwind: "$details" },
       { $replaceWith: { $mergeObjects: [{ name: "$name" }, "$details"] } }
    ])
    

    Mongo Playground

    Login or Signup to reply.
  6. By using aggregate the below query helps you alot.

    db.collection.aggregate([
      {
        $unwind: "$details"
      },
      {
        $match: {
          "details.color": { $in: ["red", "blue"] }
        }
      },
      {
        $project: {
          _id: 0,
          name: 1,
          color: "$details.color",
          price: "$details.price"
        }
      }
    ]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search