MongoDB条件查询



1.自动注入mongo

@Autowired
protected MongoOperations mongo;

@Autowired
protected GridFsOperations gridFs;



2.查询



  创建条件对象 Criteria
Criteria criteria;
//等于条件
      criteria.where("id").is(id)
//大于等于和小于等于 (>=startTimestamp && <= endTimestamp)
      criter.and("saveTime").gte(startTimestamp).lte(endTimestamp);
//大于和小于
      criter.and("saveTime").gt(minSaveTime).lt(endTimestamp);
//保存时间不等于
       criter.and("saveTime").ne(saveTimeList);
//保存时间在列表内
       criter.and("saveTime").in(saveTimeList);
//保存时间不在列表内
       criter.and("saveTime").nin(saveTimeList);

  MongoDB条件操作符

(>) 大于 - $gt
(<) 小于 - $lt
(>=) 大于等于 - $gte
(<= ) 小于等于 - $lte

 创建Query对象

// 创建
 Query query = new Query(criteria);
 
 //升序条件
 Sort sort = new Sort(Sort.Direction.ASC, "saveTime");
 Sort sort2 = new Sort(Sort.Direction.ASC, "id");
 Query query = new Query(criteria).with(sort).with(sort2);
 
// 限制条数
 Query query = new Query(criteria).with(sort).limit(pageSize);
 
// 分页
 Query quey=new Query(criteria).with(sort).with(Pageable pageable)
// Pageable对象需要自己实现
//查询
 List<T> list= mongo.find(query,Class<T> entityClass); 返回list

 

内嵌数组查询表达式

(1) $size,用于查询数组大小为size的记录
  db.a.find({"comments":{"$size":3}})
  表示查询comments数组中含有三个元素的记录。

(2) 查询一个数组的最前n、最后n个元素
    db.a.find({},{"addrs":{"$slice":5}})   返回前5条

    db.a.find({},{"addrs":{"$slice":-5}})  返回后5条

(3) skip

     使用Mongodb的时候【其他数据库也类似】,尽量少跳过过多的数据,这样会产生性能问题。
mongodb的skip是用来跳过数据的,但是要少用。需要使用的时候可以用其他方式代替。比如说要对博客  文章分页,那么一般的做法是
   db.a.find().sort({"time":1}).skip(10000).limit(10)
优化的做法可能是,根据上一页取出来的时间,在下次分页,即取出下一页的时候,根据上一个时间,来查找,然后再limit,例如
   db.a.find({"time":{"$gt":lasttime}}).sort({"time":1}).limit(10)
这样用查找替代跳过数据,效率会有提升。

(4) 查询内嵌数组文档

$elemMatch":{"author":"naughty","score":{"$gt":3}}}})

$elemMatch 将限定条件进行分组,仅当需要对一个内嵌文档的多个键进行查询的时候,才会用到。

(5) 查询内嵌文档

{ "_id" : ObjectId("54af318953b409e1c2efa8d8"), "title" : "iteye", "count" : 4,  
"blog" : { "title" : "eksliang.iteye.com", "name" : "ickes" } }  

db.iteye.find({"blog.name":"ickes"}}) --查询内嵌文档

(6) 简单数组查询

db.XXX.insert({"names":["BuleRiver1", "BuleRiver2", "BuleRiver3"]});

查询包含BuleRiver1的文档

db.XXX.find({"names":"BuleRiver1"});

查询数组里面同时有BuleRiver1和BuleRiver2才返回

db.XXX.find({"names":{"$all":["BuleRiver1", "BuleRiver2"]}})

如果要返回names数组长度为3的条目,使用$size

db.XXX.find({"names":{"$size":3}});

如果想要返回该数组的前2项,使用$slice

db.XXX.find({"names":{"$slice":2}});

返回后两条:

db.XXX.find({"names":{"$slice":-2}});

从第2条开始,返回3个条目:

db.XXX.find({"names":{"$slice":[2, 3]}});

 

 



