一、数据库操作

切换数据库

use database_name

use school

注:如果数据库存在,则切换到该数据库下,如果此数据库还不存在,也可以切过来,但是并不能立刻创建该数据库

查看所有得数据库

show dbs

备注:刚创建的数据库shcool如果不在查询的列表中,如果要显示它,则需要向school中插入数据

db.students.insert({age:1})

查看当前使用的数据库

删除数据库

db.dropDatabase()

二、集合操作

查看集合帮助

use demo

db.demo.help()

db.createCollection(collection_name)

创建集合并插入一个文档

db.collection_name.insert({document})

db.demo.insert({age:1})

注:上图里的ObjectId是有规律的,规律如下

之前我们使用MySQL等关系型数据库时,主键都是设置成自增的。但在分布式环境下,这种方法就不可行了,会产生冲突。为此,MongoDB采用了一个称之为ObjectId的类型来做主键。ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,依次代表:

collection_name:集合的名字

document 插入的文档(数据)

注:每当插入一条新文档的时候mongodb会自动为此文档生成一个 _id 属性, _id 属性是唯一的,用来标识一个文档, _id 也可以直接指定,但如果数据库中此集合下已经有此 _id 的话,将插入失败2

db.collection_name.save(document)

注:如果不指定 _id 字段,save方法类似于insert方法。如果指定 _id 字段,则会更新 _id 的数据。

db.demo.save({_id:1, age:2})

