mongodb 高级操作:聚合,游标

来源:互联网 发布:北京java培训班学费 编辑:程序博客网 时间:2024/06/06 08:38
mongodb 高级操作:聚合,游标

一: 聚合有count,distinct,group,mapReduce。

1)count()函数:
Wed Jan 08 14:39:46.008 ReferenceError: clear is not defined
> db.person.count()
7
>

2)distinct():指定谁,谁就不能重复。
> db.person.distinct("age")
[ 23, 33, 43, 53 ]
> db.person.find(distinct("age"))
Wed Jan 08 14:43:55.057 ReferenceError: distinct is not defined
>
但是好像不能嵌套使用,哈哈哈。

3)group:group操作本质上形成了一种“k-v”模型,就像C#中的Dictionary,好,有了这种思维,

我们来看看如何使用group。下面举的例子就是按照age进行group操作,value为对应age的姓名。下面对这些参数介绍一下:
  key:这个就是分组的key,我们这里是对年龄分组。
  initial:每组都分享一个”初始化函数“,特别注意:是每一组,比如这个的age=20的value的list分享一个
  initial函数,age=22同样也分享一个initial函数。
  $reduce: 这个函数的第一个参数是当前的文档对象,第二个参数是上一次function操作的累计对象,第一次
  为initial中的{”perosn“:[]}。有多少个文档, $reduce就会调用多少次。

> db.person.group({"key":{"age":true},"initial":{"person":[]},"$reduce":function(cur,prev){prev.person.push(cur.name)}})

[
        {
                "age" : 23,
                "person" : [
                        "a",
                        "a",
                        "b",
                        "c"
                ]
        },
        {
                "age" : 33,
                "person" : [
                        "c"
                ]
        },
        {
                "age" : 43,
                "person" : [
                        "d"
                ]
        },
        {
                "age" : 53,
                "person" : [
                        "e"
                ]
        }
]
>


①:想过滤掉age>25一些人员。

②:有时person数组里面的人员太多,我想加上一个count属性标明一下。

 针对上面的需求,在group里面还是很好办到的,因为group有这么两个可选参数: condition 和 finalize。

     condition:  这个就是过滤条件。

     finalize:这是个函数,每一组文档执行完后,多会触发此方法,那么在每组集合里面加上count也就是它的活了。

> db.person.group({
... "key":{"age":true},
... "inital":{"person":[]},
... "reduce":function(doc,out){
... out.person.push(doc.name);
... },
... "finalize":function(out){
... out.count=out.person.length;
... },
... "condition":{"age":{$lt:33}}
... })
Wed Jan 08 16:11:28.466 group command failed: { "ok" : 0, "errmsg" : "initial has to be an object" } at src/mongo/shell/
db.js:655
> db.person.group({ "key":{"age":true}, "initial":{"person":[]}, "reduce":function(doc,out){ out.person.push(doc.name);
}, "finalize":function(out){ out.count=out.person.length; }, "condition":{"age":{$lt:33}} })
[
        {
                "age" : 23,
                "person" : [
                        "a",
                        "a",
                        "b",
                        "c"
                ],
                "count" : 4
        }
]
>

4)mapreduce:mapReduce其实是一种编程模型,用在分布式计算中,其中有一个“map”函数,一个”reduce“函数。

   ① map:

          这个称为映射函数,里面会调用emit(key,value),集合会按照你指定的key进行映射分组。

   ② reduce:

         这个称为简化函数,会对map分组后的数据进行分组简化,注意:在reduce(key,value)中的key就是

      emit中的key,vlaue为emit分组后的emit(value)的集合,这里也就是很多{"count":1}的数组。

   ③ mapReduce:

          这个就是最后执行的函数了,参数为map,reduce和一些可选参数。


