You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在MongoDB中高效存储与检索用户笔记?Express实现优化问询

问题描述

我是Express新手,正在开发一款可按用户名存储和检索用户笔记的应用。目前我采用的方式是创建用户对象,并将笔记以数组形式存储。

当前实现代码

router.post('/editnotes', async (req, res) => {
    const data = req.body
    const { username } = req.query;
    const findUserNotes = await Note.findOne({ username: username })
    if (findUserNotes) {
        findUserNotes.notes.push(data)
        findUserNotes.save()
        res.status(201).send('ok here')
    }
    if (!findUserNotes) {
        const note = new Note({
            username: username,
            notes: [data]
        })
        await note.save()
        res.status(200).send('ok here')
    }
})

当前MongoDB数据结构

{
  "username": "Dhruv70",
  "notes": [
    {
      "topic": "hello",
      "content": "world"
    },
    {
      "topic": "bye",
      "content": "universe"
    },
    {
      "topic": "xxxxxxx",
      "content": "xx"
    }
  ],
  "__v": 31
}

该方案虽能正常运行,但我认为对数组中的笔记进行增删改操作较为繁琐,请问是否存在更高效的数据存储方法?


解决方案

方案一:优化现有嵌套数组结构,使用MongoDB原生操作符

不用完全推翻现有结构,MongoDB提供了专门的数组操作符,能让增删改笔记更高效,无需先查询整个文档再修改:

  1. 新增笔记:用$push或$addToSet(后者可避免重复笔记),结合updateOne的upsert参数,一步完成创建或更新:
router.post('/editnotes', async (req, res) => {
  const data = req.body;
  const { username } = req.query;
  
  const result = await Note.updateOne(
    { username: username },
    { $push: { notes: data } },
    { upsert: true }
  );

  res.status(result.upsertedCount > 0 ? 200 : 201)
     .send(result.upsertedCount > 0 ? '用户笔记创建成功' : '笔记新增成功');
});
  1. 修改指定笔记:用$set配合数组匹配器定位目标笔记:
router.put('/notes', async (req, res) => {
  const { username, topic } = req.query;
  const updatedContent = req.body.content;

  await Note.updateOne(
    { username: username, "notes.topic": topic },
    { $set: { "notes.$.content": updatedContent } }
  );

  res.send('笔记更新成功');
});

这里的$是占位符,会匹配查询条件中第一个符合要求的数组元素。

  1. 删除指定笔记:用$pull移除符合条件的数组元素:
router.delete('/notes', async (req, res) => {
  const { username, topic } = req.query;

  await Note.updateOne(
    { username: username },
    { $pull: { notes: { topic: topic } } }
  );

  res.send('笔记删除成功');
});

方案二:拆分集合(归一化存储)

如果用户笔记数量会非常多,或者需要更灵活的查询逻辑(比如按时间筛选所有用户的笔记),可以把笔记单独存为一个集合,通过username关联用户:

集合1:Users(可选,用于存储用户基础信息)

{
  "_id": ObjectId("xxx"),
  "username": "Dhruv70",
  // 其他信息如邮箱、创建时间等
}

集合2:Notes

{
  "_id": ObjectId("xxx"),
  "username": "Dhruv70",
  "topic": "hello",
  "content": "world",
  "createdAt": ISODate("2024-05-20T12:00:00Z")
}

这种方式的优势:

  • 增删改单条笔记更直接,无需操作数组,直接对Notes集合做CRUD
  • 可单独给Notes集合的字段(如username、createdAt)建立索引,提升查询效率
  • 避免单个用户文档过大(MongoDB单文档最大限制16MB)

对应的Express代码示例:

// 新增笔记
router.post('/notes', async (req, res) => {
  const { username } = req.query;
  const newNote = new Note({ ...req.body, username });
  await newNote.save();
  res.status(201).send('笔记创建成功');
});

// 查询用户所有笔记
router.get('/notes', async (req, res) => {
  const { username } = req.query;
  const notes = await Note.find({ username }).sort({ createdAt: -1 });
  res.send(notes);
});

选择建议

  • 如果用户笔记数量不多(单文档数组元素不超过几百个),方案一足够使用,代码更简洁,查询用户所有笔记时一次就能获取完整数据
  • 如果笔记数量大,或需要复杂的笔记筛选逻辑,方案二更适合,扩展性更强

内容的提问来源于stack exchange,提问作者Dhuruv Bansal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:40:54