3

how can i combine match document's subdocument together as one and return it as an array of object ? i have tried $group but don't seem to work.

my query ( this return array of object in this case there are two )

User.find({
      'business_details.business_location': {
        $near: coords,
        $maxDistance: maxDistance
      },
      'deal_details.deals_expired_date': {
        $gte: new Date()
      }
    }, {
      'deal_details': 1
    }).limit(limit).exec(function(err, locations) {
      if (err) {
        return res.status(500).json(err)
      }
console.log(locations) 

the console.log(locations) result // give me the result below

[{
  _id: 55 c0b8c62fd875a93c8ff7ea, // first document 
  deal_details: [{
    deals_location: '101.6833,3.1333',
    deals_price: 12.12 // 1st deal 
  }, {
    deals_location: '101.6833,3.1333',
    deals_price: 34.3 // 2nd deal 
  }],
  business_details: {}
}, {
  _id: 55 a79898e0268bc40e62cd3a, // second document 
  deal_details: [{
    deals_location: '101.6833,3.1333',
    deals_price: 12.12 // 3rd deal 
  }, {
    deals_location: '101.6833,3.1333',
    deals_price: 34.78 // 4th deal 
  }, {
    deals_location: '101.6833,3.1333',
    deals_price: 34.32 // 5th deal
  }],
  business_details: {}
}]

what i wanted to do is to combine these both deal_details field together and return it as an array of object. It will contain 5 deals in one array of object instead of two separated array of objects.

i have try to do it in my backend (nodejs) by using concat or push, however when there's more than 2 match document i'm having problem to concat them together, is there any way to combine all match documents and return it as one ? like what i mentioned above ?

2 Answers 2

3

What you are probably missing here is the $unwind pipeline stage, which is what you typically use to "de-normalize" array content, particularly when your grouping operation intends to work across documents in your query result:

User.aggregate(
    [
        // Your basic query conditions
        { "$match": {
            "business_details.business_location": {
                "$near": coords,
                "$maxDistance": maxDistance
            },
            "deal_details.deals_expired_date": {
            "$gte": new Date()
        }},

        // Limit query results here
        { "$limit": limit },

        // Unwind the array
        { "$unwind": "$deal_details" },

        // Group on the common location
        { "$group": {
             "_id": "$deal_details.deals_location",
             "prices": {
                 "$push": "$deal_details.deals_price"
             }
        }}
    ],
    function(err,results) {
        if (err) throw err;
        console.log(JSON.stringify(results,undefined,2));
    }
);

Which gives output like:

{
    "_id": "101.6833,3.1333",
    "prices": [
        12.12,
        34.3,
        12.12,
        34.78,
        34.32
    ]
}

Depending on how many documents actually match the grouping.

Alternately, you might want to look at the $geoNear pipeline stage, which gives a bit more control, especially when dealing with content in arrays.

Also beware that with "location" data in an array, only the "nearest" result is being considered here and not "all" of the array content. So other items in the array may not be actually "near" the queried point. That is more of a design consideration though as any query operation you do will need to consider this.

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

4 Comments

hi thanks for your quick answer. However @hassansin 's answer is the answer that solved my problem. your help is appreciated and thanks for your time to answer my question
@JohnLim They are essentially two completely different approaches. If your intent it to "limit" the response from the server to just the required data then use the aggregation framework. If that does not really gain you any perceived benefit then just do your combination on the client side. You still should be aware of my warning here that your current approach will not "guarantee" that "all" the data returned is "near" the queried location though. That is a limitation of storing your locations in an array.
isn't the $near operation is to return data that is closest to a specific coordinate ? if i set the $maxDistance to 5 /= 6371, isn't it will query from the specific coordinate to 5km away in a circle( 5km radius) ? or i misunderstood the $near function in mongodb
@JohnLim In itself yes that is what $near does. Hoever, if your "document" contains an array that has both "Syndey" and "New York" as location data that is indexed within that array, and you ask for "Sydney", then the "New York" data is still there because it belongs to the same parent document that was matched for the other location. As I warned, this is a design consideration.
1

You can merge them with reduce:

locations = locations.reduce(function(prev, location){
    previous = prev.concat(location.deal_details)
    return previous
},[])

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.