db.collection.update(
upsert: ,
multi:

query: 查询条件,指定要更新符合哪些条件的文档

update: 更新后的对象或指定一些更新的操作符

$set: 在原基础上累加

upsert: 可选,如果不存在符合条件的记录时是否插入updateObj。默认是false,不插入

multi:可选,mongodb默认只更新找到的第一条记录,如果这个参数为true,就更新所有符合条件的记录。

db.demo.insert({name:'zhangsan'})

db.demo.update({name:'zhangsan'},{age:12})

upsert

将students集合的数据中name是lisi的值改为ww,原本数据库中没该数据,也不会插入成功,但是设置upsert为true后就能插入成功

db.demo.update({name:'lisl'}, {name:'ww'}, {upsert:true})

multi

如果有多条age是1的数据,只更新一条,如果想全部更新,需要指定{multi:true}参数

db.school.insert({age:1})
db.school.insert({age:1})
db.school.insert({age:1})
db.school.update({age:1}, {$set:{name:'lisi'}}, {multi:true})
db.school.find()

更新操作符

直接指定更新后的值

use c3;
db.c3.insert({name:'a'});
db.c3.update({name:'a'},{$set:{age:10}});
db.c3.find({})

在原基础上累加

db.c3.update({name:'a'},{$inc:{age:1}})

$unset

删除指定的键

db.c3.update({name:'2'}, {$unset:{age:11}}) 删除name为'2'的文档中的age为11的数据

$push

数组中添加元素

db.c3.insert({name:'2', age:10})
db.c3.update({name:'2'}, {$push:{hobbys:'drink'}}, {multi: true})

向name为2的文档中的数组hobbys中添加drink

$ne类似于MYSQL的not in 或者 not exists

db.c3.update({hobbys:{$ne:"eating"}}, {$push:{"hobbys":"sleeping"}}, {multi:true})

将c3集合中没有hobbys属性,或者hobbys文档中hobbys中没有sleeping的集合中添加hobbys数组或者追加数据到hobbys数组

$addToSet

向集合中添加数组元素(一条)

db.c3.update({name:'a'}, {$addToSet:{"addr":"hb"}})

$each

把数组中的元素逐个添加到集合中(可以添加多条)

db.c3.update({name:'a'}, {$addToSet:{"addr":{$each:["wh", "sh"]}}})

数组中移除指定的索引中对应的元素

  • ${pop:{key:1}} 从数组末尾删除一个元素
  • ${pop:{key:-1}} 从数组开头删除一个元素
  • db.c3.update({name:'a'}, {$pop:{addr:1}})

    修改指定索引的元素

    修改c3中第一个name为a的集合中add数组文档

    db.c3.update({name:'a'}, {$set:{"addr.1":"zh"}})

    remove方法是用来移除集合中的数据

    db.collection.remove(
       <query>,
         justOne: <boolean>
    

    db.c3.insert([{name:1,age:2},{name:1,age:3},{name:1,age:4}])

    db.c3.find({}, {_id:0,age:1})

    findOne

    查询匹配结果的第一条数据 语法

    db.collection_name.findOne()

    db.c3.findOne()

    查询字段在某个范围内

    查询age为3或4的文档中的name和age
    db.c3.find({age:{$in:[3,4]}}, {name:1, age:1})
    

    查询字段不在某个范围内

    db.c3.find({age:{$nin:[3,4]}}, {name:1, age:1})
    

    对特定条件取反

    db.c3.find({age:{$not:{$gte:3, $lte:4}}})

    释:查找age不在3到4之间的

    array

    where

    db.c3.find({$where:"this.age>2"}, {name:1,age:1})

    注意:$where后面的条件需要引号

    cursor

    游标不是 查询结果,而是查询的一个返回资源 或接口,通过这个接口,可以逐条读取数据

    var result = db.student.find();
    while(result.hasNext()){
        printjson(result.next());
    

    条件操作符

    条件操作符用于比较两个表达式并从mongoDB集合中获取数据

    大于、大于等于、小于、小于等于

    db.collection_name.find({"_id": ObjectId("value")})

    db.c3.find({_id:ObjectId("5d99738a82b8ca8cde36ac7e")})
    

    查询结果集的条数

    db.collecton_name.find().count()

    db.c3.find().count()
    

    db.collection_name.find({key:/value/})

    查询age是2并且name是zs或者name是lisi的数据

    db.c3.insert([{age:25,name:"zs"},{age:25,name:"lisi"},{age:25,name:"wangwu"}])
    db.c3.find({age:25,$or:[{name:"zs"},{name:"lisi"}]})
    

    limit

    读取指定数量的数据记录 语法

    db.collection_name.find().limit(number)

    var a = [];
    for (var i = 0; i < 10; i++) {
    	a.push({age:i})
    db.c3.insert(a);
    db.c3.find().limit(3)
    

    跳过指定数量的数据,skip方法同样接收一个数字参数作为跳过记录的条数 语法

    db.collection_name.find().skip(number)

    db.c3.find().skip(2)
    

    skip+limit实现分页

    通常用这种方式来实现分页功能 语法

    db.collection_name.find().skip(skipNum).limit(limitNum)

    db.c3.find().skip(2).limit(2)
    

    sort排序

    sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1表示为升序,-1表示降序。语法

    db.colleciton_name.find().sort({key:1})

    db.collection_name.find().sort({key:-1})

    db.c3.find().sort({age:1})
    

    group

    group 的作用是按指定的键对集合中的文档进行分组,并执行简单的聚合函数,它与 SQL 中的 SELECT ... GROUP BY 类似。其语法格式如下:

    group: ns: <namespace>, key: <key>, $reduce: <reduce function>, $keyf: <key function>, cond: <query>, finalize: <finalize function>

    group 支持的指令及对应描述如下:

    db.sales.insertMany([
    {_id: 1, orderDate: ISODate("2012-07-01T04:00:00Z"), shipDate: ISODate("2012-07-02T09:00:00Z"), attr: {name: "新款椰子鞋", price: 2999, size: 42, color: "香槟金"}},
    {_id: 2, orderDate: ISODate("2012-07-03T05:20:00Z"), shipDate: ISODate("2012-07-04T09:00:00Z"), attr: {name: "高邦篮球鞋", price: 1999, size: 43, color: "狮王棕"}},
    {_id: 3, orderDate: ISODate("2012-07-03T05:20:10Z"), shipDate: ISODate("2012-07-04T09:00:00Z"), attr: {name: "新款椰子鞋", price: 2999, size: 42, color: "香槟金"}},
    {_id: 4, orderDate: ISODate("2012-07-05T15:11:33Z"), shipDate: ISODate("2012-07-06T09:00:00Z"), attr: {name: "极速跑鞋", price: 500, size: 43, color: "西湖蓝"}},
    {_id: 5, orderDate: ISODate("2012-07-05T20:22:09Z"), shipDate: ISODate("2012-07-06T09:00:00Z"), attr: {name: "新款椰子鞋", price: 2999, size: 42, color: "香槟金"}},
    {_id: 6, orderDate: ISODate("2012-07-05T22:35:20Z"), shipDate: ISODate("2012-07-06T09:00:00Z"), attr: {name: "透气网跑", price: 399, size: 38, color: "玫瑰红"}}
    

    假设要将集合 sales 中的文档按照 attr.name 进行分组,并限定参与分组的文档的 shipDate 大于指定时间。对应示例如下:

    # select name from sales where shipDate > '2012-07-04T09:00:00Z' group by name
    db.runCommand({
        group:{
          # from操作
          ns: 'sales',
          # group by操作
          key: {"attr.name": 1},
          # where操作
          cond: {shipDate: {$gt: ISODate('2012-07-04T09:00:00Z')}},
          # 这个处理类似having的操作; curr:是当前遍历的文档,result聚合的结果文档
          $reduce: function(curr, result){
          initial: {}
    命令执行后,会返回一个结果档。其中, retval 包含指定字段 attr.name 的数据,count 为参与分组的文档数量,keys 代表组的数量,ok 代表文档状态
    # select shipDate, count(price) total from sales where shipDate > '2012-07-04T00:00:00Z' group by shipDate
    db.runCommand({
        group:{
            ns: 'sales',
            key: {shipDate: 1},
            cond: {shipDate: {$gt: ISODate('2012-07-04T00:00:00Z')}},
            initial: {total: 0},
            $reduce: function(curr, result){
            	result.total += curr.attr.price;
    # select shipDate, count, total, avg(price) avg from sales where shipDate > '2012-07-04T00:00:00Z' group by shipDate
    db.runCommand(
        group:{
            ns: 'sales',
            key: {shipDate: 1},
            cond: {shipDate: {$gt: ISODate('2012-07-04T00:00:00Z')}},
            $reduce: function(curr, result){
            	result.total += curr.attr.price;
            	result.count ++;
            initial: {total: 0, count: 0},
            finalize: function(result) {
            	result.avg = Math.round(result.total / result.count);
    上面的示例中改动了 $reduce 函数,目的是为了统计 count。然后新增了 finalize,目的是计算分组中的平均销售额
    

    aggregate聚集框架

    管道操作符

    管道的概念:

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

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

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

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

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

    计算总和。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) 计算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}]) 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}]) $push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}]) $addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}]) $first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}]) $last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}]) var arr = [ {_id:1, name:'zs',subject:'语文',score:90,cid:1}, {_id:2, name:'zs',subject:'数学',score:95,cid:1}, {_id:3, name:'zs',subject:'英语',score:95,cid:1}, {_id:4, name:'ls',subject:'语文',score:80,cid:1}, {_id:5, name:'ls',subject:'数学',score:85,cid:1}, {_id:6, name:'ls',subject:'英语',score:90,cid:1}, {_id:7, name:'ww',subject:'语文',score:70,cid:2}, {_id:8, name:'ww',subject:'数学',score:75,cid:2}, {_id:9, name:'ww',subject:'英语',score:55,cid:2} db.student.save(arr) db.createCollection("class") db.class.save([{_id:1, cid: 1, name:"3年1班"}, {_id:2, cid: 2, name:"3年2班"}])
    # 查询name为ls的
    db.student.aggregate([
    	{$match: {name:'ls'}}
    # 查询数学成绩大于等于80,小于90的人
    db.student.aggregate([
    	{$match: {subject : "数学", score: {$gte: 80, $lt: 90}}}
    # 根据name分组求每个人的选课数
    db.student.aggregate([
    	{$group:{_id: "$name", snumber: {$sum:1}}}
    #_id: 指定分组的字段
    # "$name": 引用集合中的文档"name", 需要用$符号
    # snumber是{$sum:1}计算出来的值的别名
    # {$sum:1}这里是1, 和mysql中count(1)一个意思
    # 求每个人的总分
    db.student.aggregate([
    	{$group:{_id: "$name", snumber: {$sum:"$score"}}}
    # $sum:"$score" 在这里的意思是sum求和
    # 成绩大于等于80的科目求总分,同时总分要大于240分
    db.student.aggregate([
    	{$match: {score: {$gte: 80}}},
    	{$group:{_id: "$name", snumber: {$sum: "$score"}}},
    	{$match: {snumber: {$gte:240}}}
    # $match在group上面前面类似于mysql的where
    # $match 在 group后面,类似于mysql的having
    # 按名字分组求平均
    db.student.aggregate([
    	{$group:{_id: "$name", snumber: {$avg : "$score"}}}
    

    聚合管道操作符

    looup

    $lookup 的作用是对同一数据库中的集合执行左外连接,其语法格式如下:

    $lookup: from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, as: <output array field>
    # select c.*, studentArr from class c inner join student s where c.cid = s.cid
    # 不同的是这个studentArr是以数组的形式作为class的字段出现
    db.class.aggregate([
    		$lookup: {
    			from: "student",
    			localField: "cid", #class中的字段
    			foreignField: "cid", #student中的字段
    			as: "studentArr"
    ]).pretty()
    # 结果部分截取
    	"_id" : 1,
    	"cid" : 1,
    	"name" : "3年1班",
    	"studentArr" : [
    			"_id" : 1,
    			"name" : "zs",
    			"subject" : "语文",
    			"score" : 90,
    			"cid" : 1
    			"_id" : 2,
    			"name" : "zs",
    			"subject" : "数学",
    			"score" : 95,
    			"cid" : 1
    
    unwind

    unwind 能将包含数组的文档拆分称多个文档,其语法格式如下:

    $unwind: path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean>
    db.shoes.save({_id: 1, brand: "Nick", sizes: [37, 38, 39]})
    > db.shoes.aggregate([{$unwind : "$sizes"}])
    { "_id" : 1, "brand" : "Nick", "sizes" : 37 }
    { "_id" : 1, "brand" : "Nick", "sizes" : 38 }
    { "_id" : 1, "brand" : "Nick", "sizes" : 39 }
    

    显然,这样的文档更方便我们做数据处理。preserveNullAndEmptyArrays 指令默认为 false,也就是说文档中指定的 path 为空、null 或缺少该 path 的时候,会忽略掉该文档。假设数据如下:

    > db.shoes2.insertMany([
    	{"_id": 1, "item": "ABC", "sizes": ["S", "M", "L"]},
    	{"_id": 2, "item": "EFG", "sizes": [ ]},
    	{"_id": 3, "item": "IJK", "sizes": "M"},
    	{"_id": 4, "item": "LMN" },
    	{"_id": 5, "item": "XYZ", "sizes": null}
    > db.shoes2.aggregate([{$unwind: "$sizes"}])
    { "_id" : 1, "item" : "ABC", "sizes" : "S" }
    { "_id" : 1, "item" : "ABC", "sizes" : "M" }
    { "_id" : 1, "item" : "ABC", "sizes" : "L" }
    { "_id" : 3, "item" : "IJK", "sizes" : "M" }
    # 已经忽略了 sizes == null, sizes.leng <= 0, size == undefind的数据
    

    _id245 的文档由于满足 preserveNullAndEmptyArrays 的条件,所以不会被拆分。

    以上就是 unwind 的基本用法和作用介绍,更多与 unwind 相关的知识可查阅官方文档 unwind

    out 的作用是聚合 Pipeline 返回的结果文档,并将其写入指定的集合。要注意的是,out 操作必须出现在 Pipeline 的最后。out 语法格式如下

    { $out: "" }

    > db.student.aggregate([
    	{$group:{_id: "$name", snumber: {$avg : "$score"}}},
    	{ $out : "avg_result" }
    > show tables
    avg_result
    # 发现生成了一个集合 avg_result
    

    Map-Reduce

    Map-reduce 用于将大量数据压缩为有用的聚合结果,其语法格式如下:

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

    其中,db.runCommand({mapReduce: }) 也可以写成 db.collection.mapReduce()。各指令的对应描述如下:

    一个简单的 mapReduce 语法示例如下:

    var mapFunction = function() { ... };
    var reduceFunction = function(key, values) { ... };
    db.runCommand(
     mapReduce: <input-collection>,
     map: mapFunction,
     reduce: reduceFunction,
     out: { merge: <output-collection> },
     query: <query>
    

    map 函数负责将每个输入的文档转换为零个或多个文档。map 结构如下:

    function() {
       emit(key, value);
    

    emit 函数的作用是分组,它接收两个参数:

  • key: 指定用于分组的字段
  • value : 要聚合的字段
  • map 中可以使用 this 关键字引用当前文档。

    reduce 结构如下:

    function(key, values) {
       return result;
    

    reduce 执行具体的数据处理操作,它接收两个参数:

  • key:与 map 中的 key 相同,即分组字段。
  • values:根据分组字段,将相同 key 的值放到同一个数组,values 就是包含这些分类数组的对象。
  • out 用于指定结果输出,out: 会将结果输出到新的集合,或者使用以下语法将结果输出到已存在的集合中:

    out: { <action>: <collectionName>
            [, db: <dbName>]
            [, sharded: <boolean> ]
            [, nonAtomic: <boolean> ] }
    

    要注意的是,如果 out 指定的 collection 已存在,那么它就会覆盖该集合。在开始学习之前,我们需要准备以下数据:

    db.mprds.insertMany([
        {_id: 1, numb: 3, score: 9, team: "B"},
        {_id: 2, numb: 6, score: 9, team: "A"},
        {_id: 3, numb: 24, score: 9, team: "A"},
        {_id: 4, numb: 6, score: 8, team: "A"}
    # emit 指定按numb分组,聚合字段为score
    var func_map = function() {
    	emit(this.team, this.score);
    var func_reduce = function(key, values) {
    	return Array.sum(values);
    # 按numb分组查询 team="A" 每个人的总分
    db.student.mapReduce(func_map, func_reduce, {query: {team: "A"}, out: "mprds_result"})
    

    接着定义 map 函数、reduce 函数,并将其应用到集合 mrexample 上。然后为输出结果指定存放位置,这里将输出结果存放在名为 mrexample_result 的集合中。

    finallize(having) 剪枝

    finallize 用于修改 reduce 的输出结果,其语法格式如下:

    function(key, reducedValue) {
       return modifiedObject;
    

    它接收两个参数 :

    key,与 map 中的 key 相同,即分组字段。

    reduceValue, 一个Ojbect,是reduce的输出

    上面我们介绍了 mapreduce,并通过一个简单的示例了解 mapReduce 的基本组成和用法。实际上我们还可以编写功能更丰富的 reduce 函数,甚至使用 finallize 修改 reduce 的输出结果。以下 reduce 函数将传入的 values 进行计算和重组,返回一个 reduceVal 对象:

    var func_reduce2 = function(key, values){
    	reduceVal = {team: key, score: values, total: Array.sum(values), count: values.length};
    	return reduceVal;
    

    reduceVal 对象中包含 teamscoretotalcount 四个属性。但我们还想为其添加 avg属性,那么可以在 finallize 函数中执行 avg 值的计算和 avg 属性的添加工作:

    var func_finalize = function(key, values){
    	values.avg = values.total / values.count;
    	return values;
    

    map 保持不变,将这几个函数作用于集合 mprds 上,对应示例如下:

    # emit 指定按numb分组,聚合字段为score
    var func_map = function() {
    	emit(this.team, this.score);
    var func_reduce2 = function(key, values){
    	reduceVal = {team: key, score: values, total: Array.sum(values), count: values.length};
    	return reduceVal;
    # 这个key和上面是同一个(分组的字段), values是func_reduce2函数的返回结果
    var func_finalize = function(key, values) {
    	values.avg = values.total / values.count;
    	return values;
    db.mprds.mapReduce(func_map, func_reduce2, {query: {team: "A"}, out: "mprds_result", finalize: func_finalize})
    > db.mprds_result.find()
    { "_id" : "A", "value" : { "team" : "A", "score" : [ 9, 9, 8 ], "total" : 26, "count" : 3, "avg" : 8.666666666666666 } }
    

    finallize 在 后面使用,微调 的处理结果。这着看起来像是一个园丁在修剪花圃的枝丫,所以人们将 形象地称为“剪枝”。

    要注意的是:map 会将 key 值相同的文档中的 value 归纳到同一个对象中,这个对象会经过 reducefinallize。对于 key 值唯一的那些文档,指定的 keyvalue 会被直接输出。

    简单的聚合

    count

    count 用于计算集合或视图中的文档数,返回一个包含计数结果和状态的文档。其语法格式如下

    count: <collection or view>, query: <document>, limit: <integer>, skip: <integer>, hint: <hint>, readConcern: <document>

    count 支持的指令及对应描述如下:

    假设要统计集合 mprdsnumb6 的文档数量,对应示例如下:

    > db.runCommand({count: 'mprds', query: {numb: {$eq: 6}}})
    { "n" : 2, "ok" : 1 }
    

    指定返回结果之前跳过 1 个文档,对应示例如下:

    > db.runCommand({count: 'mprds', query: {numb: {$eq: 6}}, skip: 1})
    { "n" : 1, "ok" : 1 }
    

    更多关于 count 的知识可查阅官方文档 Count

    distinct

    distinct 的作用是查找单个集合中指定字段的不同值,其语法格式如下:

    distinct: "<collection>", key: "<field>", query: <query>, readConcern: <read concern document>, collation: <collation document>
    db.createCollection("dress")1
    db.dress.insertMany([
    	{_id: 1, "dept": "A", attr: {"款式": "立领", color: "red" }, sizes: ["S", "M" ]},
    	{_id: 2, "dept": "A", attr: {"款式": "圆领", color: "blue" }, sizes: ["M", "L" ]},
    	{_id: 3, "dept": "B", attr: {"款式": "圆领", color: "blue" }, sizes: "S" },
    	{_id: 4, "dept": "A", attr: {"款式": "V领", color: "black" }, sizes: ["S" ] }
    # 计集合 dress 中所有文档的 dept 字段的不同值
    # select distinct dept from dress
    db.runCommand ( { distinct: "dress", key: "dept" } )
    { "values" : [ "A", "B" ], "ok" : 1 }
    # select distinct attr.款式 from dress
    db.runCommand ( { distinct: "dress", key: "attr.款式" } )
    { "values" : [ "立领", "圆领", "V领" ], "ok" : 1 }
    #就算值是数组, distinct 也能作出正确处理
    db.runCommand ( { distinct: "dress", key: "sizes" } )
    { "values" : [ "M", "S", "L" ], "ok" : 1 }
    

    mongodb速成(1)

    聚合操作参考