在 MongoDB 中定义 3 个具有不同关系(多对多、一对多...)的模式
Defining 3 schemas in MongoDB with different relations (many-to-many, one-to-many...)
我的应用程序中有 3 个不同的模式:
userSchema, questionSchema, listingSchema
三者的关系如下:
每个 listing 都有许多与之相关的问题(每个 listing 的问题相同)。
每个用户可以回答多个列表中的许多问题。
每个 问题 都会在许多列表中得到许多用户的回答。
我正在努力定义这些模式之间的正确关系(主要是因为诸如“_id = 100
的用户在 [=17] 的列表中回答了 _id = 5
的问题之类的问题=] ,如何以最有效的方式更新所有这些?)。
到目前为止我已经定义了:
问题架构:
var questionSchema = new Schema({
description: String
});
用户架构:
var userSchema = new Schema({
local : {
email : String,
password : String,
name : String
},
ApartmentsAndQuestions: [{
apartmentID: String,
questionID: [String] /* one apartment -> multiple questions */
}]
});
和列表架构:
var listingSchema = new Schema({
street : String,
buildingNumber : Number,
apartmentNumber : Number,
type : String,
floor : Number,
outOfFloors : Number,
numberOfRooms : Number,
size : Number,
renovated : Boolean,
elevator : Boolean,
airConditioning : Boolean,
balcony : Boolean,
price : Number,
description : String,
flagCount : Number,
pictures : [imageSchema]
owner : [userSchema]
UsersAndQuestions: [{
userID: String,
questionID: [String] /* one user -> multiple questions asked possible */
}]
});
问题:我的NoSQL数据库如何做好?我的定义有意义吗?有没有更好的方法来描述这些模式之间的关系?
任何帮助都将 非常感谢 !
MongoDB 3.2+ 解决方案
在评论中添加提及,您可以使用新的$lookup来避免嵌入大量数据。就像 SQL LEFT JOIN:
让我们添加一些数据,与您的匹配:
db.questionSchema.insert({ _id: 1, description: "My description 1" });
db.questionSchema.insert({ _id: 2, description: "My description 2" });
db.questionSchema.insert({ _id: 3, description: "My description 3" });
db.questionSchema.insert({ _id: 4, description: "My description 4" });
db.userSchema.insert({ _id: 1, email: "my@email1.com", ApartmentsAndQuestions: [] });
db.userSchema.insert({ _id: 2, email: "my@email2.com", ApartmentsAndQuestions: [] });
db.listingSchema.insert({ _id: "A", UsersAndQuestions: [] })
db.listingSchema.insert({ _id: "B", UsersAndQuestions: [] })
// Add some questions
db.userSchema.update({ _id: 1 }, { $addToSet: { ApartmentsAndQuestions: { apartment_id: 1, question_id: [1] } } })
db.userSchema.update({ _id: 1, "ApartmentsAndQuestions.apartment_id": 1 }, { $addToSet: { "ApartmentsAndQuestions.$.question_id": 3 } })
db.userSchema.update({ _id: 2 }, { $addToSet: { ApartmentsAndQuestions: { apartment_id: 2, question_id: [1,2] } } })
db.userSchema.update({ _id: 2, "ApartmentsAndQuestions.apartment_id": 2 }, { $addToSet: { "ApartmentsAndQuestions.$.question_id": 4 } })
db.listingSchema.update({ _id: "A" }, { $addToSet: { UsersAndQuestions: { user_id: 1, question_id: [1] } } })
通过常规查找,您将得到以下结果:
test> db.listingSchema.find()
{
"_id": "B",
"UsersAndQuestions": [ ]
}
{
"_id": "A",
"UsersAndQuestions": [
{
"user_id": 1,
"question_id": [
1
]
}
]
}
然后,让我们$lookup:
db.listingSchema.aggregate([
{
$unwind: "$UsersAndQuestions"
}
,{
$lookup:
{
from: "userSchema",
localField: "UsersAndQuestions.user_id",
foreignField: "_id",
as: "fetched_user"
}
}
,{
$unwind: "$UsersAndQuestions.question_id"
}
,{
$lookup:
{
from: "questionSchema",
localField: "UsersAndQuestions.question_id",
foreignField: "_id",
as: "fetched_question"
}
}
])
你得到:
{
"waitedMS": NumberLong("0"),
"result": [
{
"_id": "A",
"UsersAndQuestions": {
"user_id": 1,
"question_id": 1
},
"fetched_user": [
{
"_id": 1,
"email": "my@email1.com",
"ApartmentsAndQuestions": [
{
"apartment_id": 1,
"question_id": [
1,
3
]
}
]
}
],
"fetched_question": [
{
"_id": 1,
"description": "My description 1"
}
]
}
],
"ok": 1
}
然后,您也可以 $unwind ApartmentsAndQuestions.questions_id
和 $lookup 查询数据。由你决定。
您需要按如下方式定义架构:
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
name : String
},
/* every entry in the array is an apartment ID and the questionsIDs (array) of the questions that the user ALREADY answered in that *specific* apartment */
ApartmentsAndQuestions: [{
apartmentID : String,
questionsIDs: [String]
}]
});
并且:
var listingSchema = new Schema({
street : String,
buildingNumber : Number,
apartmentNumber : Number,
type : String,
floor : Number,
outOfFloors : Number,
numberOfRooms : Number,
size : Number,
renovated : Boolean,
elevator : Boolean,
airConditioning : Boolean,
balcony : Boolean,
price : Number,
description : String,
flagCount : Number,
ownerID : String,
/* every entry in the array is a userID and the questionsIDs (array) of the questions that the user ALREADY answered in that *specific* apartment */
UsersAndQuestions: [{
userID: String,
questionID: [String]
}],
/* every image has a count of how many times the users answered YES or NO on it */
imagesAndCount: [{
imageID: String,
count: Number
}]
});
然后你基本上可以按照以下行做一些事情:
var someuser = db.users.find()[2] // get some user
someuser._id
>>> 其中 returns 一些 ObjectId("56472a83bd9fa764158d0cb6")
然后:db.users.find({_id: ObjectId("56472a83bd9fa764158d0cb6")}) >>> which will return someuser (with all the fields that are defined in the User schema)
然后:db.listings.insert({"street" : "SomeStreet", "buildingNumber" : 33, "apartmentNumber" : 63, "beds": 3, "owner" : "56472a83bd9fa764158d0cb6"})
列表将如下所示:
现在列表如下所示:
{
"_id": {
"$oid": "566c220abcda51a9eef08576"
},
"street": "SomeStreet",
"buildingNumber": 33,
"apartmentNumber": 63,
"beds": 3,
"owner": "56472a83bd9fa764158d0cb6"
}
我的应用程序中有 3 个不同的模式:
userSchema, questionSchema, listingSchema
三者的关系如下:
每个 listing 都有许多与之相关的问题(每个 listing 的问题相同)。
每个用户可以回答多个列表中的许多问题。
每个 问题 都会在许多列表中得到许多用户的回答。
我正在努力定义这些模式之间的正确关系(主要是因为诸如“_id = 100
的用户在 [=17] 的列表中回答了 _id = 5
的问题之类的问题=] ,如何以最有效的方式更新所有这些?)。
到目前为止我已经定义了:
问题架构:
var questionSchema = new Schema({
description: String
});
用户架构:
var userSchema = new Schema({
local : {
email : String,
password : String,
name : String
},
ApartmentsAndQuestions: [{
apartmentID: String,
questionID: [String] /* one apartment -> multiple questions */
}]
});
和列表架构:
var listingSchema = new Schema({
street : String,
buildingNumber : Number,
apartmentNumber : Number,
type : String,
floor : Number,
outOfFloors : Number,
numberOfRooms : Number,
size : Number,
renovated : Boolean,
elevator : Boolean,
airConditioning : Boolean,
balcony : Boolean,
price : Number,
description : String,
flagCount : Number,
pictures : [imageSchema]
owner : [userSchema]
UsersAndQuestions: [{
userID: String,
questionID: [String] /* one user -> multiple questions asked possible */
}]
});
问题:我的NoSQL数据库如何做好?我的定义有意义吗?有没有更好的方法来描述这些模式之间的关系?
任何帮助都将 非常感谢 !
MongoDB 3.2+ 解决方案
在评论中添加提及,您可以使用新的$lookup来避免嵌入大量数据。就像 SQL LEFT JOIN:
让我们添加一些数据,与您的匹配:
db.questionSchema.insert({ _id: 1, description: "My description 1" });
db.questionSchema.insert({ _id: 2, description: "My description 2" });
db.questionSchema.insert({ _id: 3, description: "My description 3" });
db.questionSchema.insert({ _id: 4, description: "My description 4" });
db.userSchema.insert({ _id: 1, email: "my@email1.com", ApartmentsAndQuestions: [] });
db.userSchema.insert({ _id: 2, email: "my@email2.com", ApartmentsAndQuestions: [] });
db.listingSchema.insert({ _id: "A", UsersAndQuestions: [] })
db.listingSchema.insert({ _id: "B", UsersAndQuestions: [] })
// Add some questions
db.userSchema.update({ _id: 1 }, { $addToSet: { ApartmentsAndQuestions: { apartment_id: 1, question_id: [1] } } })
db.userSchema.update({ _id: 1, "ApartmentsAndQuestions.apartment_id": 1 }, { $addToSet: { "ApartmentsAndQuestions.$.question_id": 3 } })
db.userSchema.update({ _id: 2 }, { $addToSet: { ApartmentsAndQuestions: { apartment_id: 2, question_id: [1,2] } } })
db.userSchema.update({ _id: 2, "ApartmentsAndQuestions.apartment_id": 2 }, { $addToSet: { "ApartmentsAndQuestions.$.question_id": 4 } })
db.listingSchema.update({ _id: "A" }, { $addToSet: { UsersAndQuestions: { user_id: 1, question_id: [1] } } })
通过常规查找,您将得到以下结果:
test> db.listingSchema.find()
{
"_id": "B",
"UsersAndQuestions": [ ]
}
{
"_id": "A",
"UsersAndQuestions": [
{
"user_id": 1,
"question_id": [
1
]
}
]
}
然后,让我们$lookup:
db.listingSchema.aggregate([
{
$unwind: "$UsersAndQuestions"
}
,{
$lookup:
{
from: "userSchema",
localField: "UsersAndQuestions.user_id",
foreignField: "_id",
as: "fetched_user"
}
}
,{
$unwind: "$UsersAndQuestions.question_id"
}
,{
$lookup:
{
from: "questionSchema",
localField: "UsersAndQuestions.question_id",
foreignField: "_id",
as: "fetched_question"
}
}
])
你得到:
{
"waitedMS": NumberLong("0"),
"result": [
{
"_id": "A",
"UsersAndQuestions": {
"user_id": 1,
"question_id": 1
},
"fetched_user": [
{
"_id": 1,
"email": "my@email1.com",
"ApartmentsAndQuestions": [
{
"apartment_id": 1,
"question_id": [
1,
3
]
}
]
}
],
"fetched_question": [
{
"_id": 1,
"description": "My description 1"
}
]
}
],
"ok": 1
}
然后,您也可以 $unwind ApartmentsAndQuestions.questions_id
和 $lookup 查询数据。由你决定。
您需要按如下方式定义架构:
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
name : String
},
/* every entry in the array is an apartment ID and the questionsIDs (array) of the questions that the user ALREADY answered in that *specific* apartment */
ApartmentsAndQuestions: [{
apartmentID : String,
questionsIDs: [String]
}]
});
并且:
var listingSchema = new Schema({
street : String,
buildingNumber : Number,
apartmentNumber : Number,
type : String,
floor : Number,
outOfFloors : Number,
numberOfRooms : Number,
size : Number,
renovated : Boolean,
elevator : Boolean,
airConditioning : Boolean,
balcony : Boolean,
price : Number,
description : String,
flagCount : Number,
ownerID : String,
/* every entry in the array is a userID and the questionsIDs (array) of the questions that the user ALREADY answered in that *specific* apartment */
UsersAndQuestions: [{
userID: String,
questionID: [String]
}],
/* every image has a count of how many times the users answered YES or NO on it */
imagesAndCount: [{
imageID: String,
count: Number
}]
});
然后你基本上可以按照以下行做一些事情:
var someuser = db.users.find()[2] // get some user
someuser._id
>>> 其中 returns 一些 ObjectId("56472a83bd9fa764158d0cb6")
然后:db.users.find({_id: ObjectId("56472a83bd9fa764158d0cb6")}) >>> which will return someuser (with all the fields that are defined in the User schema)
然后:db.listings.insert({"street" : "SomeStreet", "buildingNumber" : 33, "apartmentNumber" : 63, "beds": 3, "owner" : "56472a83bd9fa764158d0cb6"})
列表将如下所示:
现在列表如下所示:
{
"_id": {
"$oid": "566c220abcda51a9eef08576"
},
"street": "SomeStreet",
"buildingNumber": 33,
"apartmentNumber": 63,
"beds": 3,
"owner": "56472a83bd9fa764158d0cb6"
}