3.更新操作    

      mongodb中的update的形式是这样的:

      db.XXX.update(query, obj, upsert, multi);

      对于upsert(默认为false):

     如果upsert=true,如果query找到了符合条件的行,则修改这些行,如果没有找到,则追加一行符合query和obj的行。

     如果upsert为false,找不到时,不追加。

     对于multi(默认为false): 

     如果multi=true,则修改所有符合条件的行,否则只修改第一条符合条件的行。

     Criteria criter 对象创建方式如上

Query query = new Query(criter);
Update update = new Update();
update.set("userId", activeHistoryData.getUserId())
      .set("createdTimestamp", activeHistoryData.getCreatedTimestamp())
      .set("updatedTimestamp", activeHistoryData.getUpdatedTimestamp())
      .set("totalSteps", activeHistoryData.getTotalSteps())
      .set("totalDurations", activeHistoryData.getTotalDurations())
      .set("totalCalories", activeHistoryData.getTotalCalories())
      .set("totalDistance", activeHistoryData.getTotalDistance());

 mongo.upsert(query, update, ActiveHistoryData.class);

  “$set” 修改器 和 “$unset”修改器

  "$set"  用来指定一个键的键值,如果这个键不存在,则创建它。这对更新模式或者增加用户定义键来说非常方便。

> db.iteye.find() --查看iteye集合,里面有一个blog的内嵌文档  
{ "_id" : ObjectId("54af318953b409e1c2efa8d8"), "title" : "iteye", "count" : 4,  
"blog" : { "title" : "eksliang.iteye.com", "name" : "ickes" } }  

> db.iteye.update({"blog.name":"ickes"},{"$set":{"blog.name":"xl"}}) --用$set修改内嵌文档  

> db.iteye.find() --查看当前修改,很明显修改成功  
{ "_id" : ObjectId("54af318953b409e1c2efa8d8"), "title" : "iteye", "count" : 4,  
"blog" : { "title" : "eksliang.iteye.com", "name" : "xl" } }

  "$unset"  就是将文档中的某个键完全删除

> db.user.find({"name":"xl"}) --查询name=xl的文档  
{ "_id" : ObjectId("54ae6fb5b94cf1b60f105710"), "name" : "xl", "title" : "MongoD  
B" }  

> db.user.update({"name":"xl"},{"$unset":{"title":1}}) --删除title这个键(这里删除键的值,官网说为任意数字,我测试时,任何值都是没有任何问题的)  

> db.user.find({"name":"xl"}) --再次查看,发现键删除成功  
{ "_id" : ObjectId("54ae6fb5b94cf1b60f105710"), "name" : "xl" }

 内嵌文档字段更新

//修改resultDetail.diseaseResult.highDiseaseResultDetails的第一个数组元素的multiple的属性值
db.resultTxt.update({"barCode":"111-1146-6104"},
      {$set:{"resultDetail.diseaseResult.highDiseaseResultDetails.0.multiple":15}})

 (1) 修改器

  • $set 和 $unset:修改字段值和删除字段;
  • $rename,修改字段名字,
  • db.students.update({_id:1},{$rename:{'nickname':'alias','cell':'mobile'}})
  • $inc:数字类型字段增减,db.user.update({'name':'owen'},{'$inc':{'age':1}});
  • $push和$pushAll:向数组类型末尾追加数据,字段不存在则创建;
  • $addToSet:向数组类型追加数据时避免重复;
  • $each:遍历数组;
  • $pop:从数组中删除数据,{$pop:{key:1}}从末尾删除数据,-1从头部删除;
  • $pull和$pullAll:从数组中删除指定数据,db.list.update({},{$pull:{'todo':'undo'}});
  • 数组定位修改器:数组可以通过下标定位,也可以通过’$‘定位查询匹配到的记录,db.blog.update({'comments.author':'joe'},{'$set':{'comments.$.author':'jim'}}); 

  (2) $addToSet

   是往一个数组里插入每个记录,并保证元素唯一。$addToSet 只有在value1不为数组成员的时候才会添加 value1 到数组中的field。$addToSet  仅能保证集合中项唯一并且不能保证集合中元素的顺序。

db.collection.update( { field: value }, { $addToSet: { field: value1 } } );

   $addToSet and $each是往一个数组里插入每个记录,并保证元素唯一,插入一个集合。

db.search_loggers.update({user_id : 2}, {$addToSet : {words : {$each : [ 1, 2, 1, 2, 1]}}}, true)

 

db.search_loggers.find()  
{ "_id" : ObjectId("4f90be7a09ee78ef9db6e01c"), "user_id" : 2, "words" : [ 1, 2 ] }

  (3) 删除记录中的一个字段
  db.a.update({"xxx":"xxxx"},{"$unset":{"addrs":1}})

  (4) $push向数组中增加一个元素,如果数组不存在则创建
  db.a.update({},{"$push":{"addrs":"shenzhen"}})

  (5) 从数组中删除一个
     $pop
      db.a.update({},{"$pop":{"addrs":-1}})
         -1表示从数组末尾删除。
      db.a.update({},{"$pop":{"addrs":1}})
         1表示从数组头部删除
     $pull
      $pull可以删除指定的数组元素
      db.a.update({},{"$pull":{"addrs":"shenzhen"}})

 



 4.插入操作

User user = new User("...");
//将user对象保存到"user"这个collection中
mongo.save(user);
//将user对象保存到"new collection"这个collection中
mongo.save("new collection",user);
//将user对象保存到"user"这个collection中
mongo.insert(user);
//将user对象保存到"new collection"这个collection中
mongo.insert("new collection", user);
//将user的对象列表(List)保存到"user"collection中去
mongo.insertList(userInList);
//将user的对象列表(List)保存到"new collection"collection中去
mongo.insertList("new collection", userInList);

saveorupdate的意思。

 2) insert的意思是:当记录不存在时插入,而如果记录存在时则忽略,继续插入。



