MongoDB如何查询出所有二维数组中的内容并返回相应的数组

场景简介

一个在学校使用的公众号,公众号需要家长在微信中为自己的孩子进行注册然后进行一些后续的操作。这里我们要考虑到多位家长绑定同一个孩子和一个家长绑定多个孩子的场景。

首先一步要做的就是要把家长的登录信息和填写的孩子信息进行存储,这里我们使用的是MongoDB,肯定优先会把所有信息存储到一个文档中。文档大致结构如下

  • 整体信息是一个文档,可以看做一个文档为一个家庭
  • 父母可能为多个,所以使用内嵌数组存储
  • 孩子同样可能为多个,同样使用内嵌数组来存储

MongoDB文档结构大概如下

{
        _id: ObjectId("5f06ae15cf20bc8fea5cf982"),
        patients: [
            {
                p_id: "nxM81594361333",
                name: "cofl",
                sex: "男",
                tel: "12222222222",
                birth_year: "2000",
                school_id: "B0FFGAPLTR",
                school_name: "xxxxxxx",
                grade_id: "g01",
                grade_name: "一年级",
                class_id: "c01",
                class_name: "一班",
                add_time: NumberLong(1594361333)
            },
            {
                p_id: "nxM81594363774",
                name: "cofl111",
                sex: "男",
                tel: "12222222333",
                birth_year: "2000",
                school_id: "B0FFGAPLTR",
                school_name: "xxxxxxxx",
                grade_id: "g01",
                grade_name: "一年级",
                class_id: "c01",
                class_name: "一班",
                add_time: NumberLong(1594363774)
            },
            {
                p_id: "nxM81594365730",
                name: "cofl222",
                sex: "男",
                tel: "19999222233",
                birth_year: "2000",
                school_id: "xxxxxxx",
                school_name: "xxxxxxx",
                grade_id: "g01",
                grade_name: "一年级",
                class_id: "c01",
                class_name: "一班",
                add_time: NumberLong(1594365730)
            }
        ],
        parents: [
            {
                   openid: "xxxxxxxxxxxx",
                nickname: "cofl",
                sex: 1,
                city: "朝阳",
                province: "北京",
                country: "中国"
            }
        ]
    }

具体遇到的问题

  1. 父母及孩子都是内嵌数组,所以需要对MongoDB内嵌array的增删改查
  2. 在某些场景需要将所有的学生(孩子)信息进行列表展示,所以我们需要将学生信息查询出来
    • 首先我们排除直接查询所有文档然后在进行遍历组合学生list的方案
    • 再次我们希望能通过sql直接查询到所有学生并可以进行排序,以及支持页面分页展示。

请开始你的表演

项目使用golang进行的开发,所以引用的部分代码均为golang代码

  1. MongoDB内嵌数组的的操作。 所有操作:mongodb 官方文档

添加插入

插入---当父母初次添加孩子信息时,我们需要将孩子信息添加到patients内嵌数组中,此时我们可以使用官方文档中的$push

filter = bson.M{"parents.openid": openid}
update = bson.M{"$push": bson.M{"patients": patient}}
option := options.Update()
result, err := db.UpdateOne(ctx, filter, update, option)

这样我们就可以在文档patients数组中添加一个孩子的信息。

在MongoDB的官方文档中我们还发现$addToSet

文档描述 :The addToSet operator adds a value to an array unless the value is already present, in which case addToSet does nothing to that array.
我个人理解就是 $addToSet会看数组中有没有要追加的内容,如果有就不再追加,如果没有就进行追加。有时间可以自己尝试一下。

更新

更新修改---当父母需要更新修改孩子的信息时,我们需要更新文档中patients内嵌数组中的某一条信息,此时我们可以使用$set

filter = bson.M{"parents.openid": openid, "patients.p_id": patient.PId}
    
    //如果是更新整个文档使用bson.M{"patients.$": patient}
    update = bson.M{"$set": bson.M{"patients.$": patient}}
    
    //如果是更新文档中的某个值可以使用bson.M{"patients.$.name": "cofl888"}
    update = bson.M{"$set": bson.M{"patients.$.name": "cofl888"}}
    
    option := options.Update()
    result, err := db.UpdateOne(ctx, filter, update, option)

这样我们就可以实现内嵌数组中一条信息的修改更新。

需要注意的是目前我暂时只能做到单个属性修改或者整条数据更新,没有找到可以只更新内嵌文档中一条数据的某几个字段的方法,如有知道的还请告知补充

  1. 通过使用MongoDB的聚合查询来实现学生列表的查询。 同样给出官方文档,方便查看

MongoDB的聚合查询重点是pipeline(管道)操作,此次我们主要用到了$unwind ,首先先看下具体的代码实现

