MongoDb学习

作者: 以虚名称之 | 来源:发表于2018-03-08 11:09 被阅读0次

    0. 什么是MongoDB?

    MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。
    MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
    MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。
    MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。


    1. 为什么用MongoDB?

    现代的计算机应用大多使用关系型数据库来存储数据(如MySql, Sqlite等等),它的特点是数据以表格(table)的形式储存起来的。

    数据库由一张张排列整齐的表格构成,就好像一个Excel表单一样,每个表格会有若干列。比如一个学生信息表:可能包含学号、姓名、性别、入学年份、高考成绩、籍贯等等。而表格的每一排,则是一个个学生的具体信息。

    在企业级应用和前互联网时代,关系型数据库几乎是不二选择。关系型数据库的特点是有整齐划一的组织,很方便对数据进行描述、插入、搜索。

    想象有一个网上服装商店,它的主要的数据可能是储存在一张叫products的表单里。

    表单可能包含这些列:商品编号(ID)、名称(Name)、商家(brand)、主目录(cate)、子目录(sub-cat)、零售价(price)、是否促销(promotion)等等。如果有用户想要查找所有价格低于250元的正在促销的鞋子的编号和名称,则可以执行类似于以下的SQL语句

    SELECT ID, name FROM products WHERE cate='shoes' AND price<250 and AND promotion=true;
    

    SQL具备了强大了的深度查询能力,能满足各式各样的查询要求。而如果要对数据进行添加和删除,成本也是非常低的。这些是SQL的优势之一, 但随着互联网的兴起以及数据形式的多样化,四平八稳的SQL表单在一些领域渐渐显现出它的劣势。

    让我们通过一个例子来说明。考虑一个博客后台系统,如果我们用关系型数据库为每篇博客(article)建一个表单的话,这个表单大概会包括以下这些列:

    ID Title Description Author Content Likes
    A_1 Title1 Political Article Joe Content 1 12
    A_2 Title2 Humorous Story Sam Content 2 50

    这时候用SQL数据库来存储是非常方便的,但假如我们要位每篇文章添加评论功能,会发现每篇文章可能要多篇评论,而且这个数目是动态变化的,而且每篇评论还包括好几项内容:评论的人、评论的时间、以及评论内容。这时候要将这些内容都塞进上述的那个表,就显得很困难。通常的做法是为评论(comment)单独建一个表:

    ID Author Time Content ArticleId
    C_1 Anna 2014-12-26 08:23 Really good articles! A_1
    C_2 David 2014-12-25 09:30 I like it! A_1

    类似地,每篇文章可能会有若干标签(tags)。标签本身又是一个表单:

    ID Category Tags Content ArticleId
    T_1 Anna 2014-12-26 08:23 Really good articles! A_1
    T_2 David 2014-12-25 09:30 I like it! A_2

    而博客的表格则要通过foreign key跟这些相关联的表格联系起来(可能还包括作者、出版社等其它表格)。这样一来,当我们做查询的时候,比如说,“找出评论数不少于3的标签为‘政治评论’的作者为Sam的文章”,就会涉及到复杂的跨表查询,需要大量使用join语句。这种跨表查询不仅降低了查询速度,而且这些语句写起来也不简单。

    那么,如果用MongoDB数据库来实现,可以如何设计数据模型呢?很简单,像下面这样 MongoDb数据模型

    { 
       _id: POST_ID
       title: TITLE_OF_POST, 
       description: POST_DESCRIPTION,
       author: POST_BY,
       tags: [TAG1, TAG2, TAG3],
       likes: TOTAL_LIKES, 
       comments: [  
          
             user:'COMMENT_BY',
             message: TEXT,
             dateCreated: DATE_TIME,
          },
          {
             user:'COMMENT_BY',
             message: TEXT,
             dateCreated: DATE_TIME,
          }
       ]
    }
    

    在MongoDB里,每篇博客文章以一个文档(document)的形式保存起来,而文档内部包含了很多项目,比如title tags等,每一个项目都是key-value的形式,即有一个项目的名字,比如title,以及它的值TITLE_OF_POST。而重要的是,一个key可以有多个values,他们用[]括起来。

    这种“宽松”的数据存储形式非常灵活,MongoDB不限制每个key对应的values的数目。比如有的文章没有评论,则它的值就是一个空集,完全没有问题;有的文章评论很多,也可以无限制地插入。更灵活的是,MongoDB不要求同一个集合(collection,相当于SQL的table)里面的不同document有相同的key,比如除了上述这种文档组织,有的文档所代表的文章可能没有likes这个项目,再比如有的文章可能有更多的项目,比如可能还有dislikes等等。这些不同的文档都可以灵活地存储在同一个集合下,而且查询起来也异常简单,因为都在一个文档里,不用进行各种跨文档查询。而这种MongoDB式的存储也方便了数据的维护,对于一篇博客文章来说,所有的相关数据都在这个document里面,不用去考虑一个数据操作需要involve多少个表格。

    当然,除了上述的优点,MongoDB还有不少别的优势,比如MongoDB的数据是用JSON(Javascript Object Notation)存储的(就是上面的这种key-value的形式),而几乎所有的web应用都是基于Javascript的。因此,存储的数据和应用的数据的格式是高度一致的,不需经过转换。更多的优点可以查看 MongoDb 优点

    2. 安装MongoDb和连接数据库

    请参考链接 Windows平台Linux平台连接数据库本文在此不多做介绍了。

    3. 入门学习

    (0)MongoDB概念解析

    不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库。
    下表将帮助您更容易理解Mongo中的一些概念:

    SQL术语/概念 MongoDB术语/概念 解释/说明
    database database 数据库
    table collection 数据库表/集合
    row document 数据记录行/文档
    column field 数据字段/域
    index index 索引
    table joins 表连接,MongoDB不支持
    primary key primary key 主键,MongoDB自动将_id字段设置为主键
    通过下图实例,我们也可以更直观的了解Mongo中的一些概念:

    (1)MongoDB 数据库

    1. 创建数据库

    语法 : use DATABASE_NAME

    如果数据库不存在,则创建数据库,否则切换到指定数据库。

    实例

    以下实例我们创建了数据库 runoob:

    > use runoob
    switched to db runoob
    > db
    runoob
    > 
    

    如果你想查看所有数据库,可以使用 show dbs 命令:

    > show dbs
    local  0.078GB
    test   0.078GB
    > 
    

    可以看到,我们刚创建的数据库 runoob 并不在数据库的列表中, 要显示它,我们需要向 runoob 数据库插入一些数据。

    > db.runoob.insert({"name":"菜鸟教程"})
    WriteResult({ "nInserted" : 1 })
    > show dbs
    local   0.078GB
    runoob  0.078GB
    test    0.078GB
    > 
    

    MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。

    1. 删除数据库

    语法: db.dropDatabase()

    实例

    以下实例我们删除了数据库 runoob。

    首先,查看所有数据库:

    > show dbs
    local   0.078GB
    runoob  0.078GB
    test    0.078GB
    

    接下来我们切换到数据库 runoob:

    > use runoob
    switched to db runoob
    > 
    

    执行删除命令:

    > db.dropDatabase()
    { "dropped" : "runoob", "ok" : 1 }
    

    最后,我们再通过 show dbs 命令数据库是否删除成功:

    > show dbs
    local  0.078GB
    test   0.078GB
    >
    

    删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。

    (2)集合

    1. 创建集合

    语法:db.createCollection(name, options)

    参数说明:
    name: 要创建的集合名称
    options: 可选参数, 指定有关内存大小及索引的选项

    实例

    在 test 数据库中创建 runoob 集合:

    > use test
    switched to db test
    > db.createCollection("runoob")
    { "ok" : 1 }
    >
    

    如果要查看已有集合,可以使用 show collections 命令:

    > show collections
    runoob
    

    下面是带有几个关键参数的 createCollection() 的用法:

    创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。

    > db.createCollection("mycol", { capped : true, autoIndexId : true, size : 
       6142800, max : 10000 } )
    { "ok" : 1 }
    >
    

    在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

    > db.mycol2.insert({"name" : "菜鸟教程"})
    > show collections
    mycol2
    
    1. 删除集合

    语法:db.collection.drop()

    返回值

    如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。

    实例

    在数据库 mydb 中,我们可以先通过 show collections 命令查看已存在的集合:

    >use mydb
    switched to db mydb
    >show collections
    mycol
    mycol2
    system.indexes
    runoob
    >
    

    接着删除集合 mycol2 :

    >db.mycol2.drop()
    true
    >
    

    通过 show collections 再次查看数据库 mydb 中的集合:

    >show collections
    mycol
    system.indexes
    runoob
    >
    

    从结果中可以看出 mycol2 集合已被删除。

    (3)文档

    1.插入文档

    语法:db.COLLECTION_NAME.insert(document)

    实例

    以下文档可以存储在 MongoDB 的 runoob 数据库 的 col 集合中:

    >db.col.insert({title: 'MongoDB 教程', 
        description: 'MongoDB 是一个 Nosql 数据库',
        by: '菜鸟教程',
        url: 'http://www.runoob.com',
        tags: ['mongodb', 'database', 'NoSQL'],
        likes: 100
    })
    

    以上实例中 col 是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。

    查看已插入文档:

    > db.col.find()
    { "_id" : ObjectId("56064886ade2f21f36b03134"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
    > 
    

    我们也可以将数据定义为一个变量,如下所示:

    > document=({title: 'MongoDB 教程', 
        description: 'MongoDB 是一个 Nosql 数据库',
        by: '菜鸟教程',
        url: 'http://www.runoob.com',
        tags: ['mongodb', 'database', 'NoSQL'],
        likes: 100
    });
    

    执行后显示结果如下:

    {
            "title" : "MongoDB 教程",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    

    执行插入操作:

    > db.col.insert(document)
    WriteResult({ "nInserted" : 1 })
    > 
    

    插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据

    3.2 版本后还有以下几种语法可用于插入文档:

    db.collection.insertOne():向指定集合中插入一条文档数据
    db.collection.insertMany():向指定集合中插入多条文档数据

    2.更新文档

    update方法
    update() 方法用于更新已存在的文档。语法格式如下:

    db.collection.update(
       <query>,
       <update>,
       {
         upsert: <boolean>,
         multi: <boolean>,
         writeConcern: <document>
       }
    )
    
    实例

    我们在集合 col 中插入如下数据:

    >db.col.insert({
        title: 'MongoDB 教程', 
        description: 'MongoDB 是一个 Nosql 数据库',
        by: '菜鸟教程',
        url: 'http://www.runoob.com',
        tags: ['mongodb', 'database', 'NoSQL'],
        likes: 100
    })
    

    接着我们通过 update() 方法来更新标题(title):

    >db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })   # 输出信息
    > db.col.find().pretty()
    {
            "_id" : ObjectId("56064f89ade2f21f36b03136"),
            "title" : "MongoDB",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    >
    

    可以看到标题(title)由原来的 "MongoDB 教程" 更新为了 "MongoDB"。

    以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。

    >db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})
    

    save() 方法
    save() 方法通过传入的文档来替换已有文档。语法格式如下:

    db.collection.save(
       <document>,
       {
         writeConcern: <document>
       }
    )
    

    参数说明:

    document : 文档数据。
    writeConcern :可选,抛出异常的级别。

    实例

    以下实例中我们替换了 _id 为 56064f89ade2f21f36b03136 的文档数据:

    >db.col.save({
        "_id" : ObjectId("56064f89ade2f21f36b03136"),
        "title" : "MongoDB",
        "description" : "MongoDB 是一个 Nosql 数据库",
        "by" : "Runoob",
        "url" : "http://www.runoob.com",
        "tags" : [
                "mongodb",
                "NoSQL"
        ],
        "likes" : 110
    })
    

    替换成功后,我们可以通过 find() 命令来查看替换后的数据

    >db.col.find().pretty()
    {
            "_id" : ObjectId("56064f89ade2f21f36b03136"),
            "title" : "MongoDB",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "Runoob",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "NoSQL"
            ]
            "likes" : 110
    }
    > 
    

    3.删除文档

    remove 方法

    实例

    以下文档我们执行两次插入操作:

    >db.col.insert({title: 'MongoDB 教程', 
        description: 'MongoDB 是一个 Nosql 数据库',
        by: '菜鸟教程',
        url: 'http://www.runoob.com',
        tags: ['mongodb', 'database', 'NoSQL'],
        likes: 100
    })
    

    使用 find() 函数查询数据:

    > db.col.find()
    { "_id" : ObjectId("56066169ade2f21f36b03137"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
    { "_id" : ObjectId("5606616dade2f21f36b03138"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
    

    接下来我们移除 title 为 'MongoDB 教程' 的文档:

    >db.col.remove({'title':'MongoDB 教程'})
    WriteResult({ "nRemoved" : 2 })           # 删除了两条数据
    >db.col.find()
    ……                                        # 没有数据
    

    如果你只想删除第一条找到的记录可以设置 justOne 为 1,如下所示:

    >db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)
    

    如果你想删除所有数据,可以使用以下方式(类似常规 SQL 的 truncate 命令):

    >db.col.remove({})
    >db.col.find()
    >
    

    推荐使用 deleteOne() 和 deleteMany() 方法。

    如删除集合下全部文档:

    db.inventory.deleteMany({})
    删除 status 等于 A 的全部文档:

    db.inventory.deleteMany({ status : "A" })
    删除 status 等于 D 的一个文档:

    db.inventory.deleteOne( { status: "D" } )

    4.查询文档.

    find() 方法

    find() 方法以非结构化的方式来显示所有文档。

    MongoDB 查询数据的语法格式如下:

    db.collection.find(query, projection)
    

    query :可选,使用查询操作符指定查询条件
    projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
    如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:

    >db.col.find().pretty()
    

    pretty() 方法以格式化的方式来显示所有文档。

    实例

    以下实例我们查询了集合 col 中的数据:

    > db.col.find().pretty()
    {
            "_id" : ObjectId("56063f17ade2f21f36b03133"),
            "title" : "MongoDB 教程",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    

    除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。

    MongoDB 与 RDBMS Where 语句比较

    如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:

    操作 格式 范例 RDBMS中的类似语句
    等于 {<key>:<value>} db.col.find({"by":"菜鸟教程"}).pretty() where by = '菜鸟教程'
    小于 {<key>:{$lt:<value>}} db.col.find({"likes":{$lt:50}}).pretty() where likes < 50
    小于或等于 {<key>:{$lte:<value>}} db.col.find({"likes":{$lte:50}}).pretty() where likes <= 50
    大于 {<key>:{$gt:<value>}} db.col.find({"likes":{$gt:50}}).pretty() where likes > 50
    大于或等于 {<key>:{$gte:<value>}} db.col.find({"likes":{$gte:50}}).pretty() where likes >= 50
    不等于 {<key>:{$ne:<value>}} db.col.find({"likes":{$ne:50}}).pretty() where likes != 50

    MongoDB AND 条件

    MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。

    语法格式如下:

    >db.col.find({key1:value1, key2:value2}).pretty()
    
    实例

    以下实例通过 by 和 title 键来查询 菜鸟教程 中 MongoDB 教程 的数据

    > db.col.find({"by":"菜鸟教程", "title":"MongoDB 教程"}).pretty()
    {
            "_id" : ObjectId("56063f17ade2f21f36b03133"),
            "title" : "MongoDB 教程",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    

    以上实例中类似于 WHERE 语句:WHERE by='菜鸟教程' AND title='MongoDB 教程'

    MongoDB OR 条件

    MongoDB OR 条件语句使用了关键字 $or,语法格式如下:

    >db.col.find(
       {
          $or: [
             {key1: value1}, {key2:value2}
          ]
       }
    ).pretty()
    
    实例

    以下实例中,我们演示了查询键 by 值为 菜鸟教程 或键 title 值为 MongoDB 教程 的文档。

    >db.col.find({$or:[{"by":"菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
    {
            "_id" : ObjectId("56063f17ade2f21f36b03133"),
            "title" : "MongoDB 教程",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    >
    

    AND 和 OR 联合使用

    以下实例演示了 AND 和 OR 联合使用,类似常规 SQL 语句为: 'where likes>50 AND (by = '菜鸟教程' OR title = 'MongoDB 教程')'

    >db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
    {
            "_id" : ObjectId("56063f17ade2f21f36b03133"),
            "title" : "MongoDB 教程",
            "description" : "MongoDB 是一个 Nosql 数据库",
            "by" : "菜鸟教程",
            "url" : "http://www.runoob.com",
            "tags" : [
                    "mongodb",
                    "database",
                    "NoSQL"
            ],
            "likes" : 100
    }
    

    (4) 管道

    管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

    MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

    表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

    这里我们介绍一下聚合框架中常用的几个操作:

    $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
    $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
    $limit:用来限制MongoDB聚合管道返回的文档数。
    $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
    $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
    $group:将集合中的文档分组,可用于统计结果。
    $sort:将输入文档排序后输出。
    $geoNear:输出接近某一地理位置的有序文档。

    管道操作符实例
    1、$project实例

    db.article.aggregate(
        { $project : {
            title : 1 ,
            author : 1 ,
        }}
     );
    

    这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:

    db.article.aggregate(
        { $project : {
            _id : 0 ,
            title : 1 ,
            author : 1
        }});
    

    2.$match实例

    db.articles.aggregate( [
                            { $match : { score : { $gt : 70, $lte : 90 } } },
                            { $group: { _id: null, count: { $sum: 1 } } }
                           ] );
    

    $match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。

    3.$skip实例

    db.article.aggregate(
        { $skip : 5 });
    

    经过$skip管道操作符处理后,前五个文档被"过滤"掉。

    涉及mongoDB的知识有很多,本文只是简短介绍了其中的一部分,更多资料请参考 MongoDb 文档)

    本文参考资料:
    【1】github
    【2】菜鸟教材(本文主要参考于该网站资料)
    【3】英文文档

    相关文章

      网友评论

        本文标题:MongoDb学习

        本文链接:https://www.haomeiwen.com/subject/fewefftx.html