5.删除

WriteResult remove(Object object);

WriteResult remove(Object object, String collection);

WriteResult remove(Query query, Class<?> entityClass);

 



6.聚合

//计数,查询,返回统计的数量
long count(Query query, Class<?> entityClass);

long count(Query query, String collectionName);

long count(Query query, Class<?> entityClass, String collectionName);

 



7. 其他操作

 $type

 $type 基于 bson type来匹配一个元素的类型,像是按照类型ID来匹配,找到bson类型和id对照表。

db.things.find( { a : { $type : 2 } } ); // matches if a is a string
db.things.find( { a : { $type : 16 } } ); // matches if a is an int

 $exists:判断字段是否存在;
 查询所有存在name字段的记录
 db.users.find({name: {$exists: true}});

 查询所有不存在phone字段的记录
 db.users.find({phone: {$exists: false}}); 

 条件表达式

    1)$cond

    上文中我们已经看到相关示例,它的作用等同于一个“三元表达式”,语法:

  $cond:{if : <boolean-expression>, then : <true-expression>,else : <false-expression> }

    2)$ifNull

    判定指定表达式是否为null,比如字段不存在(undefined)、值为null。语法:

 $ifNull:[<expression>,<return-value expression>]

     如果expression的值为null则返回“return-value”。

 



8.文件存储

 GridFS存储小文件。

 GridFSDBFile  文件对象类

 //GridFS 文件操作对象
 @Autowired
 protected GridFsOperations gridFs;



9.MongoDB数据库和集合导入导出

//数据库恢复  mongorestore -d  mofangdb  ./mofangdb20170118/mofangdb/

//数据库备份 mongodump    -d  mofangdb -o  ./mofangdb20170116 

 

单个collection备份
mongoexport -h dbhost -d dbname -c collectionname -f collectionKey -o dbdirectory
-h: MongoDB所在服务器地址
-h, --host=<hostname>                           
      --port=<port>                   
-d: 需要恢复的数据库实例
-c: 需要恢复的集合
-f: 需要导出的字段(省略为所有字段)
-o: 表示导出的文件名
mongoexport -d mofangdb -c resultTxt  -o ./resultTxt.dat

