Nodejs想用MongoDB来保存书籍,该如何设计数据库比较好?
Nodejs想用MongoDB来保存书籍,该如何设计数据库比较好?
想用MongoDB来保存书籍,而书的格式刚好是树形结构的,该如何去设计数据库比较好呢? 在查找父节点和查找子节点方面都是比较快的。 或者大多数是通过父节点找到子节点, 而很少通过子节点找到父节点。
Node.js 使用 MongoDB 保存书籍的设计方案
在 Node.js 中使用 MongoDB 存储具有树形结构的书籍数据时,我们可以利用 MongoDB 的嵌套文档特性以及引用(references)来实现高效的查询操作。由于大多数情况下是通过父节点查找子节点,我们可以考虑以下设计方案。
数据库设计
- 书籍集合(Books Collection):
- 每本书都有一个唯一的
_id
。 - 每本书可以有一个
parentId
字段来表示其父节点。 - 每本书还可以有其他字段,如
title
,author
,publishedDate
等。
- 每本书都有一个唯一的
{
"_id": ObjectId("..."),
"title": "Node.js in Action",
"author": "Manuel Kiessling",
"publishedDate": "2014-03-25",
"parentId": ObjectId("..."), // 可以为空,表示这是根节点
...
}
- 索引设计:
- 在
parentId
上创建索引,以便快速查找子节点。 - 在
_id
上创建唯一索引,确保每个文档的唯一性。
- 在
db.books.createIndex({ "parentId": 1 });
db.books.createIndex({ "_id": 1 }, { unique: true });
示例代码
以下是一个简单的示例代码,展示如何使用 Mongoose 来定义模型并进行基本的 CRUD 操作:
const mongoose = require('mongoose');
// 定义书籍模型
const bookSchema = new mongoose.Schema({
title: String,
author: String,
publishedDate: Date,
parentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Book'
}
});
const Book = mongoose.model('Book', bookSchema);
// 插入新书
async function addBook(bookData) {
const newBook = new Book(bookData);
await newBook.save();
}
// 查找所有子节点
async function findChildren(parentId) {
return await Book.find({ parentId });
}
// 查找所有父节点
async function findParents(childId) {
return await Book.findOne({ _id: childId }).populate('parentId');
}
// 示例使用
(async () => {
await mongoose.connect('mongodb://localhost:27017/bookstore', { useNewUrlParser: true, useUnifiedTopology: true });
// 添加一些书籍
await addBook({ title: 'Node.js in Action', author: 'Manuel Kiessling', publishedDate: new Date('2014-03-25') });
await addBook({ title: 'Express in Action', author: 'David Gonzalez', publishedDate: new Date('2018-09-25'), parentId: ObjectId('...') });
// 查找所有子节点
const children = await findChildren(ObjectId('...'));
console.log(children);
// 查找所有父节点
const parent = await findParents(ObjectId('...'));
console.log(parent);
mongoose.disconnect();
})();
总结
通过这种方式,我们可以在 MongoDB 中高效地存储和查询树形结构的书籍数据。利用 parentId
字段和索引,可以轻松实现对子节点的快速查找。同时,Mongoose 提供了方便的 API 来处理这些操作。
txt…
并不感觉 mongo 在这方面有什么优势啊。为什么用 mongo 呢?
就直接树形结构啊,一个doc一本书,再加一些冗余数据来优化查询
这个问题前段时间遇到过,不过我用的都是关系型数据库,没有用mongo,因为我不会,, 而且我们的书籍一般是存入到数据库中,都不会再修正书籍的内容了,然后取的时候一般是按照章节来取数据, 当时想到的方法主要有两个: 1、主要是在每个章节记录父节点的值 这个写的时候要还原一棵树逻辑比较复杂,查询和插入的时候记录的数据也比较多 2、使用nested set model来存储, 这个感觉完全满足我的要求,可以快速的还原一棵树,而且插入的时候不用纠结于父节点到底是谁, 参考:http://en.wikipedia.org/wiki/Nested_set_model
ps:看mongodb的官方网站,存储树的格式,貌似跟表没多大差别么,求指导,表示对于mongo不懂 http://docs.mongodb.org/manual/applications/data-models-tree-structures/
官方给举了四种树的例子,看哪种合适你。 用起来总是有那么点不爽。
不过弱弱的说一下,关系数据库用起也好不到哪去。
要使用MongoDB保存树形结构的书籍数据,并且主要的操作是查找父节点和子节点,可以考虑使用嵌套文档(嵌套结构)或引用文档(使用引用)。下面是两种设计方法的示例:
方法一:嵌套文档
在这种方法中,将所有子节点直接嵌入到父节点中。
{
"_id": ObjectId("650c3f9e4b91d73c9f2f258c"),
"name": "书籍分类",
"children": [
{
"_id": ObjectId("650c3f9e4b91d73c9f2f258d"),
"name": "子分类A",
"children": [
{
"_id": ObjectId("650c3f9e4b91d73c9f2f258e"),
"name": "子分类A-1"
}
]
},
{
"_id": ObjectId("650c3f9e4b91d73c9f2f258f"),
"name": "子分类B"
}
]
}
示例代码
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
name: String,
children: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }]
});
const Book = mongoose.model('Book', bookSchema);
// 插入数据
async function addBook() {
const rootBook = new Book({ name: '书籍分类' });
await rootBook.save();
const subCategoryA = new Book({ name: '子分类A' });
const subCategoryA1 = new Book({ name: '子分类A-1' });
subCategoryA.children.push(subCategoryA1._id);
await subCategoryA.save();
rootBook.children.push(subCategoryA._id);
await rootBook.save();
}
addBook();
方法二:引用文档
在这种方法中,每个父节点只包含其子节点的引用。
{
"_id": ObjectId("650c3f9e4b91d73c9f2f258c"),
"name": "书籍分类",
"children": [ObjectId("650c3f9e4b91d73c9f2f258d")]
}
示例代码
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
name: String,
children: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Book' }]
});
const Book = mongoose.model('Book', bookSchema);
// 插入数据
async function addBook() {
const rootBook = new Book({ name: '书籍分类' });
await rootBook.save();
const subCategoryA = new Book({ name: '子分类A' });
const subCategoryA1 = new Book({ name: '子分类A-1' });
subCategoryA.children.push(subCategoryA1._id);
await subCategoryA.save();
rootBook.children.push(subCategoryA._id);
await rootBook.save();
}
addBook();
这两种方法都可以满足需求,但嵌套文档更方便地进行树形遍历,而引用文档则更适合大数据量的情况。根据具体场景选择合适的方法。