MongoDB 用于保存用户联系人的架构优化
MongoDB Schema optimization for saving users contacts
我想设计一个用于存储用户联系人的架构。
这是我现有的模式:
用户架构
_id : ObjectId("5c53653451154c6da4623a77"),
name : “something”,
email : “something”,
password : “something”,
配置文件架构
"_id" : ObjectId("5c53653451154c6da4623a88"),
user_id - ref
mobile : “something”,
company” : “something”,
designation : “something”,
website : “something”,
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
}
我可以想到联系模式的两种方法,但都有一些缺点:
第一种方法
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id - ref,
"contacts": [
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
...
]
上述结构的问题是,当用户更新他们的个人资料时,联系人字段无法获得更新后的值。但在这种方法中,查询和检索特定用户的所有联系人并发回响应很容易。
第二种方法
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id : ref,
contacts: [
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
...
]
在此结构中,当用户更新其配置文件时,联系人具有更新的用户值。但这里的问题是在查询时我必须从联系人模式中获取个人资料 ID,然后查询个人资料模式并将 return 值作为响应发送给客户端。
当有 30K-50K 联系人时会发生什么 - 我需要查询 DB 50K 次吗?或者有更好的方法吗?
基于 node.js,使用 mongoose。
有 2 个模式,一个用于用户,另一个用于订单。一个用户可以像您的联系人一样创建多个订单。
用户-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");
const userSchema = new Schema(
{
firstName: {
trim: true,
type: String,
required: [true, "firstName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("firstName is invalid!");
}
}
},
lastName: {
trim: true,
type: String,
required: [true, "lastName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("lastName is invalid!");
}
}
},
email: {
unique: [true, "Email already registered"],
type: String,
required: [true, "Email is required"]
},
password: {
trim: true,
type: String,
require: [true, "Password is required"],
validate(value) {
if (value.length < 6) {
throw new Error("Password should be atleast 6 characters");
}
}
},
mobile: {
trim: true,
unique: [true, "Mobile Number already available"],
type: String,
required: [true, "Mobile Number is required"],
validate(value) {
if (value.length !== 10) {
throw new Error("Mobile Number is invalid!");
}
}
},
gender: {
trim: true,
type: String,
enum: [
"Male",
"Female"
],
required: [true, "Password is required"],
},
dob: {
trim: true,
type: Date,
required: [true, "DOB is required"],
},
Address: {
address: {
trim: true,
type: String,
require: [true, "Owner Address is required"]
},
city: {
trim: true,
type: String,
require: [true, "Owner Address City is required"]
},
state: {
trim: true,
uppercase: true,
type: String,
require: [true, "Owner Address State is required"]
},
pin: {
trim: true,
uppercase: true,
type: Number,
require: [true, "Owner Address Pin is required"],
validate(value) {
if (!(value >= 100000 && value <= 999999)) {
throw new Error("Pin is invalid!");
}
}
}
}
},
{
timestamps: true
}
);
const Users = mongoose.model("Users", userSchema);
module.exports = Users;
订单-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const orderSchema = new Schema({
userId: {
trim: true,
type: Schema.Types.ObjectId,
ref: "users",
required: [true, "User ID is required"]
},
dateOfOrder: {
trim: true,
type: Date,
required: [true, "Date of Order is required"],
},
dateOfMeasurement: {
trim: true,
type: Date,
},
dateOfTrail: {
trim: true,
type: Date,
},
dateOfDelivery: {
trim: true,
type: Date,
},
},
{
timestamps: true
});
const Orders = mongoose.model("Orders", orderSchema);
module.exports = Orders;
您遇到的问题实际上是为什么 mongodb 不是这种情况的最佳选择。您的数据是关系数据,但您使用的是非关系数据库。
但是对于您的情况,有一些步骤可以使它变得更好:
第一种方法
您可以选择第一种方式,在更新用户数据时,同时更新所有联系人。更简单的查询,但很难保持数据同步。
第二种方法
或者您可以选择第二种方法,但不只是保存 ref_id 还保存联系人姓名(该字段主要用于查询)。这样可以更容易地保持数据同步,如果按名称进行搜索,您可以进行正常查找。
如果我对您的理解正确,我认为您已经正确识别了每个选项的一些 pros/cons。现在你必须决定什么对你的具体情况有意义。选项 1 很容易获取,但更新和与 Profile
保持同步很乏味。选项 2 具有更多规范化数据,更适合更新,但需要更多查询才能检索。所以你要问自己一些问题。
- 标准化数据对您有多重要?
Profile
与 Contact
模式的大小比较如何?你会有更多的个人资料吗?显着减少?数量级?
- 更经常发生的事情 - 有人更新了他们的个人资料或有人查询联系人?还有多少?数量级?
- 为了更深入地研究您的最后两个答案 - 做一些估计,如果必须的话,甚至是粗略的估计,然后做一些数学运算看看什么可能更有意义。
例如,如果每个用户有 10,000 个联系人,那么选项 #1 将为您提供一个更大的文档,可以在单个查询中收集所有联系人和配置文件。如果您的用户平均每月只更新一次个人资料,但您需要每天多次查询联系人,这可能是更好的选择。但是,如果您的用户喜欢每天更新他们的个人资料,并且您可能需要每周查询一次联系人,那么选项 #2 可能更有意义。
归根结底,这是一个非常适合您的场景的设计选择。祝你好运!
基本上你有一个需要关系数据库的场景。但是您也可以在 mongo 中实现。
您需要使用 populate of mongoose。用你的第二种方法。您存储配置文件 ID 的位置。
User.find({_id: '5c53653451154c6da4623a77'}).populate({
path:'profiles',
options: {
limit: 10,
skip: 0
}}).exec();
此查询将 return 相关个人资料。如果您有 50K 这样的数据。您必须限制一个请求中的数据。
注意:Mongodb 每个文档的限制为 16mb。不可能存储那么多数据。
因此,请重新考虑您的数据库。
我想设计一个用于存储用户联系人的架构。
这是我现有的模式:
用户架构
_id : ObjectId("5c53653451154c6da4623a77"),
name : “something”,
email : “something”,
password : “something”,
配置文件架构
"_id" : ObjectId("5c53653451154c6da4623a88"),
user_id - ref
mobile : “something”,
company” : “something”,
designation : “something”,
website : “something”,
social: {
youtube: {
type: String
},
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},
instagram: {
type: String
}
}
我可以想到联系模式的两种方法,但都有一些缺点:
第一种方法
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id - ref,
"contacts": [
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
{
name : “something”,
company : “something”,
designation : “something”,
website : “something”,
social: { something },
mobile : “something”
},
...
]
上述结构的问题是,当用户更新他们的个人资料时,联系人字段无法获得更新后的值。但在这种方法中,查询和检索特定用户的所有联系人并发回响应很容易。
第二种方法
"_id" : ObjectId("5c53653451154c6da4623a99"),
user_id : ref,
contacts: [
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
profile_id,
...
]
在此结构中,当用户更新其配置文件时,联系人具有更新的用户值。但这里的问题是在查询时我必须从联系人模式中获取个人资料 ID,然后查询个人资料模式并将 return 值作为响应发送给客户端。
当有 30K-50K 联系人时会发生什么 - 我需要查询 DB 50K 次吗?或者有更好的方法吗?
基于 node.js,使用 mongoose。
有 2 个模式,一个用于用户,另一个用于订单。一个用户可以像您的联系人一样创建多个订单。
用户-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");
const userSchema = new Schema(
{
firstName: {
trim: true,
type: String,
required: [true, "firstName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("firstName is invalid!");
}
}
},
lastName: {
trim: true,
type: String,
required: [true, "lastName is required!"],
validate(value) {
if (value.length < 2) {
throw new Error("lastName is invalid!");
}
}
},
email: {
unique: [true, "Email already registered"],
type: String,
required: [true, "Email is required"]
},
password: {
trim: true,
type: String,
require: [true, "Password is required"],
validate(value) {
if (value.length < 6) {
throw new Error("Password should be atleast 6 characters");
}
}
},
mobile: {
trim: true,
unique: [true, "Mobile Number already available"],
type: String,
required: [true, "Mobile Number is required"],
validate(value) {
if (value.length !== 10) {
throw new Error("Mobile Number is invalid!");
}
}
},
gender: {
trim: true,
type: String,
enum: [
"Male",
"Female"
],
required: [true, "Password is required"],
},
dob: {
trim: true,
type: Date,
required: [true, "DOB is required"],
},
Address: {
address: {
trim: true,
type: String,
require: [true, "Owner Address is required"]
},
city: {
trim: true,
type: String,
require: [true, "Owner Address City is required"]
},
state: {
trim: true,
uppercase: true,
type: String,
require: [true, "Owner Address State is required"]
},
pin: {
trim: true,
uppercase: true,
type: Number,
require: [true, "Owner Address Pin is required"],
validate(value) {
if (!(value >= 100000 && value <= 999999)) {
throw new Error("Pin is invalid!");
}
}
}
}
},
{
timestamps: true
}
);
const Users = mongoose.model("Users", userSchema);
module.exports = Users;
订单-
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const orderSchema = new Schema({
userId: {
trim: true,
type: Schema.Types.ObjectId,
ref: "users",
required: [true, "User ID is required"]
},
dateOfOrder: {
trim: true,
type: Date,
required: [true, "Date of Order is required"],
},
dateOfMeasurement: {
trim: true,
type: Date,
},
dateOfTrail: {
trim: true,
type: Date,
},
dateOfDelivery: {
trim: true,
type: Date,
},
},
{
timestamps: true
});
const Orders = mongoose.model("Orders", orderSchema);
module.exports = Orders;
您遇到的问题实际上是为什么 mongodb 不是这种情况的最佳选择。您的数据是关系数据,但您使用的是非关系数据库。
但是对于您的情况,有一些步骤可以使它变得更好:
第一种方法
您可以选择第一种方式,在更新用户数据时,同时更新所有联系人。更简单的查询,但很难保持数据同步。
第二种方法
或者您可以选择第二种方法,但不只是保存 ref_id 还保存联系人姓名(该字段主要用于查询)。这样可以更容易地保持数据同步,如果按名称进行搜索,您可以进行正常查找。
如果我对您的理解正确,我认为您已经正确识别了每个选项的一些 pros/cons。现在你必须决定什么对你的具体情况有意义。选项 1 很容易获取,但更新和与 Profile
保持同步很乏味。选项 2 具有更多规范化数据,更适合更新,但需要更多查询才能检索。所以你要问自己一些问题。
- 标准化数据对您有多重要?
Profile
与Contact
模式的大小比较如何?你会有更多的个人资料吗?显着减少?数量级?- 更经常发生的事情 - 有人更新了他们的个人资料或有人查询联系人?还有多少?数量级?
- 为了更深入地研究您的最后两个答案 - 做一些估计,如果必须的话,甚至是粗略的估计,然后做一些数学运算看看什么可能更有意义。
例如,如果每个用户有 10,000 个联系人,那么选项 #1 将为您提供一个更大的文档,可以在单个查询中收集所有联系人和配置文件。如果您的用户平均每月只更新一次个人资料,但您需要每天多次查询联系人,这可能是更好的选择。但是,如果您的用户喜欢每天更新他们的个人资料,并且您可能需要每周查询一次联系人,那么选项 #2 可能更有意义。
归根结底,这是一个非常适合您的场景的设计选择。祝你好运!
基本上你有一个需要关系数据库的场景。但是您也可以在 mongo 中实现。
您需要使用 populate of mongoose。用你的第二种方法。您存储配置文件 ID 的位置。
User.find({_id: '5c53653451154c6da4623a77'}).populate({
path:'profiles',
options: {
limit: 10,
skip: 0
}}).exec();
此查询将 return 相关个人资料。如果您有 50K 这样的数据。您必须限制一个请求中的数据。
注意:Mongodb 每个文档的限制为 16mb。不可能存储那么多数据。
因此,请重新考虑您的数据库。