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 具有更多规范化数据,更适合更新,但需要更多查询才能检索。所以你要问自己一些问题。

  • 标准化数据对您有多重要?
  • ProfileContact 模式的大小比较如何?你会有更多的个人资料吗?显着减少?数量级?
  • 更经常发生的事情 - 有人更新了他们的个人资料或有人查询联系人?还有多少?数量级?
  • 为了更深入地研究您的最后两个答案 - 做一些估计,如果必须的话,甚至是粗略的估计,然后做一些数学运算看看什么可能更有意义。

例如,如果每个用户有 10,000 个联系人,那么选项 #1 将为您提供一个更大的文档,可以在单个查询中收集所有联系人和配置文件。如果您的用户平均每月只更新一次个人资料,但您需要每天多次查询联系人,这可能是更好的选择。但是,如果您的用户喜欢每天更新他们的个人资料,并且您可能需要每周查询一次联系人,那么选项 #2 可能更有意义。

归根结底,这是一个非常适合您的场景的设计选择。祝你好运!

基本上你有一个需要关系数据库的场景。但是您也可以在 mongo 中实现。

您需要使用 populate of mongoose。用你的第二种方法。您存储配置文件 ID 的位置。

User.find({_id: '5c53653451154c6da4623a77'}).populate({
path:'profiles',
options: {
    limit: 10,
    skip: 0
}}).exec();

Mongoose populate

此查询将 return 相关个人资料。如果您有 50K 这样的数据。您必须限制一个请求中的数据。

注意:Mongodb 每个文档的限制为 16mb。不可能存储那么多数据。

因此,请重新考虑您的数据库。