mirror of
https://github.com/querydsl/querydsl.git
synced 2026-06-27 21:01:15 +08:00
#113 first sketch of Querydsl mongodb join/ref support
This commit is contained in:
parent
354ba024af
commit
48fa1a5a15
@ -37,10 +37,12 @@ import com.mysema.query.SimpleProjectable;
|
||||
import com.mysema.query.SimpleQuery;
|
||||
import com.mysema.query.support.QueryMixin;
|
||||
import com.mysema.query.types.Expression;
|
||||
import com.mysema.query.types.ExpressionUtils;
|
||||
import com.mysema.query.types.Operation;
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.ParamExpression;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.PathImpl;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
@ -52,6 +54,9 @@ import com.mysema.query.types.Predicate;
|
||||
*/
|
||||
public abstract class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, SimpleProjectable<K> {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class NoResults extends RuntimeException {}
|
||||
|
||||
private final MongodbSerializer serializer;
|
||||
|
||||
private final QueryMixin<MongodbQuery<K>> queryMixin;
|
||||
@ -78,34 +83,61 @@ public abstract class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, S
|
||||
protected abstract DBCollection getCollection(Class<?> type);
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
QueryMetadata metadata = queryMixin.getMetadata();
|
||||
if (!metadata.getJoins().isEmpty()) {
|
||||
Predicate extraFilter = null;
|
||||
List<JoinExpression> joins = metadata.getJoins();
|
||||
for (int i = joins.size() - 1; i >= 0; i--) {
|
||||
JoinExpression join = joins.get(i);
|
||||
Expression<?> source = ((Operation<?>)join.getTarget()).getArg(0);
|
||||
Class<?> target = ((Operation<?>)join.getTarget()).getArg(1).getType();
|
||||
List<DBObject> ids = getIds(target, join.getCondition());
|
||||
}
|
||||
} else {
|
||||
return collection.findOne(createQuery(metadata.getWhere())) != null;
|
||||
public boolean exists() {
|
||||
try {
|
||||
QueryMetadata metadata = queryMixin.getMetadata();
|
||||
Predicate filter = createFilter(metadata);
|
||||
return collection.findOne(createQuery(filter)) != null;
|
||||
} catch (NoResults ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<DBObject> getIds(Class<?> target, Predicate condition) {
|
||||
@Nullable
|
||||
protected Predicate createFilter(QueryMetadata metadata) {
|
||||
Predicate filter;
|
||||
if (!metadata.getJoins().isEmpty()) {
|
||||
filter = ExpressionUtils.allOf(metadata.getWhere(), createJoinFilter(metadata));
|
||||
} else {
|
||||
filter = metadata.getWhere();
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Predicate createJoinFilter(QueryMetadata metadata) {
|
||||
Predicate extraFilter = null;
|
||||
List<JoinExpression> joins = metadata.getJoins();
|
||||
for (int i = joins.size() - 1; i >= 0; i--) {
|
||||
JoinExpression join = joins.get(i);
|
||||
Expression<?> source = ((Operation<?>)join.getTarget()).getArg(0);
|
||||
Class<?> target = ((Operation<?>)join.getTarget()).getArg(1).getType();
|
||||
Predicate filter = ExpressionUtils.allOf(join.getCondition(), extraFilter);
|
||||
List<Object> ids = getIds(target, filter);
|
||||
if (ids.isEmpty()) {
|
||||
throw new NoResults();
|
||||
}
|
||||
Path path = new PathImpl<String>(String.class, (Path)source, "$id");
|
||||
extraFilter = ExpressionUtils.in(path, ids);
|
||||
}
|
||||
return extraFilter;
|
||||
}
|
||||
|
||||
protected List<Object> getIds(Class<?> target, Predicate condition) {
|
||||
DBCollection collection = getCollection(target);
|
||||
DBCursor cursor = createCursor(condition, QueryModifiers.EMPTY, Collections.<OrderSpecifier<?>>emptyList());
|
||||
// TODO : fetch only ids
|
||||
DBCursor cursor = createCursor(collection, condition, QueryModifiers.EMPTY, Collections.<OrderSpecifier<?>>emptyList());
|
||||
if (cursor.hasNext()) {
|
||||
|
||||
List<Object> ids = new ArrayList<Object>(cursor.count());
|
||||
for (DBObject obj : cursor) {
|
||||
ids.add(obj.containsField("id") ? obj.get("id") : obj.get("_id"));
|
||||
}
|
||||
return ids;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean notExists() {
|
||||
return !exists();
|
||||
@ -177,20 +209,25 @@ public abstract class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, S
|
||||
|
||||
@Override
|
||||
public List<K> list() {
|
||||
DBCursor cursor = createCursor();
|
||||
List<K> results = new ArrayList<K>(cursor.size());
|
||||
for (DBObject dbObject : cursor) {
|
||||
results.add(transformer.transform(dbObject));
|
||||
}
|
||||
return results;
|
||||
try {
|
||||
DBCursor cursor = createCursor();
|
||||
List<K> results = new ArrayList<K>(cursor.size());
|
||||
for (DBObject dbObject : cursor) {
|
||||
results.add(transformer.transform(dbObject));
|
||||
}
|
||||
return results;
|
||||
} catch (NoResults ex) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
protected DBCursor createCursor() {
|
||||
QueryMetadata metadata = queryMixin.getMetadata();
|
||||
return createCursor(metadata.getWhere(), metadata.getModifiers(), metadata.getOrderBy());
|
||||
Predicate filter = createFilter(metadata);
|
||||
return createCursor(collection, filter, metadata.getModifiers(), metadata.getOrderBy());
|
||||
}
|
||||
|
||||
protected DBCursor createCursor(@Nullable Predicate where, QueryModifiers modifiers,
|
||||
protected DBCursor createCursor(DBCollection collection, @Nullable Predicate where, QueryModifiers modifiers,
|
||||
List<OrderSpecifier<?>> orderBy) {
|
||||
DBCursor cursor = collection.find(createQuery(where));
|
||||
if (modifiers.getLimit() != null){
|
||||
@ -212,40 +249,52 @@ public abstract class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, S
|
||||
|
||||
@Override
|
||||
public K singleResult() {
|
||||
DBCursor c = createCursor().limit(1);
|
||||
if (c.hasNext()){
|
||||
return transformer.transform(c.next());
|
||||
} else {
|
||||
try {
|
||||
DBCursor c = createCursor().limit(1);
|
||||
if (c.hasNext()){
|
||||
return transformer.transform(c.next());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (NoResults ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public K uniqueResult() {
|
||||
Long limit = queryMixin.getMetadata().getModifiers().getLimit();
|
||||
if (limit == null){
|
||||
limit = 2l;
|
||||
}
|
||||
DBCursor c = createCursor().limit(limit.intValue());
|
||||
if (c.hasNext()){
|
||||
K rv = transformer.transform(c.next());
|
||||
if (c.hasNext()){
|
||||
throw new NonUniqueResultException();
|
||||
try {
|
||||
Long limit = queryMixin.getMetadata().getModifiers().getLimit();
|
||||
if (limit == null){
|
||||
limit = 2l;
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
DBCursor c = createCursor().limit(limit.intValue());
|
||||
if (c.hasNext()){
|
||||
K rv = transformer.transform(c.next());
|
||||
if (c.hasNext()){
|
||||
throw new NonUniqueResultException();
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (NoResults ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchResults<K> listResults() {
|
||||
long total = count();
|
||||
if (total > 0l){
|
||||
return new SearchResults<K>(list(), queryMixin.getMetadata().getModifiers(), total);
|
||||
} else {
|
||||
try {
|
||||
long total = count();
|
||||
if (total > 0l){
|
||||
return new SearchResults<K>(list(), queryMixin.getMetadata().getModifiers(), total);
|
||||
} else {
|
||||
return SearchResults.emptyResults();
|
||||
}
|
||||
} catch (NoResults ex) {
|
||||
return SearchResults.emptyResults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -255,7 +304,12 @@ public abstract class MongodbQuery<K> implements SimpleQuery<MongodbQuery<K>>, S
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return collection.count(createQuery(queryMixin.getMetadata().getWhere()));
|
||||
try {
|
||||
Predicate filter = createFilter(queryMixin.getMetadata());
|
||||
return collection.count(createQuery(filter));
|
||||
} catch (NoResults ex) {
|
||||
return 0l;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -16,9 +16,11 @@ package com.mysema.query.mongodb;
|
||||
import static java.util.Arrays.asList;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -32,6 +34,8 @@ import org.junit.Test;
|
||||
|
||||
import com.google.code.morphia.Datastore;
|
||||
import com.google.code.morphia.Morphia;
|
||||
import com.mongodb.Mongo;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mysema.query.NonUniqueResultException;
|
||||
import com.mysema.query.SearchResults;
|
||||
import com.mysema.query.mongodb.domain.Address;
|
||||
@ -50,9 +54,11 @@ import com.mysema.query.types.path.StringPath;
|
||||
|
||||
public class MongodbQueryTest {
|
||||
|
||||
private final Mongo mongo;
|
||||
private final Morphia morphia;
|
||||
private final Datastore ds;
|
||||
|
||||
private final String dbname = "testdb";
|
||||
private final Morphia morphia = new Morphia().map(User.class).map(Item.class);
|
||||
private final Datastore ds = morphia.createDatastore(dbname);
|
||||
private final QUser user = QUser.user;
|
||||
private final QItem item = QItem.item;
|
||||
private final QAddress address = QAddress.address;
|
||||
@ -60,8 +66,15 @@ public class MongodbQueryTest {
|
||||
User u1, u2, u3, u4;
|
||||
City tampere, helsinki;
|
||||
|
||||
public MongodbQueryTest() throws UnknownHostException, MongoException {
|
||||
mongo = new Mongo();
|
||||
morphia = new Morphia().map(User.class).map(Item.class);
|
||||
ds = morphia.createDatastore(mongo, dbname, null, null);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
public void before() throws UnknownHostException, MongoException {
|
||||
ds.delete(ds.createQuery(Item.class));
|
||||
ds.delete(ds.createQuery(User.class));
|
||||
|
||||
tampere = new City("Tampere", 61.30, 23.50);
|
||||
@ -354,10 +367,43 @@ public class MongodbQueryTest {
|
||||
|
||||
@Test
|
||||
public void Join() {
|
||||
User friend = new User();
|
||||
friend.setFirstName("Max");
|
||||
User friend1 = new User("Max", null);
|
||||
User friend2 = new User("Jack", null);
|
||||
User friend3 = new User("Bob", null);
|
||||
ds.save(friend1, friend2, friend3);
|
||||
|
||||
User user1 = new User("Jane", null, friend1);
|
||||
User user2 = new User("Mary", null, user1);
|
||||
User user3 = new User("Ann", null, friend3);
|
||||
ds.save(user1, user2, user3);
|
||||
|
||||
QUser friend = new QUser("friend");
|
||||
|
||||
// count
|
||||
assertEquals(1, where().join(user.friend(), friend).on(friend.firstName.eq("Max")).count());
|
||||
assertEquals(1, where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Max")).count());
|
||||
assertEquals(0, where(user.firstName.eq("Mary")).join(user.friend(), friend).on(friend.firstName.eq("Max")).count());
|
||||
assertEquals(0, where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Jack")).count());
|
||||
|
||||
// exists
|
||||
assertTrue(where().join(user.friend(), friend).on(friend.firstName.eq("Max")).exists());
|
||||
assertTrue(where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Max")).exists());
|
||||
assertFalse(where(user.firstName.eq("Mary")).join(user.friend(), friend).on(friend.firstName.eq("Max")).exists());
|
||||
assertFalse(where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Jack")).exists());
|
||||
|
||||
// list
|
||||
assertEquals(1, where().join(user.friend(), friend).on(friend.firstName.eq("Max")).list().size());
|
||||
assertEquals(1, where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Max")).list().size());
|
||||
assertEquals(0, where(user.firstName.eq("Mary")).join(user.friend(), friend).on(friend.firstName.eq("Max")).list().size());
|
||||
assertEquals(0, where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Jack")).list().size());
|
||||
|
||||
// single
|
||||
assertEquals("Jane", where().join(user.friend(), friend).on(friend.firstName.eq("Max")).singleResult().getFirstName());
|
||||
assertEquals("Jane", where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Max")).singleResult().getFirstName());
|
||||
assertNull(where(user.firstName.eq("Mary")).join(user.friend(), friend).on(friend.firstName.eq("Max")).singleResult());
|
||||
assertNull(where(user.firstName.eq("Jane")).join(user.friend(), friend).on(friend.firstName.eq("Jack")).singleResult());
|
||||
}
|
||||
|
||||
|
||||
//TODO
|
||||
// - test dates
|
||||
// - test with empty values and nulls
|
||||
|
||||
@ -56,6 +56,11 @@ public class User {
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(String firstName, String lastName, User friend) {
|
||||
this(firstName, lastName);
|
||||
this.friend = friend;
|
||||
}
|
||||
|
||||
public User(String firstName, String lastName) {
|
||||
this.firstName = firstName; this.lastName = lastName;
|
||||
this.created = new Date();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user