one-to-many 与 mongodb 的关系?
one-to-many relation with mongodb?
我有两个模式 HotelSchema
和 PricelineSchema
:
// 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" }
})
因此有两个模型称为 Hotel
和 Priceline
:
// 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
的数组放入集合中(用于填充),这减少了集合大小
我有两个模式 HotelSchema
和 PricelineSchema
:
// 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" }
})
因此有两个模型称为 Hotel
和 Priceline
:
// 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
的数组放入集合中(用于填充),这减少了集合大小