MongoDB学习整理

MongoDB概念

  • database:数据库。一个MongoDB实例可以容纳多个数据库。
  • collection:集合。类似于关系型数据库中的表。
  • document:文档。类似于关系型数据库中的行。
  • field:域。类似于关系型数据库中的字段。

常用的命令

show dbs:显示所有的数据库
db:显示当前数据库对象
use XXX:连接到指定的数据库,XXX为数据库名

创建与删除

使用 use DATABASE_NAME命令可以创建数据库。如果数据库不存在就创建,如果存在就切到该数据库。刚创建的数据库在用show dbs命令时是不显示出来的,需要插入一条数据后使用show dbs命令才会显示。

使用db.dropDatabase()命令删除当前数据库。

增删改查

新增

使用insert()或save()方法向集合中插入文档。
语法:db.COLLECTION_NAME.insert(document)

db.col.insert({
    "name" : "ZZ",
    "name_cn" : "造字法",
    "order" : 0,
    "update_time" : 1463404597556,
    "delete_flag" : false
})

使用insert()方法后,MongoDB会自动给你添加一个_id字段,并自动给予一个值。使用save()方法,如果不指定_id字段save()方法就类似于insert()方法,如果指定_id字段,则会更新该_id的数据(其实就是修改)。

删除

使用remove()方法删除集合中的文档。
语法:

db.COLLECTION_NAME.remove(
   < query语句 >,
   {
     justOne: < boolean >,
     writeConcern: < document >
   }
)

query语句:删除文档的条件(可选)。
justOne : (可选)如果设为 true 或 1,则只删除一个文档。
writeConcern :(可选)抛出异常的级别。

示例:

db.col.remove({"name":"ZZ"})

修改

使用update()或save()方法向集合中插入文档。
update()语法:

db.COLLECTION_NAME.update(
   < query查询条件 >,
   < update语句 >,
   {
     upsert: < boolean >,
     multi: < boolean >,
     writeConcern: < document >
   }
)

query语句:更新的查询条件,类似于SQL语句的where。
update语句:要更新的对象以及更新操作符(如:$),类似于SQL语句的set。
upsert:可选,这个参数的意思是,如果不存在update的记录,是否插入新对象;true为插入,默认是false,不插入。
multi:可选,mongodb默认是false,只更新找到的第一条记录;如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern:可选,抛出异常的级别。

示例:

db.col.update({"name":"ZZ"},{$set:{"name_cn":"造字"}})
db.col.update({"name":"ZZ"},{$set:{"name_cn":"造字"}},{multi:true})

save()语法:

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

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

查询

使用find()方法对集合进行查询。

语法:

db.COLLECTION_NAME.find(query, projection)

query:查询的条件语句
projection:映射关系语句

示例:

db.dimensions.find()
db.dimensions.find({"name":"ZZ"})

条件

比较操作符
操作符 语法格式
等于 { < field >: < value > }
$eq(等于) { < field >: { $eq: < value > } }
$gt(大于) { < field >: { $gt: < value > } }
$gte(大于等于) { < field >: { $gte: < value > } }
$lt(小于) { < field >: { $lt: < value > } }
$lte(小于等于) { < field >: { $lte: < value > } }
$ne(不等于) { < field >: { $ne: < value > } }
$in { < field >: { $in: [< value1 >, < value2 >, … < valueN > ] } }
$nin { < field >: { $nin: [< value1 >, < value2 >, … < valueN > ] } }
逻辑操作符
操作符 语法格式
AND {key1:value1, key2:value2}
$and(AND操作) { $and: [ { < expression1 > }, { < expression2 > } , … , { < expressionN > } ] }
$or(OR操作) { $or: [ { < expression1 > }, { < expression2 > } , … , { < expressionN > } ] }
$not(NOT操作) { field: { $not: { < operator-expression > } } }
$nor(NOT OR操作) { $nor: [ { < expression1 > }, { < expression2 > }, … { < expressionN > } ] }

示例代码:

