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’});


7 回复

Node.js 在处理 MongoDB 多表连接问题的思路

在 Node.js 中使用 MongoDB 进行开发时,由于 MongoDB 是一个文档数据库,并没有像关系型数据库那样的外键约束或直接的表连接操作。因此,在处理多个 Collection 之间的数据关联时,通常需要通过编程的方式来实现。

思路

  1. 预加载数据:可以在读取 accounts 数据时,先一次性读取 sys_code 的所有数据,然后在遍历 accounts 数据时,根据需要的字段快速查找对应的 sys_code 数据。

  2. 嵌入式文档:可以将 sys_code 相关的信息嵌入到 accounts 文档中,这样在读取 accounts 数据时,不需要额外的查询操作。不过这种方法可能会导致数据冗余,需要权衡利弊。

  3. 引用式文档:在 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 中定义了 kindInfoaccountInfo 字段,用于存储 kindaccount 对应的 sys_code 信息。在实际应用中,你可能需要根据具体需求调整这些字段的结构。

  • 查询和填充数据:最后,我们在查询 accounts 数据时,使用 populate 方法来填充 kindInfoaccountInfo 字段。这使得我们在获取 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 中的多表连接问题,具体选择哪种方法取决于你的应用场景和性能需求。

回到顶部