/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.querydsl.mongodb; import static java.util.Arrays.asList; import static org.junit.Assert.*; import java.net.UnknownHostException; import java.util.*; import org.bson.types.ObjectId; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Morphia; import com.google.common.collect.Lists; import com.mongodb.BasicDBObject; import com.mongodb.Mongo; import com.mongodb.MongoException; import com.mongodb.ReadPreference; import com.querydsl.core.NonUniqueResultException; import com.querydsl.core.QueryResults; import com.querydsl.core.testutil.MongoDB; import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.ListPath; import com.querydsl.core.types.dsl.StringPath; import com.querydsl.mongodb.domain.*; import com.querydsl.mongodb.domain.User.Gender; import com.querydsl.mongodb.morphia.MorphiaQuery; @Category(MongoDB.class) public class MongodbQueryTest { private final Mongo mongo; private final Morphia morphia; private final Datastore ds; private final String dbname = "testdb"; private final QUser user = QUser.user; private final QItem item = QItem.item; private final QAddress address = QAddress.address; private final QMapEntity mapEntity = QMapEntity.mapEntity; private final QDates dates = QDates.dates; private final QCountry country = QCountry.country; List<User> users = Lists.newArrayList(); 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).map(MapEntity.class).map(Dates.class); ds = morphia.createDatastore(mongo, dbname); } @Before public void before() throws UnknownHostException, MongoException { ds.delete(ds.createQuery(Item.class)); ds.delete(ds.createQuery(User.class)); ds.delete(ds.createQuery(Country.class)); ds.delete(ds.createQuery(MapEntity.class)); tampere = new City("Tampere", 61.30, 23.50); helsinki = new City("Helsinki", 60.15, 20.03); u1 = addUser("Jaakko", "Jantunen", 20, new Address("Aakatu", "00100", helsinki), new Address("Aakatu1", "00100", helsinki), new Address("Aakatu2", "00100", helsinki)); u2 = addUser("Jaakki", "Jantunen", 30, new Address("Beekatu", "00200", helsinki)); u3 = addUser("Jaana", "Aakkonen", 40, new Address("Ceekatu","00300", tampere)); u4 = addUser("Jaana", "BeekkoNen", 50, new Address("Deekatu","00400",tampere)); // order users by lastname, firstname users = Lists.newArrayList(u3, u4, u2, u1); } @Test public void query1() { assertEquals(4L, query(user).fetchCount()); assertEquals(4L, query(User.class).fetchCount()); } @Test public void list_keys() { User u = where(user.firstName.eq("Jaakko")).fetch(user.firstName, user.mainAddress().street).get(0); assertEquals("Jaakko", u.getFirstName()); assertNull(u.getLastName()); assertEquals("Aakatu", u.getMainAddress().street); assertNull(u.getMainAddress().postCode); } @Test public void singleResult_keys() { User u = where(user.firstName.eq("Jaakko")).fetchFirst(user.firstName); assertEquals("Jaakko", u.getFirstName()); assertNull(u.getLastName()); } @Test public void uniqueResult_keys() { User u = where(user.firstName.eq("Jaakko")).fetchOne(user.firstName); assertEquals("Jaakko", u.getFirstName()); assertNull(u.getLastName()); } @Test public void list_deep_keys() { User u = where(user.firstName.eq("Jaakko")).fetchFirst(user.addresses.any().street); for (Address a : u.getAddresses()) { assertNotNull(a.street); assertNull(a.city); } } @Test public void between() { assertQuery(user.age.between(20, 30), u2, u1); assertQuery(user.age.goe(20).and(user.age.loe(30)), u2, u1); } @Test public void between_not() { assertQuery(user.age.between(20, 30).not(), u3, u4); assertQuery(user.age.goe(20).and(user.age.loe(30)).not(), u3, u4); } @Test public void contains() { assertQuery(user.friends.contains(u1), u3, u4, u2); } @Test public void contains2() { assertQuery(user.friends.contains(u4)); } @Test public void notContains() { assertQuery(user.friends.contains(u1).not(), u1); } @Test public void contains_key() { MapEntity entity = new MapEntity(); entity.getProperties().put("key", "value"); ds.save(entity); assertTrue(query(mapEntity).where(mapEntity.properties.get("key").isNotNull()).fetchCount() > 0); assertFalse(query(mapEntity).where(mapEntity.properties.get("key2").isNotNull()).fetchCount() > 0); assertTrue(query(mapEntity).where(mapEntity.properties.containsKey("key")).fetchCount() > 0); assertFalse(query(mapEntity).where(mapEntity.properties.containsKey("key2")).fetchCount() > 0); } @Test public void contains_key_not() { MapEntity entity = new MapEntity(); entity.getProperties().put("key", "value"); ds.save(entity); assertFalse(query(mapEntity).where(mapEntity.properties.get("key").isNotNull().not()).fetchCount() > 0); assertTrue(query(mapEntity).where(mapEntity.properties.get("key2").isNotNull().not()).fetchCount() > 0); assertFalse(query(mapEntity).where(mapEntity.properties.containsKey("key").not()).fetchCount() > 0); assertTrue(query(mapEntity).where(mapEntity.properties.containsKey("key2").not()).fetchCount() > 0); } @Test public void equals_ignore_case() { assertTrue(where(user.firstName.equalsIgnoreCase("jAaKko")).fetchCount() > 0); assertFalse(where(user.firstName.equalsIgnoreCase("AaKk")).fetchCount() > 0); } @Test public void equals_ignore_case_not() { assertTrue(where(user.firstName.equalsIgnoreCase("jAaKko").not()).fetchCount() > 0); assertTrue(where(user.firstName.equalsIgnoreCase("AaKk").not()).fetchCount() > 0); } @Test public void equals_and_between() { assertQuery(user.firstName.startsWith("Jaa").and(user.age.between(20, 30)), u2, u1); assertQuery(user.firstName.startsWith("Jaa").and(user.age.goe(20).and(user.age.loe(30))), u2, u1); } @Test public void equals_and_between_not() { assertQuery(user.firstName.startsWith("Jaa").and(user.age.between(20, 30)).not(), u3, u4); assertQuery(user.firstName.startsWith("Jaa").and(user.age.goe(20).and(user.age.loe(30))).not(), u3, u4); } @Test public void exists() { assertTrue(where(user.firstName.eq("Jaakko")).fetchCount() > 0); assertFalse(where(user.firstName.eq("JaakkoX")).fetchCount() > 0); assertTrue(where(user.id.eq(u1.getId())).fetchCount() > 0); } @Test public void find_by_id() { assertNotNull(where(user.id.eq(u1.getId())).fetchFirst() != null); } @Test public void notExists() { assertFalse(where(user.firstName.eq("Jaakko")).fetchCount() == 0); assertTrue(where(user.firstName.eq("JaakkoX")).fetchCount() == 0); } @Test public void uniqueResult() { assertEquals("Jantunen", where(user.firstName.eq("Jaakko")).fetchOne().getLastName()); } @Test(expected = NonUniqueResultException.class) public void uniqueResultContract() { where(user.firstName.isNotNull()).fetchOne(); } @Test public void singleResult() { where(user.firstName.isNotNull()).fetchFirst(); } @Test public void longPath() { assertEquals(2, query().where(user.mainAddress().city().name.eq("Helsinki")).fetchCount()); assertEquals(2, query().where(user.mainAddress().city().name.eq("Tampere")).fetchCount()); } @Test public void collectionPath() { assertEquals(1, query().where(user.addresses.any().street.eq("Aakatu1")).fetchCount()); assertEquals(0, query().where(user.addresses.any().street.eq("akatu")).fetchCount()); } @Test public void dates() { long current = System.currentTimeMillis(); int dayInMillis = 24 * 60 * 60 * 1000; Date start = new Date(current); ds.delete(ds.createQuery(Dates.class)); Dates d = new Dates(); d.setDate(new Date(current + dayInMillis)); ds.save(d); Date end = new Date(current + 2 * dayInMillis); assertEquals(d, query(dates).where(dates.date.between(start, end)).fetchFirst()); assertEquals(0, query(dates).where(dates.date.between(new Date(0), start)).fetchCount()); } @Test public void elemMatch() { // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1"}}} assertEquals(1, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1")).fetchCount()); // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1" , "postCode" : "00100"}}} assertEquals(1, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1"), address.postCode.eq("00100")).fetchCount()); // { "addresses" : { "$elemMatch" : { "street" : "akatu"}}} assertEquals(0, query().anyEmbedded(user.addresses, address).on(address.street.eq("akatu")).fetchCount()); // { "addresses" : { "$elemMatch" : { "street" : "Aakatu1" , "postCode" : "00200"}}} assertEquals(0, query().anyEmbedded(user.addresses, address).on(address.street.eq("Aakatu1"), address.postCode.eq("00200")).fetchCount()); } @Test public void indexedAccess() { assertEquals(1, query().where(user.addresses.get(0).street.eq("Aakatu1")).fetchCount()); assertEquals(0, query().where(user.addresses.get(1).street.eq("Aakatu1")).fetchCount()); } @Test public void count() { assertEquals(4, query().fetchCount()); } @Test public void order() { List<User> users = query().orderBy(user.age.asc()).fetch(); assertEquals(asList(u1, u2, u3, u4), users); users = query().orderBy(user.age.desc()).fetch(); assertEquals(asList(u4, u3, u2, u1), users); } @Test public void restrict() { assertEquals(asList(u1, u2), query().limit(2).orderBy(user.age.asc()).fetch()); assertEquals(asList(u2, u3), query().limit(2).offset(1).orderBy(user.age.asc()).fetch()); } @Test public void listResults() { QueryResults<User> results = query().limit(2).orderBy(user.age.asc()).fetchResults(); assertEquals(4L, results.getTotal()); assertEquals(2, results.getResults().size()); results = query().offset(2).orderBy(user.age.asc()).fetchResults(); assertEquals(4L, results.getTotal()); assertEquals(2, results.getResults().size()); } @Test public void emptyResults() { QueryResults<User> results = query().where(user.firstName.eq("XXX")).fetchResults(); assertEquals(0L, results.getTotal()); assertEquals(Collections.emptyList(), results.getResults()); } @Test public void eqInAndOrderByQueries() { assertQuery(user.firstName.eq("Jaakko"), u1); assertQuery(user.firstName.equalsIgnoreCase("jaakko"), u1); assertQuery(user.lastName.eq("Aakkonen"), u3); assertQuery(user.firstName.in("Jaakko","Teppo"), u1); assertQuery(user.lastName.in("Aakkonen","BeekkoNen"), u3, u4); assertQuery(user.firstName.eq("Jouko")); assertQuery(user.firstName.eq("Jaana"), user.lastName.asc(), u3, u4); assertQuery(user.firstName.eq("Jaana"), user.lastName.desc(), u4, u3); assertQuery(user.lastName.eq("Jantunen"), user.firstName.asc(), u2, u1); assertQuery(user.lastName.eq("Jantunen"), user.firstName.desc(), u1, u2); assertQuery(user.firstName.eq("Jaana").and(user.lastName.eq("Aakkonen")), u3); //This should produce 'and' also assertQuery(where(user.firstName.eq("Jaana"), user.lastName.eq("Aakkonen")), u3); assertQuery(user.firstName.ne("Jaana"), u2, u1); } @Test public void regexQueries() { assertQuery(user.firstName.startsWith("Jaan"), u3, u4); assertQuery(user.firstName.startsWith("jaan")); assertQuery(user.firstName.startsWithIgnoreCase("jaan"), u3, u4); assertQuery(user.lastName.endsWith("unen"), u2, u1); assertQuery(user.lastName.endsWithIgnoreCase("onen"), u3, u4); assertQuery(user.lastName.contains("oN"), u4); assertQuery(user.lastName.containsIgnoreCase("on"), u3, u4); assertQuery(user.firstName.matches(".*aa.*[^i]$"), u3, u4, u1); } @Test public void regexQueries_not() { assertQuery(user.firstName.startsWith("Jaan").not(), u2, u1); assertQuery(user.firstName.startsWith("jaan").not(), u3, u4, u2, u1); assertQuery(user.firstName.startsWithIgnoreCase("jaan").not(), u2, u1); assertQuery(user.lastName.endsWith("unen").not(), u3, u4); assertQuery(user.lastName.endsWithIgnoreCase("onen").not(), u2, u1); assertQuery(user.lastName.contains("oN").not(), u3, u2, u1); assertQuery(user.lastName.containsIgnoreCase("on").not(), u2, u1); assertQuery(user.firstName.matches(".*aa.*[^i]$").not(), u2); } @Test public void like() { assertQuery(user.firstName.like("Jaan")); assertQuery(user.firstName.like("Jaan%"), u3, u4); assertQuery(user.firstName.like("jaan%")); assertQuery(user.lastName.like("%unen"), u2, u1); } @Test public void like_not() { assertQuery(user.firstName.like("Jaan").not(), u3, u4, u2, u1); assertQuery(user.firstName.like("Jaan%").not(), u2, u1); assertQuery(user.firstName.like("jaan%").not(), u3, u4, u2, u1); assertQuery(user.lastName.like("%unen").not(), u3, u4); } @Test public void isNotNull() { assertQuery(user.firstName.isNotNull(), u3, u4, u2, u1); } @Test public void isNotNull_not() { assertQuery(user.firstName.isNotNull().not()); } @Test public void isNull() { assertQuery(user.firstName.isNull()); } @Test public void isNull_not() { assertQuery(user.firstName.isNull().not(), u3, u4, u2, u1); } @Test public void isEmpty() { assertQuery(user.firstName.isEmpty()); assertQuery(user.friends.isEmpty(), u1); } @Test public void isEmpty_not() { assertQuery(user.firstName.isEmpty().not(), u3, u4, u2, u1); assertQuery(user.friends.isEmpty().not(), u3, u4, u2); } @Test public void not() { assertQuery(user.firstName.eq("Jaakko").not(), u3, u4, u2); assertQuery(user.firstName.ne("Jaakko").not(), u1); assertQuery(user.firstName.matches("Jaakko").not(), u3, u4, u2); assertQuery(user.friends.isNotEmpty(), u3, u4, u2); } @Test public void or() { assertQuery(user.lastName.eq("Aakkonen").or(user.lastName.eq("BeekkoNen")), u3, u4); } @Test public void or_not() { assertQuery(user.lastName.eq("Aakkonen").or(user.lastName.eq("BeekkoNen")).not(), u2, u1); } @Test public void iterate() { User a = addUser("A", "A"); User b = addUser("A1", "B"); User c = addUser("A2", "C"); Iterator<User> i = where(user.firstName.startsWith("A")) .orderBy(user.firstName.asc()) .iterate(); assertEquals(a, i.next()); assertEquals(b, i.next()); assertEquals(c, i.next()); assertEquals(false, i.hasNext()); } @Test public void uniqueResultAndLimitAndOffset() { MorphiaQuery<User> q = query().where(user.firstName.startsWith("Ja")).orderBy(user.age.asc()); assertEquals(4, q.fetch().size()); assertEquals(u1, q.fetch().get(0)); } @Test public void references() { for (User u : users) { if (u.getFriend() != null) { assertQuery(user.friend().eq(u.getFriend()), u); assertQuery(user.friend().id.eq(u.getFriend().getId()), u); assertQuery(user.friend().ne(u.getFriend()), otherUsers(u)); assertQuery(user.friend().id.ne(u.getFriend().getId()), otherUsers(u)); } } } @Test public void references2() { for (User u : users) { if (u.getFriend() != null) { assertQuery(user.enemy().eq(u.getEnemy()), u); assertQuery(user.enemy().id.eq(u.getEnemy().getId()), u); assertQuery(user.enemy().ne(u.getEnemy()), otherUsers(u)); assertQuery(user.enemy().id.ne(u.getEnemy().getId()), otherUsers(u)); } } } private User[] otherUsers(User user) { List<User> list = Lists.newArrayList(); for (User u : users) { if (!u.equals(user)) { list.add(u); } } return list.toArray(new User[list.size()]); } @Test public void various() { ListPath<Address, QAddress> list = user.addresses; StringPath str = user.lastName; List<Predicate> predicates = new ArrayList<Predicate>(); predicates.add(str.between("a", "b")); predicates.add(str.contains("a")); predicates.add(str.containsIgnoreCase("a")); predicates.add(str.endsWith("a")); predicates.add(str.endsWithIgnoreCase("a")); predicates.add(str.eq("a")); predicates.add(str.equalsIgnoreCase("a")); predicates.add(str.goe("a")); predicates.add(str.gt("a")); predicates.add(str.in("a","b","c")); predicates.add(str.isEmpty()); predicates.add(str.isNotNull()); predicates.add(str.isNull()); predicates.add(str.like("a")); predicates.add(str.loe("a")); predicates.add(str.lt("a")); predicates.add(str.matches("a")); predicates.add(str.ne("a")); predicates.add(str.notBetween("a", "b")); predicates.add(str.notIn("a","b","c")); predicates.add(str.startsWith("a")); predicates.add(str.startsWithIgnoreCase("a")); predicates.add(list.isEmpty()); predicates.add(list.isNotEmpty()); for (Predicate predicate : predicates) { long count1 = where(predicate).fetchCount(); long count2 = where(predicate.not()).fetchCount(); assertEquals(predicate.toString(), 4, count1 + count2); } } @Test public void enum_eq() { assertQuery(user.gender.eq(Gender.MALE), u3, u4, u2, u1); } @Test public void enum_ne() { assertQuery(user.gender.ne(Gender.MALE)); } @Test public void in_objectIds() { Item i = new Item(); i.setCtds(Arrays.asList(ObjectId.get(), ObjectId.get(), ObjectId.get())); ds.save(i); assertTrue(where(item, item.ctds.contains(i.getCtds().get(0))).fetchCount() > 0); assertTrue(where(item, item.ctds.contains(ObjectId.get())).fetchCount() == 0); } @Test public void in_objectIds2() { Item i = new Item(); i.setCtds(Arrays.asList(ObjectId.get(), ObjectId.get(), ObjectId.get())); ds.save(i); assertTrue(where(item, item.ctds.any().in(i.getCtds())).fetchCount() > 0); assertTrue(where(item, item.ctds.any().in(Arrays.asList(ObjectId.get(), ObjectId.get()))).fetchCount() == 0); } @Test public void size() { assertQuery(user.addresses.size().eq(2), u1); } @Test public void size_not() { assertQuery(user.addresses.size().eq(2).not(), u3, u4, u2); } @Test public void readPreference() { MorphiaQuery<User> query = query(); query.setReadPreference(ReadPreference.primary()); assertEquals(4, query.fetchCount()); } @Test public void asDBObject() { MorphiaQuery<User> query = query(); query.where(user.firstName.eq("Bob"), user.lastName.eq("Wilson")); assertEquals( new BasicDBObject().append("firstName", "Bob").append("lastName", "Wilson"), query.asDBObject()); } @Test public void converter() { Country germany = new Country("Germany", Locale.GERMANY); ds.save(germany); Country fetchedCountry = query(Country.class).where(country.defaultLocale.eq(Locale.GERMANY)).fetchOne(); assertEquals(germany, fetchedCountry); } //TODO // - test dates // - test with empty values and nulls // - test more complex ands private void assertQuery(Predicate e, User ... expected) { assertQuery(where(e).orderBy(user.lastName.asc(), user.firstName.asc()), expected); } private void assertQuery(Predicate e, OrderSpecifier<?> orderBy, User ... expected) { assertQuery(where(e).orderBy(orderBy), expected); } private <T> MorphiaQuery<T> where(EntityPath<T> entity, Predicate... e) { return new MorphiaQuery<T>(morphia, ds, entity).where(e); } private MorphiaQuery<User> where(Predicate ... e) { return query().where(e); } private MorphiaQuery<User> query() { return new MorphiaQuery<User>(morphia, ds, user); } private <T> MorphiaQuery<T> query(EntityPath<T> path) { return new MorphiaQuery<T>(morphia, ds, path); } private <T> MorphiaQuery<T> query(Class<? extends T> clazz) { return new MorphiaQuery<T>(morphia, ds, clazz); } private void assertQuery(MorphiaQuery<User> query, User ... expected) { String toString = query.toString(); List<User> results = query.fetch(); assertNotNull(toString, results); if (expected == null) { assertEquals("Should get empty result", 0, results.size()); return; } assertEquals(toString, expected.length, results.size()); int i = 0; for (User u : expected) { assertEquals(toString, u, results.get(i++)); } } private User addUser(String first, String last) { User user = new User(first, last); ds.save(user); return user; } private User addUser(String first, String last, int age, Address mainAddress, Address... addresses) { User user = new User(first, last, age, new Date()); user.setGender(Gender.MALE); user.setMainAddress(mainAddress); for (Address address : addresses) { user.addAddress(address); } for (User u : users) { user.addFriend(u); } if (!users.isEmpty()) { user.setFriend(users.get(users.size() - 1)); user.setEnemy(users.get(users.size() - 1)); } ds.save(user); users.add(user); return user; } }