db.t_resources.find({"create_time":1463233651537,"update_time":1463416983501})
db.inventory.find( { $and: [ { price: { $ne: 1.99 } }, { price: { $exists: true } } ] } )
db.inventory.find( { $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )
db.inventory.find( { price: { $not: { $gt: 1.99 } } } )
db.inventory.find( { $nor: [ { price: 1.99 }, { sale: true } ]  } )
其他常用的操作符
操作符 语法格式 说明
$exists { field: { $exists: < boolean > } } 是否存在
$type { field: { $type: < BSON type number > or < String alias > } } 基于BSON类型来检索集合中匹配的数据类型,并返回结果
$all { < field >: { $all: [ < value1 > , < value2 > … ] } } 数组中的值全部包含

示例代码:

db.articles.find( { tags: { $all: [ [ "ssl", "security" ] ] } } )
db.addressBook.find( { "zipCode" : { $type : 2 } } )
db.records.find( { b: { $exists: false } } )

排序

使用sort()对数据进行排序。sort()方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列

语法:db.COLLECTION_NAME.find().sort({field: value})

示例:

db.dimensions.find().sort({'name_cn':1,'update_time':-1})

limit和skip方法

如果要读取指定数量的数据记录,可以使用limit()方法。

语法:db.COLLECTION_NAME.find().limit(NUMBER)

可以使用skip()方法跳过一定数量来读取数据。

语法:db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

所以,skip()方法和limit()方法结合可以用来分页。

示例:

db.dimensions.find().skip(10).limit(10)

索引

创建索引来提升查询性能是很常见的做法。

在MySQL中explain显示了MySQL如何使用索引来处理select语句以及连接表。同理,Mongo中的EXPLAIN用来描述查询路径,通过判断查询使用了哪个索引来帮助开发者诊断慢查询。如:db.numbers.find( {num: {"$gt": 199995 }} ).explain()

创建索引的示例:

db.numbers.ensureIndex({num: 1})
或者
db.numbers.createIndex({num: 1})

在MongoDB v3.0.0以后,建议使用的是createIndex()方法,ensureIndex()方法是不赞成在v3.0.0以后的Mongo版本中使用的。

语法:db.collection.createIndex( <key and index type specification>, <options> )

语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可

下表是一些比较常用的创建索引时候的附加参数:

操作符 类型 说明
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false。
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间.
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。

示例代码:

db.numbers.createIndex({num: 1}, {background: true})

聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。

Aggregation Pipeline

语法:db.collection.aggregate(pipeline, options),其中pipeline是一个管道数组,options是选项。

我们以一张官方文档上的图来说明一下聚合操作:

aggregation-pipeline.png

上图中我们看到了\$match,\$group。这两个是什么呢? 答案就在以下常用的piple操作列表。

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

以上各操作的用法详见:MongoDB文档

distinct方法

MongoDB的distinct命令是获取特定字段中不同值列表的最简单工具。该命令既适用于单键,也适用于数组键。distinct默认覆盖整个集合,但也可以通过查询选择器进行约束。

语法:db.collection.distinct(field, query)

示例:

db.products.distinct("tags")
db.inventory.distinct( "item.sku", { dept: "A" } )

group方法

gourp方法的作用与SQL中的 “SELECT <…> GROUP BY <…>”的作用一样。

语法:db.collection.group({ key, reduce, initial [, keyf] [, cond] [, finalize] })

示例:

results = db.reviews.group({
    key:{
        user_id:true
    },
    initial:{
        reviews:0,
        votes:0
    },
    reduce:function(doc,aggregator){
        aggregator.reviews +=1;
        aggregator.votes += doc.votes;
    },
    finalize:function(doc){
        doc.average_votes = doc.votes / doc.reviews;
    }
})

group方法的各个参数说明如下:

  • key,描述分组字段的文档。如上例,我们根据user_id分组,所以设置user_id:true。
  • keyf,这是一个JavaScript函数,应用于文档之上,为该文档生成一个键,当用于分组的键需要计算时,这个函数非常有用。
  • initial,作为聚合结果初始值的文档。reduce函数第一次运行时,该初始文档会作为聚合器的第一个值,通常会包含所有要聚合的键。
  • reduce,用于执行聚合的JavaScript函数。该函数接受两个参数:正被迭代的当前文档和用于存储聚合结果的聚合器文档。聚合器的初始值就是初始文档。reduce函数并不返回任何内容,它只不过是修改聚合器对象。
  • cond,过滤要聚合文档的查询选择器。如果不希望分组操作处理整个集合,就必须提供一个查询选择器。
  • finalize,在返回结果集之前应用于每个结果文档的JavaScript函数。该函数支持对分组操作的结果进行后置处理。

Map-Reduce

Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。
MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

语法:

db.collection.mapReduce(
                         <map>,
                         <reduce>,
                         {
                           out: <collection>,
                           query: <document>,
                           sort: <document>,
                           limit: <number>,
                           finalize: <function>,
                           scope: <document>,
                           jsMode: <boolean>,
                           verbose: <boolean>,
                           bypassDocumentValidation: <boolean>
                         }
                       )

同样的,来一张官方的图:
map-reduce.png

主要的参数说明如下;

  • map,应用于每个文档之上的JavaScript函数。该函数必须调用emit()来选择要聚合的键和值。
  • reduce,一个JavaScript函数,接受一个键和一个值列表。该函数对返回值的结构有严格要求,必须总是与values数组所提供的结构一致。reduce函数通常会迭代一个值的列表,在此过程中对其进行聚合。
  • query,用于过滤映射处理的集合的查询选择器。该参数的作用与group的cond参数相同。
  • sort,对于查询的排序。
  • limit,一个整数,指定了查询和排序的条数。
  • out,该参数决定了如何返回输出内容。要将所有输出作为命令本身的结果,传入{inline: 1}。请注意,这仅适用于结果集符合16 MB返回限制的情况。

参考: