V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
naijoag
V2EX  ›  程序员

MongoDB 文档结构设计

  •  
  •   naijoag · 2022-10-18 22:07:28 +08:00 · 2006 次点击
    这是一个创建于 772 天前的主题,其中的信息可能已经有所发展或是发生改变。

    mongodb 文档结构设计

    需求: mongodb 记录用户搜索日志并提供用户和管理后台查询

    • 用户端:需要查询时间范围内的日志

    query+qty 唯一

    用户端需要展示一个这样的表格:

    query (搜索型号) qty (搜索数量) weekly_search_times (最近 7 天搜索次数) last_search_time (最近搜索时间)
    型号 1 1 10 2022-10-13 00:00:00
    型号 1 2 4 2022-10-18 00:00:00
    型号 2 3 4 2022-10-18 01:00:00
    • 管理后台:要查询搜索词 query 被哪些用户搜索过

    开始是设计这样的结构,之前没怎么用过 mongo ,就用了这种扁平化的结构。

    {
        "_id": xxx,
        "query": "xxx",
        "user": 1,
        "qty": 100
    }
    

    这种当然是最简单方便的,但是领导说不行,这样还不如用 mysql ,这样的化存储的数据量大,后期查询会慢。说要设计一种结构,最好是一个用户一个文档,这个文档除了放用户的搜索历史,后面还可能放一些其他日志类的数据。 但是一个 mongo 文档又有 16M 的大小限制,说是设置一个容量,如果最多存 1000 个搜索日志,超过了清除。于是就有了下面这种结构:

    {
        "_id": xxx,
        "user": 1000,
        "search_history": [{"query": "xxx", "time": 1660000, "qty": 1}]
    }
    
    

    这样子呢,相对上一种方法大大减少了文档数,一个用户一个文档,查询效率貌似变高了,因为只要查到一个文档就行,但是查询时无法直接对 search_history 进行过滤, 要把整个文档查询出来然后在内存中实现过滤(时间筛选)、分组( group by query+qty )、分页。 而且查询某个搜索词被哪些用户搜索过不方便实现。 所以这种方案不行。

    搜索接口的访问量挺大的,所以是这个日志是写多读少的情况。

    所以应该设计怎样的一个结构较为合理,各位大佬赐教。

    15 条回复    2022-10-19 21:27:41 +08:00
    naijoag
        1
    naijoag  
    OP
       2022-10-18 22:20:48 +08:00
    {
    "_id": xxx,
    "query": "xxx",
    "user": 1,
    "qty": 100
    }
    少了一个 time 字段(时间戳)这样的结构也是需要在代码中操作的 开始不知道前端需要展示那样的一个表格(数据要聚合)
    naijoag
        2
    naijoag  
    OP
       2022-10-18 22:23:44 +08:00
    用户端:需要查询时间范围内的日志 (时间范围是日期)
    AS4694lAS4808
        3
    AS4694lAS4808  
       2022-10-18 22:56:30 +08:00
    如果是哪些词被用户搜索过,应该一个词一个文档?这个文档包含搜索过的用户的 id ?去重或者不去重?
    mysql 用关系表+索引搜索也很快。
    另外第一种方式查询起来性能还可以,除非真有上千万的用户每天查 N 次。。
    naijoag
        4
    naijoag  
    OP
       2022-10-18 23:02:42 +08:00
    @AS4694lAS4808 意思是再弄一个集合:一个词一个文档 用来满足后台的需求吗
    第一种我是觉得还可以 倒没有那么多用户 但是领导说就是第一种方式的话就没必要用 mongo 了 因为 mysql 也可以实现
    wxf666
        5
    wxf666  
       2022-10-19 00:16:52 +08:00
    数据库新手求问,为嘛不能用 MySQL 呢?如果用 MySQL ,这种表结构行不行:

    搜索日志表(
      用户 ID INT ,时间 TIMESTAMP ,型号 TINYTEXT ,数量 INT ,
       PRIMARY KEY (用户 ID ,时间,型号,数量),
       INDEX (型号,数量)


    理由:

    1. 每个用户搜索的内容,会尽量聚集在几页(有点类似『一个用户一个文档』),并按时间排序(尽量顺序插入),方便用户端按时间搜索(分组只能临时计算。但问题不大,反正一个用户一段时间内应该也没多少数据)

    2. 后台查询某关键词被谁搜索过,走索引也很快
    lithiumii
        6
    lithiumii  
       2022-10-19 00:23:06 +08:00 via Android
    查询时可以过滤的,你搜搜 mongo 的 query 怎么写
    caotian
        7
    caotian  
       2022-10-19 00:26:48 +08:00
    $elemMatch
    naijoag
        8
    naijoag  
    OP
       2022-10-19 09:45:29 +08:00
    @lithiumii @caotian 我看了$elemMatch 好像只是说匹配 search_history 里面满足条件的 doc 我拿到了 doc 还得在代码里对 search_history 进行过滤等操作
    lithiumii
        9
    lithiumii  
       2022-10-19 12:09:11 +08:00 via Android   ❤️ 1
    @naijoag 先 match 再 unwind 再 match 呢?
    w07128597
        10
    w07128597  
       2022-10-19 13:08:38 +08:00 via iPhone
    第二种方法几年前用过,存的用户的消息记录,可以不用全查出来在内存中筛选分页的,mongodb 自带的函数可以实现
    naijoag
        11
    naijoag  
    OP
       2022-10-19 14:09:25 +08:00
    @w07128597 什么函数
    AS4694lAS4808
        12
    AS4694lAS4808  
       2022-10-19 14:16:39 +08:00
    @naijoag 要只是为了搜索这个功能启用 mongo 我觉得没必要。。增加了项目复杂度

    如果非要用 mongo ,就是一个词一个文档,每次更新用户的 id 和查询时间之类的,甚至对用户 id 去重和时间排序(用户少可以在内存里干,用户多就得用 mongo 函数)
    w07128597
        13
    w07128597  
       2022-10-19 18:44:12 +08:00 via iPhone
    @naijoag 不是一个函数,也是类似于 9 楼的多个操作合集,具体我也忘了,好几年了,不过我当时全部是用 mongodb 本身的功能实现的,查询效率确实高
    naijoag
        14
    naijoag  
    OP
       2022-10-19 21:27:32 +08:00
    目前利用 aggregate ,pipline 里面经历了 objectToArray 和 unwind 两次实现了如下转换。不知道你说的是不是也是这样。

    ```javascript
    {
    "_id": 10000,
    "search_history": {
    "query1": [{"qty": 1, "time": 1666185797}, {"qty": 2, "time": 1666185798}]
    }
    }
    ```

    ===>


    ```javascript
    [
    {"_id": 10000, "qty": 1, "time": 1666185797},
    {"_id": 10000, "qty": 2, "time": 1666185798}
    ]
    ```
    naijoag
        15
    naijoag  
    OP
       2022-10-19 21:27:41 +08:00
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2831 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 15:31 · PVG 23:31 · LAX 07:31 · JFK 10:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.