> map
Wed Jan 08 16:16:31.777 ReferenceError: map is not defined
> map
Wed Jan 08 16:16:43.399 ReferenceError: map is not defined
> map function(){
... emit(this.name,{count:1});
... }
Wed Jan 08 16:17:21.822 SyntaxError: Unexpected token function
> reduce
Wed Jan 08 16:17:38.873 ReferenceError: reduce is not defined
> db.person.mapReduce(map,reduce,{"out":"collection"})
Wed Jan 08 16:18:18.305 ReferenceError: map is not defined
> map
Wed Jan 08 16:19:25.495 ReferenceError: map is not defined
> map
Wed Jan 08 16:19:29.208 ReferenceError: map is not defined
> map .
... function(){
... emit(this.name,{count:1});
... }
Wed Jan 08 16:19:58.489 SyntaxError: Unexpected token {
> db.person.find()
{ "_id" : ObjectId("52ccf24c9a8355185b2af3df"), "name" : "a", "age" : 23, "gender" : "man" }
{ "_id" : ObjectId("52ccf2639a8355185b2af3e0"), "name" : "a", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf2769a8355185b2af3e1"), "name" : "b", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf27e9a8355185b2af3e2"), "name" : "c", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf2879a8355185b2af3e3"), "name" : "c", "age" : 33, "gender" : "women" }
{ "_id" : ObjectId("52ccf2949a8355185b2af3e4"), "name" : "d", "age" : 43, "gender" : "women" }
{ "_id" : ObjectId("52ccf29e9a8355185b2af3e5"), "name" : "e", "age" : 53, "gender" : "women" }
> map
Wed Jan 08 16:20:18.784 ReferenceError: map is not defined
>

一直报错,不知道为什么?


二>游标:
  var list=db.person.find();

    针对这样的操作,list其实并没有获取到person中的文档,而是申明一个“查询结构”,等我们需要的时候通过

for或者next()一次性加载过来,然后让游标逐行读取,当我们枚举完了之后,游标销毁,之后我们在通过list获取时,

发现没有数据返回了。

> list.forEach(function(x){)
... print(x.name);
... }
Wed Jan 08 16:29:19.707 SyntaxError: Unexpected token )
> var list=db.person.find();
> list.forEach(function(x){
... print(x.name);
... })
a
a
b
c
c
d
e
> list
> var list=db.person.find()
> list
{ "_id" : ObjectId("52ccf24c9a8355185b2af3df"), "name" : "a", "age" : 23, "gender" : "man" }
{ "_id" : ObjectId("52ccf2639a8355185b2af3e0"), "name" : "a", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf2769a8355185b2af3e1"), "name" : "b", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf27e9a8355185b2af3e2"), "name" : "c", "age" : 23, "gender" : "women" }
{ "_id" : ObjectId("52ccf2879a8355185b2af3e3"), "name" : "c", "age" : 33, "gender" : "women" }
{ "_id" : ObjectId("52ccf2949a8355185b2af3e4"), "name" : "d", "age" : 43, "gender" : "women" }
{ "_id" : ObjectId("52ccf29e9a8355185b2af3e5"), "name" : "e", "age" : 53, "gender" : "women" }
>


三>索引:

首先插入10w数据:

> db.person.remove();
> db.person.count();
0
> for(var i=0;i<100000;i++){
... var rand=parseInt(i.Math.random());
... db.person.insert({"name":"hxc","age":i})
... }
Wed Jan 08 16:38:40.489 TypeError: Cannot call method 'random' of undefined
> for(var i=0;i<100000;i++){
... var rand=parseInt(i*Math.random());
... db.person.insert({"name":"hxc","age":i})
... }




一:性能分析函数(explain)

好了,数据已经插入成功,既然我们要做分析,肯定要有分析的工具,幸好mongodb中给我们提供了一个关键字叫做“explain",那么怎么用呢?

还是看图,注意,这里的name字段没有建立任何索引,这里我就查询一个“name10000”的姓名。

> db.person.find({"name":"hxc"+10000}).explain()
{
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 100000,
        "nscanned" : 100000,
        "nscannedObjectsAllPlans" : 100000,
        "nscannedAllPlans" : 100000,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 58,
        "indexBounds" : {

        },
        "server" : "penghl:27017"
}
>


 仔细看红色区域,有几个我们关心的key。

   cursor:       这里出现的是”BasicCursor",什么意思呢,就是说这里的查找采用的是“表扫描”,也就是顺序查找,很悲催啊。

   nscanned:  这里是10w,也就是说数据库浏览了10w个文档,很恐怖吧,这样玩的话让人受不了啊。

   n:             这里是1,也就是最终返回了1个文档。

   millis:        这个就是我们最最最....关心的东西,总共耗时114毫秒。



二:建立索引(ensureIndex)

> db.person.ensureIndex({"name":1})
> db.person.find({"name":"hxc"+10000}).explain()
{
        "cursor" : "BtreeCursor name_1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 0,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 6,
        "indexBounds" : {
                "name" : [
                        [
                                "hxc10000",
                                "hxc10000"
                        ]
                ]
        },
        "server" : "penghl:27017"
}
可以看到,建立索引之后的查询只需要6毫秒。

这里我们使用了ensureIndex在name上建立了索引。”1“:表示按照name进行升序,”-1“:表示按照name进行降序。

