package org.sothis.dal.mongo;
import java.util.List;
import java.util.Map;
import org.bson.types.ObjectId;
import org.sothis.dal.AbstractJpaCompatibleDao.PropertyInfo;
import org.sothis.dal.query.Chain;
import org.sothis.dal.query.Cnd;
import org.sothis.dal.query.Logic;
import org.sothis.dal.query.Op;
import org.sothis.dal.query.OrderBy;
import org.sothis.dal.query.Sort;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* mongo db 的查询生成器
*
* @author velna
*
*/
public class MongoQueryBuilder {
private final static String[] OP_MAP;
private final static String[] LOGIC_MAP;
static {
OP_MAP = new String[Op.values().length];
OP_MAP[Op.EQ.ordinal()] = "$eq";
OP_MAP[Op.GT.ordinal()] = "$gt";
OP_MAP[Op.GTE.ordinal()] = "$gte";
OP_MAP[Op.IN.ordinal()] = "$in";
OP_MAP[Op.LIKE.ordinal()] = "$regex";
OP_MAP[Op.LT.ordinal()] = "$lt";
OP_MAP[Op.LTE.ordinal()] = "$lte";
OP_MAP[Op.NE.ordinal()] = "$ne";
OP_MAP[Op.NIN.ordinal()] = "$nin";
LOGIC_MAP = new String[Logic.values().length];
LOGIC_MAP[Logic.AND.ordinal()] = "$and";
LOGIC_MAP[Logic.OR.ordinal()] = "$or";
}
private final Map<String, PropertyInfo> propertyMap;
private final DBObject defaultFields;
private final boolean idGeneratedValue;
public MongoQueryBuilder(Map<String, PropertyInfo> propertyMap, boolean idGeneratedValue) {
this.propertyMap = propertyMap;
this.idGeneratedValue = idGeneratedValue;
defaultFields = new BasicDBObject();
for (PropertyInfo p : propertyMap.values()) {
defaultFields.put(p.getColumn().name(), 1);
}
}
private String mapField(String property) {
return mapProperty(property).getColumn().name();
}
private PropertyInfo mapProperty(String property) {
PropertyInfo pi = propertyMap.get(property);
if (null == pi) {
throw new IllegalArgumentException("no property named [" + property + "] found.");
}
return pi;
}
/**
* 将{@code cnd}转化为mongo db可用的查询对象
*
* @param cnd
* @return
*/
public DBObject cndToQuery(Cnd cnd) {
if (null == cnd) {
return null;
}
DBObject query = new BasicDBObject();
Object op = cnd.getOp();
if (op instanceof Op) {
PropertyInfo pi = mapProperty((String) cnd.getLeft());
Object value = cnd.getRight();
if (pi.isID() && this.idGeneratedValue) {
value = new ObjectId((String) cnd.getRight());
}
if (op == Op.EQ) {
query.put(pi.getColumn().name(), value);
} else {
query.put(pi.getColumn().name(), new BasicDBObject(OP_MAP[((Op) op).ordinal()], value));
}
} else if (op instanceof Logic) {
BasicDBList logicQuery = new BasicDBList();
logicQuery.add(cndToQuery((Cnd) cnd.getLeft()));
logicQuery.add(cndToQuery((Cnd) cnd.getRight()));
query.put(LOGIC_MAP[((Logic) op).ordinal()], logicQuery);
} else if (op instanceof String) {
String _op = (String) op;
if (_op.length() > 1 && _op.charAt(0) == '$') {
PropertyInfo pi = mapProperty((String) cnd.getLeft());
Object value = cnd.getRight();
if (pi.isID() && this.idGeneratedValue) {
value = new ObjectId((String) cnd.getRight());
}
query.put(pi.getColumn().name(), new BasicDBObject(_op, value));
} else {
throw new RuntimeException("unknown op: " + op);
}
} else {
throw new RuntimeException("unknown op: " + op);
}
if (cnd.isNot()) {
BasicDBList nors = new BasicDBList();
nors.add(query);
return new BasicDBObject("$nor", nors);
} else {
return query;
}
}
/**
* 将{@code chain}转化为mongo db可用的字段信息
*
* @param chain
* @return
*/
public DBObject chainToFields(Chain chain) {
if (null == chain || chain.size() == 0) {
return defaultFields;
}
DBObject fields = new BasicDBObject();
for (Chain c : chain) {
fields.put(mapField(c.name()), 1);
}
return fields;
}
/**
* 将{@code chain}转化为mongo db可用的update语句
*
* @param chain
* @return
*/
public DBObject chainToUpdate(Chain chain) {
if (null == chain) {
return null;
}
return new BasicDBObject("$set", chainToData(chain));
}
private DBObject chainToData(Chain chain) {
if (null == chain) {
return null;
}
DBObject update = new BasicDBObject();
for (Chain c : chain) {
if (c.value() instanceof Chain) {
update.put(mapField(c.name()), chainToData((Chain) c.value()));
} else {
update.put(mapField(c.name()), c.value());
}
}
return update;
}
/**
* 将{@code orderBy}转化为mongo db可用的排序对象
*
* @param orderBy
* @return
*/
public DBObject orderByToSorts(OrderBy orderBy) {
if (null == orderBy) {
return null;
}
List<Sort> sorts = orderBy.getSorts();
if (sorts.isEmpty()) {
return null;
}
DBObject sort = new BasicDBObject();
for (Sort s : sorts) {
sort.put(mapField(s.getField()), s.isAsc() ? 1 : -1);
}
return sort;
}
}