mongdb定位是介于关系型数据库与非关系型数据库之间
相对于一般nosql数据库,mongodb拥有强大的查询能力
语法设计上是参照了sql的语法

查询集合所有文档
db.account.find()

ps:db.account.find不加括号时,返回find命令的源码 


 简单条件查询 

 db.account.find({"userName":"bbs10"}) 


 多值匹配条件查询,类似“条件1 and 条件2” 

 db.account.find({"userName":"bbs10","passwd":"ddd"}) 


 指定返回某些键 

 1表示返回该属性,0表示不返回该属性 

 db.account.find({},{"_id":1,"userName":1}) 

 db.account.find({},{"passwd":0}) 


 只返回userName,不返回_id(默认会返回_id) 

 db.account.find({},{"userName":1,"_id":0}) 


 复合条件查询 

 比较操作符 

 lt,lte,gt,gte,ne 

  <, <=, >, >=,!= 


 先定义一个start 

 start=new Date() 

 查询大于start的文档 

 db.account.find({"createTime":{"$gt":start}}) 


 组合使用查询年龄大于30小于40 

 db.account.find({"age":{"$gt":30,"$lt":40}}) 


 年龄不等于30的文档 

 db.account.find({"age":{"$ne":30}}) 


 $in查询,类似于sql中的in 

 db.account.find({"age":{$in:[30,32]}}) 


 $nin查询,类似于 not in 

 db.account.find({"age":{$nin:[30,32]}}) 


 $or  或查询,可多键查询 

 查询userName为bbs10或者age为32的文档 

 db.account.find({"$or":[{"userName":"bbs10"},{"age":32}]}) 


 组合起来用 

 db.account.find({"$or":[{"userName":"bbs10"},{"age":{"$in":[30,32]}}]}) 


 $not运算符,取非,可以用于任何查询条件上 

 db.account.find({"age":{"$not":{"$nin":[30,32]}}}) 


 $mod取模运算,查询用5去除余数为0的文档 

 db.account.find({"age":{"$mod":[5,0]}}) 


 高级查询规则 

 null不仅匹配自身,还匹配不存在 

 该语句查出了为null值的文档和不包含createTime字段的文档 

 db.account.find({"createTime":null}) 


 要结合$exist才能准确查出属性为null的文档 

 db.account.find({"createTime":{"$in":[null],"$exists":true}}) 

 写程序时null值处理要谨慎,否则容易出bug 


 正则表达式 

 db.account.find({"userName":/bbs10/i}) 

 带前缀正则表达式查询性能最好 

 db.account.find({"userName":/^bbs10/i}) 


 查询数组 

 先插入数组 

 db.food.insert("_id":1,["apple","banana","peach"]) 

 db.food.insert("_id":2,["apple","watermelon","orange"]) 

 db.food.insert("_id":3,["cherry","banana","apple"]) 


 单元素匹配 

 数组里任何一个匹配,则算条件符合 

 db.food.find("fruit":"apple") 

 可以查出全部123 


 多元素匹配,既有apple又有banana 

 $all 

 db.food.find("fruit":{$all:["apple","banana"]}) 

 查到1,3 


 数组下标从0开始,用数组下标指定位置查询 

 查询下标为2的值是apple的文档 

 db.food.find({"fruit.2":"apple"}) 

 查到3 


 $size查询指定数组长度的数组 

 db.food.find({"fruit":{"$size":3}}) 


 查询内嵌文档 

 先插入一些数据 

 db.account.insert("userName":{"first":"joe","last":"schmoe"},"age":35) 

 db.account.insert("userName":{"first":"tom","last":"schmoe"},"age":35) 


 完全匹配查询 

 db.account.find("userName":{"first":"joe","last":"schmoe"}) 


 用点表示法改变文档 

 db.account.update({"$set":{"userName.passwd":"ddd"}}) 


 点表示法查询,不受文档数据模式改变影响 

 db.account.find({"userName.first":"joe","userName.last":"schmoe"}) 


 增加数组元素 

 db.account.update({},{"$push":{"comments":{"author":"joe","score":3,"comment":"test"}}}) 

 db.account.update({},{"$push":{"comments":{"author":"tom","score":5,"comment":"test"}}}) 

 查询作者为joe并且得分超过5分的文档,可惜下面这条查出来的是作者是joe或者得分为5,即单个匹配 

 db.account.find({"comments.author":"joe","comments.score":{"$gte":5}}) 

 正确做法,采用$elemMatch对内嵌文档多键匹配 

 db.account.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}}) 


 where查询,不同于sql中的where 

 查出存在键值相等的文档(比如{"_id":"1","apple":4,"banana":4}) 

 db.food.find({"$where":function(){ 

     for(var current in this){ 

         for(var other in this) { 

             if (current != other && this[current]==this[other]){ 

                 return true; 

             } 

         } 

     } 

     return false; 

 }}) 

 ps:上面function是javascript语法。 


 尽量使用键/值对查询方式,不是万不得已逼急了请不要使用where查询。 

 where查询无法使用索引,并且文档要从bson转成javascript对象,所以查询速度非常慢。 


 游标操作 

 先造数据,插入100个文档 

 for(i=0;i<100;i++){db.c.insert(x:i);} 


 定义游标 

 var cursor=db.c.find() 

 此时find不会马上执行 

 游标迭代器: 

 while(cursor.hasNext()) { 

     obj=curosr.next() 

 } 

 此处和jdbc很相似,游标可以理解为一个指针,客户端执行cursor.hasNext()时,查询发往服务器,执行真正查询,shell会获取前100个结果或者4M数据(两者较小者)返回。 


 java代码 

 /** 

  * 批量查询账号 

  */ 

 public Account[] getAccounts(String[] nameArray) { 

     List<Account> result = new ArrayList<Account>(); 

     DBCollection coll = connection.getCollection(COLLECTION_ACCOUNT; 

     QueryBUilder queryBuilder = QueryBuilder.start(); 

     DBObject query = queryBuilder.put("userName").in(nameArray).get(); 

     DBCursor cursor = coll.find(query); 

     try{ 

         while(cursor.hasNext()){ 

             DBObject obj = cursor.next(); 

             Account a = new Account(); 

             a.fromDBObject(obj); 

             result.add(a); 

         } 

     }finally{ 

         if (cursor!=null) 

             corsor.close(); 

     } 

 } 


 游标用来限制结果数据,取前5个 

 db.c.find().limit(5) 

 跳过匹配的文档,跳过前5个 

 db.c.find().skip(5) 


 排序:sort用一个对象作为参数,键值对表示,键对应文档键名,值表示排序方向。1升序,-1降序 

 db.f.find().sort({x:-1}) 

 多键复合排序 

 db.account.find().sort({userName:1,age-1}) 


 游标分页查询,每页显示20条数据,查询第二页 

 db.c.find({"y":"post"}).limit(20).skip(20).sort({"x":1}) 

 用skip跳过文档是可行的,但是数量过多就会变慢 



 不用skip的分页: 

 java代码 

 /** 

  * 分页查询(利用id从小到大的排序的原理,注意id一定要升序排列) 

  * pageNum 每页条数 

  * lastID 上一页最后的_id值 

  */ 

 public LinkedList<LoginToken> getPage(int pageNum,String lastID) { 

     DBCollection coll = connection.getCollection(COLLECTION_NAME); 

     QueryBuilder queryBuilder = QueryBuilder.start(); 

     DBObject query = queryBuilder.put("_id").greaterThan(lastID).get(); 

     DBObject orderBy = new BasicDBObject("_id",1);//根据_id升序排列 

     DBCurrsor cursor = coll.find(query).sort(orderBy).limit(pagenum); 

     Linkedlist<LoginToken> result = new LinkedList<LoginToken>(); 

     while(cursor.hasNext()){ 

         DBObject obj=cursor.next(); 

         LoginToken token = new LoginToken(); 

         token.fromDbObject(obj); 

         result.add(token); 

     } 

     return result; 

 }


ps: mongodb中_id会默认建索引。


游标内幕
每一个客户端游标在服务端都会对应,游标消耗内存和其他资源,要尽快合理释放。
游标遍历完或客户端发消息终止时释放游标。
游标在客户端不在作用域(java方法体内),驱动会向服务器发送消息销毁游标。
超时销毁机制,游标即使在客户端作用域内,但是10分钟不用,也会自动销毁。
关闭游标超时销毁机制(使用驱动immortal函数),游标使用完,一定要显式将其关闭,否则会一直消耗服务器资源。