将 geoQuery 与评级聚合相结合

Combine geoQuery with rating aggregation

我想获取一个半径内的所有对象,以及每个对象的平均评分和总评分。我的两个查询都有效,但我希望将这两个查询合二为一。

位置架构

const LocationObject = new Schema({
    name: String
    location: {
        type: {
          type: String,
          enum: ['Point'],
          default: 'Point',
          required: true
        },
        coordinates: {
          type: [Number],
          required: true
        }
    }
})

ratingSchema

const Rating = new Schema({
    locationObject: { type: Schema.Types.ObjectId, ref: 'LocationObject' },
    average: Number,
})

位置查询

const objects = await LocationObject.find({
        location: {
            $geoWithin: {
                $centerSphere: [[lon, lat, radius] 
            }
        }
    })

单个 LocationObject 的 RatingAggregation

const result = await Rating.aggregate([
    { 
      "$match": {
        "locationObject": objectID
      }
    },
    {
      "$facet": {
        "numbers": [
          {
          "$group": {
            "_id": null,
            "totalRating": {
              "$sum": "$average"
            },
            "totalItemCount": {
              "$sum": 1.0
            }
          }
        }
        ],
      }
    },
    {
      "$unwind": "$numbers"
    },
    {
      "$project": {
        "_id": null,
        "avgRating": {"$divide": ["$numbers.totalRating", "$numbers.totalItemCount"]},
        "totalRatings":  "$numbers.totalItemCount"
      }
    }
  ])

最终结果应该 return 一个包含 locationObjects 的数组,每个对象都添加了 average 和 totalRatings。

mongo 操场:https://mongoplayground.net/p/JGuJtB5bZV4

预期结果

[
  {
    name: String,
    location: {
      coordinates: [Number, Number],
    },
    avgRating: Number,
    totalRatings: Number
  },
  {
    name: String,
    location: {
      coordinates: [Number, Number],
    }
  }
]

根据你最新的 playground,你可以使用 this

db.locationObject.aggregate([
  {
    "$match": {
      "location": {
        "$geoWithin": {
          "$centerSphere": [
            [
              6.064953,
              52.531348
            ],
            0.0012
          ]
        }
      }
    }
  },
  {
    "$lookup": { //You need to bring both the collection data together
      "from": "Rating",
      "localField": "_id",
      "foreignField": "locationObject",
      "as": "locRatings"
    }
  },
  {
    $unwind: "$locRatings"
  },
  {
    "$group": { //you can simplify the other pipelines
      "_id": "$_id",
      "field": {
        "$avg": "$locRatings.average"
      },
      "totalItemCount": {
        "$sum": 1.0
      }
    }
  }
])

要保留文档字段,您需要使用累加器,如 this playground

{
    "$group": {
      "_id": "$_id",
      "field": {
        "$avg": "$locRatings.average"
      },
      "totalItemCount": {
        "$sum": 1.0
      },
      "locations": {
        "$addToSet": "$location"
      }
    }
  }

您可以将 empty/null 个数组保留在展开阶段,如下所示

playground

{
    $unwind: {
      "path": "$locRatings",
      "preserveNullAndEmptyArrays": true
    }
  },

如果需要,您可以添加项目阶段以忽略空值。