one-to-many 与 mongodb 的关系?

one-to-many relation with mongodb?

我有两个模式 HotelSchemaPricelineSchema:

// schemas.js
const mongoose = require("mongoose")

module.exports.HotelSchema = mongoose.Schema({
  name: {
    type: String,
    required: true,
    unique: true,
    index: true
  },
  accommodation_type: {
    type: String,
    enum: ["هتل", "هتل آپارتمان", "مهمانسرا"],
    index: true
  },
  pricelines: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Priceline' }]
})

module.exports.PricelineSchema = mongoose.Schema({
  source: {
    type: String,
    required: true,
    index: true
  },
  price: {
    type: String,
    required: true,
    index: true
  },
  hotel: { type: mongoose.Schema.Types.ObjectId, ref: "Hotel" }
})

因此有两个模型称为 HotelPriceline:

// models.js
const mongoose = require("mongoose")
const {HotelSchema, PricelineSchema} = require("./schemas")

module.exports.Priceline = mongoose.model("Priceline", PricelineSchema)
module.exports.Hotel = mongoose.model("Hotel", HotelSchema)

简而言之,我有一个端点,其中 async.waterfall 使用了:

const async = require("async")
const bodyParser = require("body-parser")
const app = express()
app.use(bodyParser.json())

const { Hotel, Priceline } = require("./models")

function buildPricelineMany (arr, hotel) {
  const pricelines = []
  for (let priceline of arr) {
    pricelines.push(
      new Priceline({
        source: priceline.source,
        price: priceline.price,
        hotel: hotel._id
      })
    )
  }
  return pricelines
}

app.put("/hotels", (req, res) => {
  async.waterfall(
    [
      function (callback) {
        Hotel.findOne({ name: req.body.name })
          .populate("pricelines")
          .exec(function (err, hotel) {
            if (err) return callback(hotel)
            return callback(null, hotel)
          })
      },
      function (hotel, callback) {
        const pricelines = buildPricelineMany(req.body.pricelines, hotel)
        Priceline.insertMany(pricelines)
          .then(docs => callback(null, hotel, docs))
          .catch(err => {
            return callback(err)
          })
      }
    ],
    function (error, hotel, pricelines) {
      if (error) {
        res.status(500).jsonp({ errors: [error] })
        return
      }

      res.status(200).jsonp(hotel)
    }
  )
})

app.listen(8080, "localhost")

当我调用 populate('pricelines') 时,结果是一个空数组,我不知道为什么?!

{
    "pricelines": [],
    "_id": "5b0569dc1b99f37385c4d975",
    "name": "baz",
    "accommodation_type": "هتل",
    "__v": 0
}

我创建 one-to-many 关系的方式与记录的完全一致 http://mongoosejs.com/docs/populate.html

这是我的 collection:

> db.pricelines.find({})
{ "_id" : ObjectId("5b058bf9e87a0e7ae7f6bd47"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfae87a0e7ae7f6bd48"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfae87a0e7ae7f6bd49"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfae87a0e7ae7f6bd4a"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd4b"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd4c"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd4d"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd4e"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd4f"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd50"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
{ "_id" : ObjectId("5b058bfbe87a0e7ae7f6bd51"), "source" : "jabama", "price" : "1231231", "hotel" : ObjectId("5b058beee87a0e7ae7f6bd42"), "__v" : 0 }
>
> db.hotels.find({})
{ "_id" : ObjectId("5b058beee87a0e7ae7f6bd42"), "name" : "baz", "accommodation_type" : "هتل", "pricelines" : [ ], "__v" : 0 }
{ "_id" : ObjectId("5b058bf6e87a0e7ae7f6bd46"), "name" : "استقلال", "accommodation_type" : "هتل", "pricelines" : [ ], "__v" : 0 }

您是否尝试过在收到对象后填充?像这样我的意思是:

...
.exec(function (err, hotel) {
   Priceline.populate(hotel, {path:'pricelines'}, function (err, populatedHotels){
        //do your things here
    })
});
...

我最近偶然发现了这个,我认为它可能会有所帮助。 让我知道

填充现在已成为历史...您应该使用 $lookup 在 pricelineshotel 字段中填充酒店的 _id

这是要执行的示例 (mongodb 版本 3.6)

db.collection.aggregate([
  { "$match": {  "name": req.body.name } },
  { "$lookup": {
    "from": Priceline.collection.name,
    "let": { "hotel_id": "$_id" },
    "pipeline": [
       { "$match": { "$expr": { "$eq": [ "$hotel", "$$hotel_id" ] } } }
     ],
     "as": "pricelines"
  }},
 ])

如果你有mongodb版本然后3.6那么你可以使用这个

 db.collection.aggregate([
  { "$match": {  "name": req.body.name } },
  { "$lookup": {
    "from": Priceline.collection.name,
    "localField": "_id",
    "foreignField": "hotel",
    "as": "pricelines"
  }},
 ])

$lookup 的最佳用途之一是您不需要将 _id 的数组放入集合中(用于填充),这减少了集合大小