1
votes

I have found a scenario where the query produced when using QueryDSL does not work with mongodb but it does work with jpa. The code is part of a project on github.

The method is in https://github.com/corneil/spring-data-demo/blob/master/src/main/java/org/springframework/data/demo/service/LocationAndDeviceServiceImpl.java

Code: ​

private List<LocationUpdate> findLocationsFunctions(String deviceId, Date startDate, Date endDate) {
    logger.info("findLocations:" + deviceId + "," + startDate + "," + endDate);
    DeviceInfo device = deviceInfoRepository.findByDeviceId(deviceId);
    if (device == null) {
        throw new DataRetrievalFailureException("DeviceInfo:" + deviceId);
    }
    return locationUpdateRepository.findByDeviceAndLocTimeBetween(device, startDate, endDate);
}
private List<LocationUpdate> findLocationsQsl(String deviceId, Date startDate, Date endDate) {
    logger.info("findLocations:" + deviceId + "," + startDate + "," + endDate);
    DeviceInfo device = deviceInfoRepository.findByDeviceId(deviceId);
    if (device == null) {
        throw new DataRetrievalFailureException("DeviceInfo:" + deviceId);
    }
    List<LocationUpdate> result = new ArrayList<LocationUpdate>();
    Iterable<LocationUpdate> resultSet = locationUpdateRepository
            .findAll(locationUpdate.device.eq(device).and(locationUpdate.locTime.between(startDate, endDate)));
    for (LocationUpdate loc : resultSet) {
        result.add(loc);
    }
    return result;
}
public List<LocationUpdate> findLocations(String deviceId, Date startDate, Date endDate) {
    return findLocationsFunctions(deviceId, startDate, endDate);
    // return findLocationsQsl(deviceId, startDate, endDate);
}

When commenting the findLocationsFunctions and uncommenting findLocationsQsl you will be able to induce the problem. The normal tests in the project will execute the JPA code using embedded H2. You will need access to mondodb to execute the tests for the mongo profile.. The database.properties file contains the mongodb url. Code:

./gradlew test testMongo

I think the problem lies in how the QueryDSL predicate is converted in Mongo Query. When I did the mongoTemplate initially . Code:

List<LocationUpdate> locations = mongoTemplate .find(
   query(where("device").is(device).and("locTime").gte(startDate)
   .and("locTime").lte(endTime)), LocationUpdate.class);

it gave an exception 'Due To Limitations Of The BasicDBObject, You Can’t Add A Second $And" and had to change to:

Code:

List<LocationUpdate> locations = mongoTemplate .find(
   query(where("device").is(device).andOperator(
       where("locTime").gte(startDate),
      where("locTime").lte(endTime))), LocationUpdate.class);

I noticed that when using the finder method renders the following:

Code:

"locTime" : { 
    "$gt" : { "$date" : "2014-04-02T14:06:23.600Z"} , 
    "$lt" : { "$date" : "2014-04-02T14:06:23.931Z"}
}

The code in findLocationsQsl doesn't render any criteria related to locTime.

Test with Spring Data MongoDB 1.5.0 and QueryDSL 3.3.4

1
What do you mean by doesn't render any criteria related to locTime? Did you debug the Mongo java driver calls? How does the serialized query look like in the Querydsl case? - Timo Westkämper
The queries are logged as follows: Using Spring Data Repository findByDeviceAndLocTimeBetween find using query: { "device" : { "$ref" : "deviceInfo" , "$id" : { "$oid" : "53aead0e44aed74aa839299a"}} , "locTime" : { "$gt" : { "$date" : "2014-06-28T11:54:54.910Z"} , "$lt" : { "$date" : "2014-06-28T11:54:55.142Z"}}} Using findAll(locationUpdate.device.eq(device).and(locationUpdate.locTime.between(startDate, endDate))) findOne using query: { "deviceId" : "1234-4567-6789"} - Corneil du Plessis

1 Answers

0
votes

As also answered in the Querydsl issue tracker this looks like an issue in the Querydsl integration of Spring Data Mongodb. It looks like Spring Data serializes dates differently from the default Mongodb Java API Date serialization.