试图克服MongoDB中关系的缺点,这就是我想要在数据库中提供的内容:
型号:
uid
进行查询,但是_id
也可用。规格:
每个用户可以是应用程序的团队成员或独立用户。普通用户可以拥有自己的私人项目和资产。
团队成员可以访问团队所有者决定可以访问的资产和项目。
成员可以创建自己的私人资产和项目。
根据成员的角色,成员还可以与团队成员共享资产。
根据成员的角色,成员可以添加,删除,编辑团队资产,删除或重命名团队,添加或删除团队成员。
成员可以查看团队中与其共享的资产和项目的列表。
成员可以查看单个资产页面,他们可以在其中根据其角色在资产中添加删除项。
成员可以根据其角色查看,编辑,添加,删除单个项目。
当前架构:
const teamSchema = new Schema({
uid: {
type: String,
required: true
},
name: {
type: String,
required: true
},
members: {
type: [String]
},
brands: {
type: [String]
}
}, { collection: 'team', timestamps: true });
const memberSchema = new Schema({
uid: {
type: String,
required: true
},
owner: {
type: String,
required: true
},
teamID: {
type: Schema.Types.ObjectId,
required: true
},
acl: {
type: String,
required: true,
enum: ['view', 'copy', 'edit', 'admin', 'owner'],
default: 'view'
},
pending: {
type: Boolean,
default: true
}
}, { collection: 'member', timestamps: true, _id: false });
const assetsSchema = new Schema({
uid: {
type: String,
required: true
},
title: {
type: String,
required: true
},
fonts: [AssetFile],
colors: [Color],
logos: [AssetFile],
images: [AssetFile],
projects: Array,
items: Array,
members: [String]
}, { collection: 'branding', timestamps: true });
const userSchema = new Schema({
uid: {
type: String,
required: true
},
email: {
type: String,
required: true,
lowercase: true,
index: { unique: true },
validate: v => validator.isEmail(v)
},
avatar: String,
files: [FilesSchema],
isAdmin: Boolean,
projectCount: {
type: Number,
default: 0
},
userName: {
type: String,
trim: true
},
userType: {
type: String,
required: true,
default: 'Free User'
},
lastSeenAt: {
type: Date
},
}, { collection: 'user', timestamps: true });
const projectSchema = new Schema({
uid: {
type: String,
required: true
},
objects: Array,
projectTitle: {
type: String,
trim: true,
default: 'Untitled Project'
},
preview: {
type: String
},
folder: {
type: String,
trim: true,
default: null
},
archived: {
type: Boolean,
default: false
},
}, { collection: 'project', timestamps: true });
这些可以在关系数据库中轻松实现,但是由于我的应用已经公开使用,拥有超过4万名用户,因此迁移到另一个数据库并不是一个容易的选择。
这些可以在MongoDB中实现吗?或者我应该停止尝试并移至另一个DB?
我已经在使用猫鼬了,但是我也愿意使用本地的MongoDB代码(例如聚合,$ lookup等)
编辑:
对我而言,与其说是可扩展性问题,不如说它是如何在MongoDB中编写查询的,这是我已经拥有的查询:
资产列表:
const personalAssets = await AssetsModel.aggregate([
{ $match: { uid: user.uid } },
{ $project: { _id: 1, uid: 1, title: 1, logos: 1, teamID: 1, acl: 'owner', createdAt: 1, fonts: 1 } },
{ $sort: { createdAt: -1 } }
]);
const sharedAssets = await MemberModel.aggregate([
{
$match: {
$or: [
{ uid: user.uid },
{ owner: user._id }
]
}
},
{ $project: { _id: 0, acl: 1, teamID: 1, owner: 1 } },
{
$lookup: {
from: 'team',
let: { team_id: '$teamID' },
pipeline: [
{
$match: {
$expr: {
$eq: [ '$_id', '$$team_id' ]
}
}
},
{ $project: { _id: 0, assets: 1, teamID: '$$team_id' } }
],
as: 'team'
}
},
{ $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ '$team', 0 ] }, '$$ROOT' ] } } },
{ $project: { assets: 1, acl: 1, teamID: 1, owner: 1 } },
{ $match: { 'assets.0': { $exists: true } } },
{
$lookup: {
from: 'branding',
let: { 'assets': '$assets' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $in: [ '$_id', '$$assets' ] },
{ $ne: [ '$uid', user.uid ] }
]
}
}
}
],
as: 'assets'
}
},
{ $unwind: '$assets' },
{
$replaceRoot: {
newRoot: {
$mergeObjects: ['$assets', { acl: '$acl' }, { teamID: '$teamID' }, { owner: '$owner' }]
}
}
},
{
$project: {
assets: 1,
acl: {
$cond: {
if: { $eq: [ user._id, '$owner' ] },
then: 'owner',
else: '$acl'
}
},
_id: 1,
uid: 1,
title: 1,
teamID: 1,
logos: 1,
fonts: 1
}
}
]);
return [...personalAssets, ...sharedAssets];
单个资产:
const personalAsset = await AssetsModel.findOne({ _id, uid: user.uid });
if (brand) asset.acl = 'owner';
const sharedAsset = await MemberModel.aggregate([
{
$match: {
$and: [
{ teamID: mongoose.Types.ObjectId(teamID) },
{
$or: [
{ uid: user.uid },
{ owner: user._id }
]
}
]
}
},
{ $project: { _id: 0, acl: 1, teamID: 1, owner: 1 } },
{
$lookup: {
from: 'team',
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: [ '$_id', mongoose.Types.ObjectId(teamID) ] },
{ $in: [ mongoose.Types.ObjectId(_id), '$assets' ] }
]
}
}
},
{ $project: { _id: 0, assets: 1, teamID: teamID } }
],
as: 'team'
}
},
{ $replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ '$team', 0 ] }, '$$ROOT' ] } } },
{ $match: { 'assets.0': { $exists: true } } },
{
$lookup: {
from: 'branding',
pipeline: [
{
$match: {
$expr: {
$eq: [ '$_id', mongoose.Types.ObjectId(_id) ]
}
}
}
],
as: 'assets'
}
},
{ $unwind: '$assets' },
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
'$assets',
{
acl: {
$cond: {
if: { $eq: [ user._id, '$owner' ] },
then: 'owner',
else: '$acl'
}
}
},
{ teamID: '$teamID' }
]
}
}
}
]);
brand = sharedAsset[0];
文件夹和项目也一样。这里的主要挑战是获取资产列表并获取成员ACL(访问控制级别)时。对于单个资产(文件夹和项目)更容易做到这一点。
我的主要问题是如何以更可扩展的方式构造模式并查询它们。