高级 DAO 操作
AdvancedDao是BuguDao的子类,它主要提供:
(1) 统计功能。
(2) Aggregation功能。
(3) MapReduce功能。
统计
AdvancedDao提供如下常用的统计功能,它们都是基于Aggregation实现的。
public double max(String key)
public double max(String key, BuguQuery query)
public double min(String key)
public double min(String key, BuguQuery query)
public double sum(String key)
public double sum(String key, BuguQuery query)
public double average(String key)
public double average(String key, BuguQuery query)
示例代码:
//继承自AdvancedDao
public class FooDao extends AdvancedDao<Foo>{
public FooDao(){
super(Foo.class);
}
}
FooDao dao = new FooDao();
double d = dao.max("embed.x");
Aggregation
bugu-mongo框架使用BuguAggregation类来实现aggregate的功能,其用法与BuguQuery类似。
得到一个BuguAggregation实例:
AdvancedDao<MockEntity> dao = ...
BuguAggregation agg = dao.aggregate();
BuguAggregation包括如下方法:
/* pipeline方法 */
public BuguAggregation lookup(DBObject dbo)
public BuguAggregation lookup(String jsonString)
public BuguAggregation lookup(Lookup lookup)
public BuguAggregation project(DBObject dbo)
public BuguAggregation project(String jsonString)
public BuguAggregation projectInclude(String... fields)
public BuguAggregation project(String key, Object val)
public BuguAggregation match(String key, Object value)
public BuguAggregation match(DBObject dbo)
public BuguAggregation match(BuguQuery query)
public BuguAggregation limit(int n)
public BuguAggregation skip(int n)
public BuguAggregation unwind(String field)
public BuguAggregation geoNear(DBObject dbo)
public BuguAggregation geoNear(String jsonString)
public BuguAggregation geoNear(GeoNearOptions options)
public BuguAggregation group(DBObject dbo)
public BuguAggregation group(String jsonString)
public BuguAggregation sort(String jsonString)
public BuguAggregation sort(DBObject dbo)
public BuguAggregation out(String target)
/* 返回结果 */
public Iterable<DBObject> results()
一段代码示例如下:
AdvancedDao<MockEntity> dao = new AdvancedDao<MockEntity>(MockEntity.class);
DBObject match = dao.query().greaterThanEquals("score", 60).getCondition();
DBObject group = new BasicDBObject();
group.put("_id", null);
group.put("average", new BasicDBObject("$avg", "$score"));
Iterable<DBObject> results = dao.aggregate().match(match).group(group).results();
DBObject dbo = results[0];
System.out.println(dbo.get("average"));
project和group等方法,其参数不仅支持DBObject,而且还支持JSON字符串。例如:
BuguAggregation agg = dao.aggregate();
agg.group("{_id:'$author', count:{$sum:1}}");
agg.sort("{count:-1}");
Iterable<DBObject> it = agg.results();
Lookup
从mongoDB 3.2开始,聚合操作开始支持lookup功能。lookup相当于SQL中的left outer join。详见MongoDB官方文档。
bugu-mongo中提供了辅助类Lookup,用于简化该功能的实现。Lookup的构造函数如下:
/**
* constructor
* @param from 需要join的另一个表
* @param localField 本表的字段名称
* @param foreignField 需要join的字段
* @param as 添加到本表的新的数组名称
*/
public Lookup(String from, String localField, String foreignField, String as)
例:
假如有图书类Book、评论类Comment,两者之间通过图书的title来关联(现实中这样不太合理,这只是个例子)。
@Entity
public class Book extends SimpleEntity {
private String title;
private String author;
private float price;
...
}
@Entity
public class Comment extends SimpleEntity {
private String title;
private int star;
...
}
可以用lookup来关联这2个collection,计算出每位作者的平均得分(star):
BookDao dao = new BookDao();
BuguAggregation agg = dao.aggregate();
agg.lookup(new Lookup("comment", "title", "title", "book_comment"));
agg.unwind("$book_comment");
agg.group("{_id:'$author', averageStar:{$avg:'$book_comment.star'}}");
agg.sort("{averageStar:-1}");
Iterable<DBObject> it = agg.results();
for(DBObject dbo : it){
System.out.println(dbo.get("_id"));
System.out.println(dbo.get("averageStar"));
}
就像前面说的,通过title来关联,仅仅是为了举例的需要。在实际的应用中,更合理的设计,应该是通过@Ref来关联。
需要特别注意的是,通过@Ref来关联的属性,如果要进行lookup操作,则必须将其设置成为reduced=true。
例如:
@Entity
public class Book extends SimpleEntity {
private String title;
private String author;
private float price;
...
}
@Entity
public class CoolComment extends SimpleEntity {
@Ref(reduced = true)
private Book book;
private int star;
...
}
计算出每位作者的平均得分(star):
BookDao dao = new BookDao();
BuguAggregation agg = dao.aggregate();
agg.lookup(new Lookup("coolcomment", "_id", "book", "book_comment")); //注意:localField是_id
agg.unwind("$book_comment");
agg.group("{_id:'$author', averageStar:{$avg:'$book_comment.star'}}");
agg.sort("{averageStar:-1}");
Iterable<DBObject> it = agg.results();
for(DBObject dbo : it){
System.out.println(dbo.get("_id"));
System.out.println(dbo.get("averageStar"));
}
ExpressionBuilder
为了方便编写聚合代码,bugu-mongo提供了ExpressionBuilder工具类,用于创建不同的expression表达式。最常用的2个是BoolBuilder和CondBuilder。
例如,我们需要把图书的价格进行阶梯统计,1-10为一个阶梯,10-20为一个阶梯,20-30为一个阶梯,然后计算出每个阶梯的图书数量:
BookDao dao = new BookDao();
BuguAggregation agg = dao.aggregate();
DBObject cond1 = ExpressionBuilder.cond().ifCondition("{'$lte':['$price', 10]}").thenValue(10).elseValue("$price").build();
DBObject bool2 = ExpressionBuilder.bool().and("{'$gt':['$price', 10]}", "{'$lte':['$price', 20]}").build();
DBObject cond2 = ExpressionBuilder.cond().ifCondition(bool2).thenValue(20).elseValue("$price").build();
DBObject bool3 = ExpressionBuilder.bool().and("{'$gt':['$price', 20]}", "{'$lte':['$price', 30]}").build();
DBObject cond3 = ExpressionBuilder.cond().ifCondition(bool3).thenValue(30).elseValue("$price").build();
agg.project("price", cond1);
agg.project("price", cond2);
agg.project("price", cond3);
agg.group("{_id:'$price', count:{$sum:1}}");
Iterable<DBObject> it = agg.results();
for(DBObject dbo : it){
System.out.print(dbo.get("_id"));
System.out.print(" : ");
System.out.println(dbo.get("count"));
}
更多的BuguAggregation的使用示例,请参考项目源代码中的单元测试部分。
MapReduce
AdvancedDao提供了对MapReduce的支持。但鉴于MongoDB的MapReduce性能不是很理想,如果您的数据量非常庞大,请谨慎使用。
AdvancedDao中MapReduce相关的方法包括:
public Iterable<DBObject> mapReduce(MapReduceCommand cmd)
public Iterable<DBObject> mapReduce(String map, String reduce)
public Iterable<DBObject> mapReduce(String map, String reduce, DBObject query)
public Iterable<DBObject> mapReduce(String map, String reduce, String outputTarget, MapReduceCommand.OutputType outputType, DBObject sort, DBObject query)
public Iterable<DBObject> mapReduce(String map, String reduce, String outputTarget, MapReduceCommand.OutputType outputType, DBObject sort, int pageNum, int pageSize, DBObject query)