Nodejs在如何处理mongodb的多表连接问题的,求思路
Nodejs在如何处理mongodb的多表连接问题的,求思路
我在学习nodejs项目中使用mongodb涉及到2个Collection,accounts和sys_code,查询accounts时,其中的一些字段需要到sys_code查询对应的内容,如果是关系型数据库很简单,不知道node使用mongodb的情况下如何处理。还是我collection设计的就有问题?
var AccountsSchema = new Schema({ date : { type: String }, //消费日期 kind : { type: String }, //收支类型 需要关联sys_code type : { type: String }, //账目分类 需要关联sys_code cash : { type: String }, //金额(元) account : { type: Number, default: 0 }, //资金账户 需要关联sys_code remark : { type: String}, //备注 user_id : { type: ObjectId }, //添加人 create_at: { type: Date, default: Date.now }, update_at: { type: Date, default: Date.now }, active: { type: Boolean, default: true } //是否有效 },{collection : ‘accounts’});
var SysCodeSchema = new Schema({ code_type_no : { type: String }, //代码类型编码 code_order : { type: String }, //代码序号 code_no : { type: String }, //代码编码 code_value : { type: String }, //代码值 code_notes : { type: String}, //代码说明 active: { type: Boolean, default: true } //是否有效
},{collection : ‘sys_code’});
Node.js 在处理 MongoDB 多表连接问题的思路
在 Node.js 中使用 MongoDB 进行开发时,由于 MongoDB 是一个文档数据库,并没有像关系型数据库那样的外键约束或直接的表连接操作。因此,在处理多个 Collection 之间的数据关联时,通常需要通过编程的方式来实现。
思路
-
预加载数据:可以在读取
accounts
数据时,先一次性读取sys_code
的所有数据,然后在遍历accounts
数据时,根据需要的字段快速查找对应的sys_code
数据。 -
嵌入式文档:可以将
sys_code
相关的信息嵌入到accounts
文档中,这样在读取accounts
数据时,不需要额外的查询操作。不过这种方法可能会导致数据冗余,需要权衡利弊。 -
引用式文档:在
accounts
文档中只存储sys_code
的_id
,在需要的时候再进行查询。这种方法比较灵活,但会增加查询次数。
示例代码
假设我们选择第二种方法,即在 accounts
文档中嵌入 sys_code
的信息。
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// 定义 SysCode 模型
const SysCodeSchema = new Schema({
code_type_no: { type: String },
code_order: { type: String },
code_no: { type: String },
code_value: { type: String },
code_notes: { type: String },
active: { type: Boolean, default: true }
}, { collection: 'sys_code' });
const SysCodeModel = mongoose.model('SysCode', SysCodeSchema);
// 定义 Accounts 模型
const AccountsSchema = new Schema({
date: { type: String },
kind: { type: String },
type: { type: String },
cash: { type: String },
account: { type: Number, default: 0 },
remark: { type: String },
user_id: { type: Schema.Types.ObjectId },
create_at: { type: Date, default: Date.now },
update_at: { type: Date, default: Date.now },
active: { type: Boolean, default: true },
kindInfo: { type: Schema.Types.Mixed }, // 嵌入 kind 对应的 sys_code 信息
accountInfo: { type: Schema.Types.Mixed } // 嵌入 account 对应的 sys_code 信息
}, { collection: 'accounts' });
const AccountsModel = mongoose.model('Account', AccountsSchema);
async function fetchData() {
// 预加载 sys_code 数据
const sysCodes = await SysCodeModel.find({}, { _id: 1, code_value: 1 }).lean();
// 构建一个映射表,方便查找
const sysCodeMap = {};
sysCodes.forEach(code => {
sysCodeMap[code._id] = code;
});
// 查询 accounts 数据并嵌入 sys_code 信息
const accounts = await AccountsModel.find().populate([
{ path: 'kindInfo', select: '_id code_value', match: { _id: 'kind' } },
{ path: 'accountInfo', select: '_id code_value', match: { _id: 'account' } }
]).lean();
return accounts.map(account => ({
...account,
kindInfo: sysCodeMap[account.kind],
accountInfo: sysCodeMap[account.account]
}));
}
fetchData().then(accounts => console.log(accounts)).catch(err => console.error(err));
解释
-
预加载数据:首先,我们从
sys_code
收集中获取所有的数据,并将其存储在一个映射表中,以便于后续快速查找。 -
嵌入式文档:在
AccountsSchema
中定义了kindInfo
和accountInfo
字段,用于存储kind
和account
对应的sys_code
信息。在实际应用中,你可能需要根据具体需求调整这些字段的结构。 -
查询和填充数据:最后,我们在查询
accounts
数据时,使用populate
方法来填充kindInfo
和accountInfo
字段。这使得我们在获取accounts
数据时能够同时获得相关的sys_code
信息。
这种方法虽然会增加一些存储空间,但在查询时可以减少额外的数据库查询次数,提高性能。
mongodb是非关系型数据库,所以一切逻辑在代码中实现即可,当然考虑效率问题,不然很容易内存溢出
ref populate
正在看api,谢谢
mongodb的Join问题大多可使用信息冗余来解决. ref什么的尽量不要用
有道理,最近看了很多关于此的文章, “数据冗余是为了性能,引用数据是为了完整性”。详见: http://virusswb.blog.51cto.com/115214/793135
在处理 MongoDB 中的多表连接问题时,通常不会像关系型数据库那样直接进行表连接操作。MongoDB 是一个文档数据库,更倾向于将数据嵌入到单个文档中,或者通过引用的方式来处理跨集合的数据访问。
思路一:数据预加载
一种常见的方法是在查询 accounts
之前,先加载 sys_code
数据,并将其存储在一个缓存中,然后在查询 accounts
时使用缓存中的数据来填充关联字段。
示例代码:
const mongoose = require('mongoose');
const { AccountsSchema, SysCodeSchema } = require('./schemas');
mongoose.connect('mongodb://localhost:27017/yourdb', { useNewUrlParser: true, useUnifiedTopology: true });
const SysCodeModel = mongoose.model('SysCode', SysCodeSchema);
const AccountsModel = mongoose.model('Accounts', AccountsSchema);
// 预加载 sys_code 数据
async function preloadSysCodes() {
const sysCodes = await SysCodeModel.find();
return sysCodes.reduce((acc, code) => {
acc[code.code_no] = code;
return acc;
}, {});
}
// 查询 accounts 并填充关联字段
async function getAccounts() {
const sysCodes = await preloadSysCodes();
const accounts = await AccountsModel.find();
return accounts.map(account => ({
...account._doc,
kind: sysCodes[account.kind]?.code_value || account.kind,
type: sysCodes[account.type]?.code_value || account.type,
account: sysCodes[account.account]?.code_value || account.account,
}));
}
getAccounts().then(accounts => console.log(accounts));
思路二:聚合管道 (Aggregation Pipeline)
另一种方法是使用 MongoDB 的聚合管道来处理跨集合的数据访问。这种方法可以在查询 accounts
的同时嵌入 sys_code
数据。
示例代码:
async function getAccountsWithSysCode() {
const accounts = await AccountsModel.aggregate([
{
$lookup: {
from: 'sys_code',
localField: 'kind',
foreignField: 'code_no',
as: 'kindDetails'
}
},
{
$lookup: {
from: 'sys_code',
localField: 'type',
foreignField: 'code_no',
as: 'typeDetails'
}
},
{
$lookup: {
from: 'sys_code',
localField: 'account',
foreignField: 'code_no',
as: 'accountDetails'
}
},
{
$project: {
date: 1,
kind: { $arrayElemAt: ['$kindDetails.code_value', 0] },
type: { $arrayElemAt: ['$typeDetails.code_value', 0] },
cash: 1,
account: { $arrayElemAt: ['$accountDetails.code_value', 0] },
remark: 1,
user_id: 1,
create_at: 1,
update_at: 1,
active: 1
}
}
]);
return accounts;
}
getAccountsWithSysCode().then(accounts => console.log(accounts));
这两种方法都可以有效地处理 MongoDB 中的多表连接问题,具体选择哪种方法取决于你的应用场景和性能需求。