单个collection恢复
mongoimport -d db  -c collectionname  –type csv –headerline 
-d  数据库
-h, --host=<hostname>                
     --port=<port>  
-type: 指明要导入的文件格式
-headerline: 批明不导入第一行,因为第一行是列名

mongoimport -d mofangdb -c collectionname   ./

//集合导出  mongoexport -d mofangdb -c resultTxt  -o ./resultTxt.dat
//集合导入  mongoimport -d mofangdb -c collectionname   ./resultTxt.dat

 



10. 聚合group by功能

db.sp_listpage.group({	
   keyf:function(doc){
  	 return {sourceId:doc.sourceId};
   },
   //统计结果字段
   initial:{ count:0,notdownload:0,downloaded:0,geturl:0,faildownload:0,failgeturl:0},

   reduce:function(doc,result){
   	  result.count +=1;

	 if(doc.status == 0){
	 	result.notdownload += 1;
	 }
	 if(doc.status == 1){
	 	result.downloaded +=1;
	 }
	 if(doc.status == 2){
	 	result.geturl += 1;
	 }
	 if(doc.status == -1){
	 	result.faildownload +=1;
	 }
	 if(doc.status == -2){
	 	result.failgeturl += 1;
	 }
   },
   finalize:function(result){
   		result.downFailRate = result.faildownload/result.count;
   		result.parseFailRate = result.failgeturl/result.count;
   }

})

 

staus = 0  //未下载

status = 1 //已下载

status = 2  //解析成功

status = -1  //下载失败

status= -2   //解析失败

根据表中的status字段来统计,不同状态的总数

 

db.collection.aggregate()

db.collection.aggregate([
    { "$match": { "_id": ObjectId("545b9fa0dd5318a4285f7ce7") } },
    { "$unwind": "$messages" },
    { "$group": {
        "_id": "$_id",
        "sent": {
            "$sum": {
                "$cond": [
                    { "$eq": [ "$mesages.status", "sent" ] },
                    1,
                    0
                ]
            }
        },
        "pending": {
            "$sum": {
                "$cond": [
                    { "$eq": [ "$messages.status", "pending" ] },
                    1,
                    0
                ]
            }
        },
        "done": {
            "$sum": {
                "$cond": [
                    { "$eq": [ "$messages.status", "done" ] },
                    1,
                    0
                ]
            }
        }
    }}
])

Java实现:

class CustomGroupOperation implements AggregationOperation {
    private DBObject operation;

    public CustomGroupOperation (DBObject operation) {
        this.operation = operation;
    }

    @Override
    public DBObject toDBObject(AggregationOperationContext context) {
        return context.getMappedObject(operation);
    }
}

 DBObject myGroup = (DBObject)new BasicDBObject(
        "$group", new BasicDBObject(
            "_id","$_id"
        ).append(
            "sent", new BasicDBObject(
                "$sum", new BasicDBObject(
                    "$cond", new Object[]{
                        new BasicDBObject(
                            "$eq", new Object[]{ "$messages.status", "sent"}
                        ),
                        1,
                        0
                    }
                )
            )
        ).append(
            "pending", new BasicDBObject(
                "$sum", new BasicDBObject(
                    "$cond", new Object[]{
                        new BasicDBObject(
                            "$eq", new Object[]{ "$messages.status", "pending"}
                        ),
                        1,
                        0
                    }
                )
             )
        ).append(
            "done", new BasicDBObject(
                "$sum", new BasicDBObject(
                    "$cond", new Object[]{
                         new BasicDBObject(
                            "$eq", new Object[]{ "$messages.status", "done"}
                         ),
                         1,
                         0
                    }
                 )
            )
        )
     );


   ObjectId myId = new ObjectId("545b9fa0dd5318a4285f7ce7");

   Aggregation aggregation = newAggregation(
           match(Criteria.where("_id").is(myId)),
           unwind("messges"),
           new CustomGroupOperation(myGroup)
   );