看看这些敏感信息。

   cursor:       这里出现的是”BtreeCursor",这么牛X,mongodb采用B树的结构来存放索引,索引名为后面的“name_1"。

   nscanned:  我擦,数据库只浏览了一个文档就OK了。

   n:             直接定位返回。

   millis:        


三:唯一索引:

建立唯一索引,重复的键值自然就不能插入,在mongodb中的使用方法是:

> db.person.remove()
> db.person.ensureIndex({"name":1},{"unique":true})
> db.person.insert({"name":"hxc","age":20})
> db.person.insert({"name":"hxc","age":20})
> db.person.remove()
> db.person.ensureIndex({"name":1},{"unique":true})
> db.person.insert({"name":"hxcc","age":20})
> db.person.insert({"name":"hxcc","age":20})
> db.person.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.person",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "name" : 1
                },
                "ns" : "test.person",
                "name" : "name_1"
        }
]

> db.person.dropIndexes("name_1")
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}
> db.person.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.person",
                "name" : "_id_"
        }
]
>
好像建不了唯一索引?


四:组合索引
有时候我们的查询不是单条件的,可能是多条件,比如查找出生在‘1989-3-2’名字叫‘jack’的同学,那么我们可以建立“姓名”和"生日“
的联合索引来加速查询。
> db.person.remove()
> db.person.count()
0
> db.person.insert({"name":"hxc","birthday":"1989-2-2"})
> db.person.insert({"name":"jack","birthday":"1989-3-2"})
> db.person.insert({"name":"joe","birthday":"1989-2-22"})
> db.person.insert({"name":"mary","birthday":"1989-3-12"})
> db.person.insert({"name":"jr","birthday":"1989-3-2"})
> db.person.ensureIndex({"name":1,"birthday":1})
> db.person.ensureIndex({"birthday":1,"name":1})
> db.person.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.person",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "name" : 1,
                        "birthday" : 1
                },
                "ns" : "test.person",
                "name" : "name_1_birthday_1"
        },
        {
                "v" : 1,
                "key" : {
                        "birthday" : 1,
                        "name" : 1
                },
                "ns" : "test.person",
                "name" : "birthday_1_name_1"
        }
]
>

到底查询优化器会使用哪个查询作为操作:
> db.person.find({"name":"hxc","birthday":"1989-2-2"}).explain()
{
        "cursor" : "BtreeCursor name_1_birthday_1",
        "isMultiKey" : false,
        "n" : 1,
        "nscannedObjects" : 1,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 1,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 17,
        "indexBounds" : {
                "name" : [
                        [
                                "hxc",
                                "hxc"
                        ]
                ],
                "birthday" : [
                        [
                                "1989-2-2",
                                "1989-2-2"
                        ]
                ]
        },
        "server" : "penghl:27017"
}
> db.person.find({"birthday":"1989-2-2","name":"jack"}).explain()
{
        "cursor" : "BtreeCursor name_1_birthday_1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 0,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "name" : [
                        [
                                "jack",
                                "jack"
                        ]
                ],
                "birthday" : [
                        [
                                "1989-2-2",
                                "1989-2-2"
                        ]
                ]
        },
        "server" : "penghl:27017"
}
>

我们要相信查询优化器,它给我们做出的选择往往是最优的,因为我们做查询时,查询优化器会使用我们建立的这些索引来创建查询方案,

如果某一个先执行完则其他查询方案被close掉,这种方案会被mongodb保存起来,当然如果非要用自己指定的查询方案,这也是

可以的,在mongodb中给我们提供了hint方法让我们可以暴力执行。

> db.person.find({"birthday":"1989-2-2","name":"jack"}).hint("birthday":1,"name":1).explain()
Wed Jan 08 17:44:28.466 SyntaxError: Unexpected token :
> db.person.find({"birthday":"1989-2-2","name":"jack"}).hint({"birthday":1,"name":1}).explain()
{
        "cursor" : "BtreeCursor birthday_1_name_1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 0,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "birthday" : [
                        [
                                "1989-2-2",
                                "1989-2-2"
                        ]
                ],
                "name" : [
                        [
                                "jack",
                                "jack"
                        ]
                ]
        },
        "server" : "penghl:27017"
}
>

五>删除索引:
可能随着业务需求的变化,原先建立的索引可能没有存在的必要了,可能有的人想说没必要就没必要呗,但是请记住,索引会降低CUD这三

种操作的性能,因为这玩意需要实时维护,所以啥问题都要综合考虑一下,这里就把刚才建立的索引清空掉来演示一下:dropIndexes的使用。
> db.person.dropIndexes()
{
        "nIndexesWas" : 3,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}
> db.person.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.person",
                "name" : "_id_"
        }
]
>








































































0 0
原创粉丝点击