Search code examples
mongodbaggregate

MongoDB find center of array of points


I have 2 collections lands and users,

with lands looks like this:

{
    "_id": "locationID1",  # the string is "STRING" that could be referred in applicant collection
    "location": {
        "type": "Point",
        "coordinates": [100, 100]
    }
},
{
    "_id": "locationID2", 
    "location": {
        "type": "Point",
        "coordinates": [101, 101]
    }
}

and users like this:

{
    "_id": ObjectID("someRandomByteString")
    "name": "name",
    "owned_lands": ['locationID1', 'locationID2']
}

My goal is to create a center field that is also a point, representing the center of the owned_lands, so I can perform a $geoNear search by name later. The desired output:

{
    "_id": ObjectID("someRandomByteString")
    "name": "name",
    "owned_lands": ['locationID1', 'locationID2'],
    "center": {
        "type": "Point",
        "coordinates": [100.5, 100.5]
    }
}

I tried to use $lookup on users to applicant:

db.users.aggregate([{
    $lookup: {
        from: "lands",
        localField: "owned_lands",
        foreignField: "_id",
        as: "detailedLands",
    },
}])

However mongoDB only supports single element to array lookup search, the other way around will return an empty array. What can I do to complete the task?


Solution

  • One option is to use the $lookup pipeline to get only the data you need, with the formatting you need:

    db.users.aggregate([
      {$lookup: {
          from: "lands",
          localField: "owned_lands",
          foreignField: "_id",
          pipeline: [
            {$project: {
                _id: 0,
                x: {$first: "$location.coordinates"},
                y: {$last: "$location.coordinates"}
            }}
          ],
          as: "detailedLands"
      }},
      {$set: {
          center: {
            type: "Point",
            coordinates: [{$avg: "$detailedLands.x"}, {$avg: "$detailedLands.y"}]
          },
          detailedLands: "$$REMOVE"
      }},
      { $merge : { into : "users" } }
    ])
    

    See how it works on the playground example