Java代码通过org.springframework.data.mongodb.core.MongoTemplate对MongoDB的CRUD的操作,如果要实现复杂的条件查询,需要通过Query类来实现。
一、Query类的使用说明
query查询语句的实现的方式有两种:
1.1、通过org.springframework.data.mongodb.core.query
构造函数
Query (Criteria criteria)
接受的参数是org.springframework.data.mongodb.core.query.Criteria
org.springframework.data.mongodb.core.query.Criteria:
Criteria是标准查询的接口,可以引用静态的Criteria.where的把多个条件组合在一起,就可以轻松地将多个方法标准和查询连接起来,方便我们操作查询语句。
例如: 查询条件onumber="002"
new Query(Criteria.where("onumber").is("002"))
多个条件组合查询时:
例如:onumber="002" and cname="zcy"
new Query(Criteria.where("onumber").is("002").and("cname").is("zcy"))
例如:onumber="002" or cname="zcy"
new Query(newCriteria().orOperator(Criteria.where("onumber").is("002"),Criteria.where("cname").is("zcy")))
我们通过Criteria的and方法,把这个条件组合一起查询
Criteria提供了很多方法,我们这边先介绍基本文档的查询操作符,对于数组文档或者内嵌文档的操作符,我们下一篇在介绍。
Criteria |
Mongodb |
说明 |
Criteria and (String key) |
$and |
并且 |
Criteria andOperator (Criteria… criteria) |
$and |
并且 |
Criteria orOperator (Criteria… criteria) |
$or |
或者 |
Criteria gt (Object o) |
$gt |
大于 |
Criteria gte (Object o) |
$gte |
大于等于 |
Criteria in (Object… o) |
$in |
包含 |
Criteria is (Object o) |
$is |
等于 |
Criteria lt (Object o) |
$lt |
小于 |
Criteria lte (Object o) |
$lte |
小等于 |
Criteria nin (Object… o) |
$nin |
不包含 |
。。。。。。。。。。。。。。 |
1.2、子类 org.springframework.data.mongodb.core.query.BasicQuery
构造方法
BasicQuery(DBObject queryObject)
BasicQuery(DBObject queryObject, DBObject fieldsObject)
BasicQuery(java.lang.String query)
BasicQuery(java.lang.String query, java.lang.String fields)
DBObject就是转换成JSON格式,提供了我们回顾一下,MongoDB查询时,
db.collection.find(query,projection),query类型是document,所以,我们想使用JSON字符串查询时,我们使用DBObject创建查询实例。
DBObject是接口,提供了几个子类,
我们比较经常使用的比较底层子类,扩展了自己的方法和继承父类,所以功能会比较多。
A. BasicDBObject
public class BasicDBObject extends BasicBSONObject implements DBObject, Bson {
例如:查询条件onumber="002"
DBObject obj = new BasicDBObject(); obj.put( "onumber","002" );
相当于
db.collect.find({"onumber":"002"})
B. BasicDBList
public class BasicDBList extends BasicBSONList implements DBObject {
BasicDBList可以存放多个BasicDBObject条件
例如:我们查询onumber=002 OR cname=zcy1
BasicDBList basicDBList=new BasicDBList(); basicDBList.add(new BasicDBObject("onumber","002")); basicDBList.add(new BasicDBObject("cname","zcy1")); DBObjectobj =new BasicDBObject(); obj.put("$or", basicDBList); Query query=new BasicQuery(obj);
相当于
db.orders.find({$or:[{"onumber":"002"},{"cname":"zcy1"}]})
basicDBList.add方法是添加一个文档的查询条件
C. com.mongodb. QueryBuilder
QueryBuilder默认构造函数,是初始化BasicDBObject,QueryBuilder多个方法标准和查询连接起来,方便我们操作查询语句。跟Criteria是标准查询的接口一样,
QueryBuilder和BasicDBObject配合使用
QueryBuilder帮我们实现了 $and等操作符,我们查看部分的源代码:QueryBuilder部分的源代码:
1.3、Query使用示例
Query query = new Query(); //搜索条件 if(null!=conditions.getAgentId()){ query.addCriteria(Criteria.where("agentId").is(conditions.getAgentId())); } if(null!=conditions.getOrderId()){ query.addCriteria(Criteria.where("orderId").is(conditions.getOrderId())); } if(null!=conditions.getOrderStatus()){ query.addCriteria(Criteria.where("orderStatus").is(conditions.getOrderStatus())); } if(null!=conditions.getAgentMemberId()){ query.addCriteria(Criteria.where("agentMemberId").is(conditions.getAgentMemberId())); } if(null!=conditions.getCustomerMemberId()){ query.addCriteria(Criteria.where("customerMemberId").is(conditions.getCustomerMemberId())); } if(null!=conditions.getPending()){ query.addCriteria(Criteria.where("orderStatus").is(0).orOperator(Criteria.where("orderStatus").is(1))); } // 排序 query.with(new Sort(new Order(Direction.DESC, "createTime"))); query.with(new Sort(new Order(Direction.ASC, "memberId"))); // 翻页 PageCondition pageCondition = new PageCondition(pageNo, pageSize, ""); return memberDao.pageQuery(query, pageCondition); //翻页场景 //不翻页 //return memberDao.query(query, "");//第二个参数是国家码,sprit17里可以传""
二、find查询时指定返回的需要的字段
org.springframework.data.mongodb.core.query.BasicQuery提供了
BasicQuery查询语句可以指定返回字段,构造函数
BasicQuery(DBObject queryObject, DBObject fieldsObject)
fieldsObject 这个字段可以指定返回字段
fieldsObject.put(key,value)
key:字段
value:
说明:
1或者true表示返回字段
0或者false表示不返回该字段
_id:默认就是1,没指定返回该字段时,默认会返回,除非设置为0是,就不会返回该字段。
指定返回字段,有时文档字段多并数据大时,我们指定返回我们需要的字段,这样既节省传输数据量,减少了内存消耗,提高了性能,在数据大时,性能很明显的。
示例:
//查询条件 queryBuilder.or(new BasicDBObject("memberName", memberNames.get(1)), new BasicDBObject("createTime", memberNames.get(2))); queryBuilder.or(new BasicDBObject("createTime", "002"), new BasicDBObject("createTime", "zcy1")); //返回字段列表 BasicDBObject fieldsObject = new BasicDBObject(); fieldsObject.put("onumber", 1); fieldsObject.put("cname", 1); Query query = new BasicQuery(queryBuilder.get(), fieldsObject); // 翻页 PageCondition pageCondition = new PageCondition(pageNo, pageSize, ""); return pageQuery(query, pageCondition);
三、翻页的性能问题
目前我们使用的是query.with(pc.getPageable()),看源码其实就是最常见的分页采用的是skip+limit这种组合方式,
public Query with(Pageable pageable) { if (pageable == null) { return this; } this.limit = pageable.getPageSize(); this.skip = pageable.getOffset(); return with(pageable.getSort()); }
skip+limit这种组合方式,这种方式对付小数据倒也可以,但是对付上几百上千万的大数据,只能力不从心,skip如果跳过大量的数据会很慢,并且会越查越慢。
// const list = db.getCollection('sent_logs').count({ field_1: 'wx5dacee99764a8af5' }).skip(200).limit(10);
针对这一情况,可以通过条件查询+排序+限制返回记录,即 边查询,边排序,排序之后,抽取上一页中的最后一条记录,作为当前分页的查询条件,从而避免了skip效率低下的问题。
db.getCollection('sent_logs').find({ field_1: 'wx5dacee99764a8af5', key1:{$gt: '#上一条记录的排序值#'} }).limit(20)
不过在项目使用过程中,发现后面的数据基本没有用,所以用了一个阉割版的办法,如果条目数大于特定值 比如5000条,则只返回前5000条,否则返回全部,即只能查看前5000条;
再想看更多结果的话 就得用缩小插叙范围来解决了:
//代码大概看下意思就行了 const total_count = 5000; const list = db.getCollection('sent_logs').find({ field_1: 'wx5dacee99764a8af5' }).skip(5000).limit(1); if (list.length === 0) { total_count = db.getCollection('sent_logs').count({ field_1: 'wx5dacee99764a8af5' }) }
这个方法虽然多了一次数据库查询,但是对于几十万往上的查询结果分页来说,提升的性能还是很客观的。
2. 模糊匹配
2.1比较
> $gt , >= $gte, < $lt, <= $lte, != $ne
> db.tianyc02.find()
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$lt:100}})
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$lt:100,$gt:20}})
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$ne:11}})
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
2.2 $in & $nin
> db.tianyc02.find()
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$in:[11,22]}})
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$nin:[11,22]}})
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
2.3 $or
> db.tianyc02.find({$or:[{age:11},{age:22}]})
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({$or:[{age:11},{name:'xttt'}]})
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
2.4 $not
> db.tianyc02.find({age:{$mod:[11,0]}})
{ "_id" : ObjectId("50ea6b6f12729d90ce6e341b"), "name" : "xtt", "age" : 11 }
{ "_id" : ObjectId("50ea6b7312729d90ce6e341c"), "name" : "xtt", "age" : 22 }
> db.tianyc02.find({age:{$not:{$mod:[11,0]}}})
{ "_id" : ObjectId("50ea6eba12729d90ce6e3423"), "name" : "xttt", "age" : 111 }
{ "_id" : ObjectId("50ea6eba12729d90ce6e3424"), "name" : "xttt", "age" : 222 }
$mod会将查询的值除以第一个给定的值,若余数等于第二个给定的值,则返回该结果。
$not与正则表达式联合使用时极为有效,用来查找那些与特定模式不匹配的文档。