option := options.Aggregate()
    db, ctx := mongoDb("user")
    pipeline := []bson.M{bson.M{"$unwind": "$patients"},
        bson.M{"$project": bson.M{"_id": 1, "parents": 1, "patient": "$patients"}},
        bson.M{"$sort":bson.M{"patient.add_time":-1}},
        bson.M{"$skip": (page - 1) * limit},
        bson.M{"$limit": limit}}

    cur, err1 := db.Aggregate(ctx, pipeline, option)

$unwin的官方文档解释

Deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.

大概意思:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。

重点看下上面查询的几个要点

  • $unwind 实现内嵌数组拆解
  • bson.M{"project": bson.M{"_id": 1, "parents": 1, "patient": "patients"}}, 实现控制查询哪些字段;"patient": "patients"实现了拆解内嵌数组后字段修改名字的作用 ***limit和$skip是我们很常用的分页用到的,这里要特别说明一下在这里使用时,他们两个的先后顺序很重要,上面代码会先进性skip然后在进行limit,如果换成下面的顺序,则会先进行limit,然后进行skip就会导致查询不到数据***
pipeline := []bson.M{bson.M{"$unwind": "$patients"},
        bson.M{"$project": bson.M{"_id": 1, "parents": 1, "patient": "$patients"}},
        bson.M{"$sort":bson.M{"patient.add_time":-1}},
        bson.M{"$limit": limit}}
        bson.M{"$skip": (page - 1) * limit},

看下实际输出结果

原本是一个文档,查询结果变成了一个数组,仔细看对比一下原文档,我们会发现其实是把patients内嵌数组的每一项和文档其他项组合成了一个新文档,数组数量就是patients内嵌数组的item数量,这样正好就是我们想要的学生列表的形式。

    [
      {
        "id": "5f06ae15cf20bc8fea5cf982",
        "parents": [
          {
            "subscribe": 0,
            "openid": "xxxxxxx",
            "nickname": "cofl",
            "sex": 1,
            "city": "朝阳",
            "province": "北京",
            "country": "中国"
          }
        ],
        "patient": {
          "p_id": "nxM81594365730",
          "name": "cofl222",
          "sex": "男",
          "tel": "19999222233",
          "birth_year": "2000",
          "school_id": "B0FFGAPLTR",
          "school_name": "东交民巷小学马坊分校",
          "grade_id": "g01",
          "grade_name": "一年级",
          "class_id": "c01",
          "class_name": "一班",
          "add_time": 1594365730
        }
      },
      {
        "id": "5f06ae15cf20bc8fea5cf982",
        "parents": [
          {
            "subscribe": 0,
            "openid": "xxxxxxxxx",
            "nickname": "cofl",
            "sex": 1,
            "city": "朝阳",
            "province": "北京",
            "country": "中国"
          }
        ],
        "patient": {
          "p_id": "nxM81594363774",
          "name": "cofl111",
          "sex": "男",
          "tel": "12222222333",
          "birth_year": "2000",
          "school_id": "B0FFGAPLTR",
          "school_name": "xxxxxxx",
          "grade_id": "g01",
          "grade_name": "一年级",
          "class_id": "c01",
          "class_name": "一班",
          "add_time": 1594363774
        }
      },
      {
        "id": "5f06ae15cf20bc8fea5cf982",
        "parents": [
          {
            "subscribe": 0,
            "openid": "xxxxxxx",
            "nickname": "cofl",
            "sex": 1,
            "city": "朝阳",
            "province": "北京",
            "country": "中国",
          }
        ],
        "patient": {
          "p_id": "nxM81594361333",
          "name": "cofl",
          "sex": "男",
          "tel": "12222222222",
          "birth_year": "2000",
          "school_id": "B0FFGAPLTR",
          "school_name": "xxxxxx",
          "grade_id": "g01",
          "grade_name": "一年级",
          "class_id": "c01",
          "class_name": "一班",
          "add_time": 1594361333
        }
      }
    ]

总结

  • MongoDB作为比较常用的非关系型数据,文档结构很灵活。在数据库结构设计的时候我们可以有更多的可能性
  • 个人觉得内嵌数组对于MongoDB来说也比较重要,能够有效的扩展文档结构,刚开始使用的时候可能会直接查处整个文档然后修改完在更新回去...(狗头),MongoDB发展到现在功能已经和完善,各个语言的driver也都比较成熟。最重要的还是要多去看看官方的文档
  • 这次主要是根据开发中遇到的情景简单介绍了MongoDB的Array和聚合查询Aggregate。

以上内容为本人真实代码验证过的,如阐述如有差错的还请各位能纠正补充。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355