Using $slice in Spring MongoDB Repository with #Query - spring

I am using Spring Boot with MongoDB. In this project, I have the domain class ControlVariable, which has an array attribute named ControlVarEntries. I have been trying to use the operator $slice to get only the last five controlVarEntries of all controlVars when retrieving them in my own findAll method. This query works when tried in my Mongo client:
db.getCollection('controlVariable').find( {}, { controlVarEntries: { $slice: -5 } } )
However, I tried a similar approach in my Spring Boot project with the #Query annotation and I could not retrieve the same results. This, for instance, returns all the entries:
#Repository
public interface ControlVariableRepository extends MongoRepository<ControlVariable, String> {
#Query("{ controlVarEntries: { $slice: ?0 } }")
Page<ControlVariable> findAllLimitedNumberOfEntriesQuery(Pageable pageable, Integer numberOfEntries);
}
I would like to know if my query is wrong or if I am not using the proper syntax.
Thanks!

You have to identify the projection document in the #Query annotation.
Try
#Query(value="{}", fields = "{'controlVarEntries': { '$slice': ?0 } }")
Page<ControlVariable> findAllLimitedNumberOfEntriesQuery(Pageable pageable, Integer numberOfEntries);

Related

Returning only the first 10 record - Redis OM

I’m using Redis OM for spring boot, I am having trouble querying objects because it only returns the first 10 records.
Repository Class:
public interface RedisBillerRepository extends RedisDocumentRepository<Biller, Long> {
List<Biller> findByClientIds(String clientId);
}
Is there a way to return ALL the objects with the specific clientId? Not the first 10 only.
The only way which i found was with the interface Page. For example your Repository would look like this:
public interface RedisBillerRepository extends RedisDocumentRepository<Biller, Long> {
Page<Biller> findByClientIds(String clientId, Pageable pageable);
}
And your class could look like this
public class BillerService {
#Autowired
RedisBillerRepository redisBillerRepository;
public List<Biller> getAllClientsById(String clientId){
Pageable pageRequest = PageRequest.of(0, 500000);
Page<Biller> foundBillers = redisBillerRepository.findByClientIds(clientId, pageRequest);
List<Biller> billersAsList = foundBillers.getContent();
return billersAsList;
}
}
You have to set the limit for now.
I'm the author of the library... #member2 is correct. RediSearch currently has a default for the underlying FT.SEARCH (https://redis.io/commands/ft.search/) method of returning the first 10 records found. To override that, the only way to do so currently is to use the Pagination constructs in Spring.
I will expose a configuration parameter in upcoming versions to set the MAX globally.

Spring Data - Build where clause at runtime

In Spring Data, how can I append more conditions to an existing query?
For example, I have the CrudRepository below:
#RepositoryRestResource
public interface MyRep extends CrudRepository<MyObject, Long> {
#Query("from MyObject mo where mo.attrib1 = :attrib1")
List<MyObj> findMyObjects(String attrib1, String conditions);
}
At runtime, I will need to call "findMyObjects" with two params. The first param is obviously the value of attrib1. the second param will be a where clause that would be determined at runtime, for example "attrib2 like '%xx%' and attrib3 between 'that' and 'this' and ...". I know this extra where condition will be valid, but I don't know what attributes and conditions will be in it. Is there anyway to append this where clause to the query defined in the #Query annotation?
Unfortunately, no. There is no straightforward way to achieve that.
You'll want to use custom reporistory methods where you'll be able to inject an EntityManager and interact with EntityManager.createQuery(...) directly.
Alternatively, you can build dynamic queries using Specifications or QueryDsl.
I ended up injecting an EntityManager that I obtained in the rest controller. Posting what I did here for criticism:
The repository code:
#RepositoryRestResource
public interface MyRepo extends CrudRepository<MyObject, Long> {
default List<MyObject> findByRuntimeConditions(EntityManager em, String runtimeConditions) {
String mySql = "<built my sql here. Watch for sql injection.>";
List<MyObject> list = em.createQuery(mySql).getResultList();
return list
}
}
The Rest controller code:
#RestController
public class DataController {
#Autowired
EntityManager em;
// of course watch for sql injection
#RequestMapping("myobjects/{runtimeConditions}")
public List<MyObject> getMyObjects(#PathVariable String runtimeConditions) {
List<MyObject> list = MyRepo.findByRuntimeConditions(em, runtimeConditions);
return list;
}
}

Javers QueryBuilder Pagination Support

UseCase :: How to write query using javers query builder that can support pagination while fetching audit's logs for api's.
One possible way to write query is by using skip() and limit().
#Service
public class AuditService {
#Autowired
Javers javer;
public List<Change> fetchAudits(String auditer,Integer offset,Integer limit) {
return javer.findChanges(QueryBuilder.anyDomainObject().byAuthor(auditer).skip(offset).limit(limit).build());
}
}

Sorting with spring and querydsl

I'm trying to get sorting working in a #RepositoryRestResource where I'm creating a custom query a couple of querydsl interfaces but I seem to be missing something. The paging works but you can't sort on fields that have more than one word (shippedQty). Sorting on other fields works fine. Is this a PagingAndSortingRepository bug or do I have to do something else or multi-word fields?
#RepositoryRestResource(path = "/report", collectionResourceRel = "report", itemResourceRel = "report")
public interface ReportRepository extends PagingAndSortingRepository<Report, Long>, QueryDslPredicateExecutor<Report>,
QuerydslBinderCustomizer<QReport> {
#Override
default void customize(QuerydslBindings bindings, QReport report) {
bindings.including(
report.description,
report.item,
report.program,
report.shippedQty,
);
bindings.excludeUnlistedProperties(true);
SingleValueBinding<NumberPath<Integer>, Integer> numberPathContains = (path, value) -> path.stringValue().contains(value.toString());
bindings.bind(firstFill.description).first(StringPath::containsIgnoreCase);
bindings.bind(firstFill.item).first(StringPath::containsIgnoreCase);
bindings.bind(firstFill.program).first(StringPath::containsIgnoreCase);
bindings.bind(firstFill.shippedQty).as("shipped_qty").first(numberPathContains);
}
}
This sorts correctly:
http://localhost:8080/api/v1/report?page=0&size=5&sort=description,asc
This does not:
http://localhost:8080/api/v1/report?page=0&size=5&sort=shipped_qty,asc
I just ran into this problem myself. It turns out that Sort does not use the QueryDSL repository binding aliases, but instead uses the names of the "Q" entity pathes.

Case insensitive Query with Spring CrudRepository

With Spring CrudRepository Query; I want to select "DeviceType" entities with it's "name" property. But following query select the entitles on case sensitive manner. How I make it case insensitive way. Thanks.
public interface DeviceTypeRepository extends CrudRepository<DeviceType, Integer>, JpaSpecificationExecutor<DeviceType> {
public Iterable<DeviceType> findByNameContaining(String name);
}
Exactly as #Peter mentioned in the comment, just add IgnoreCase:
public interface DeviceTypeRepository
extends CrudRepository<DeviceType, Integer>, JpaSpecificationExecutor<DeviceType> {
public Iterable<DeviceType> findByNameContainingIgnoreCase(String name);
}
See documentation for a list of all supported keywords inside method names.
The following Spring data mongo query works for me. I would prefer to use List instead of Iterator
public interface DeviceTypeRepository extends CrudRepository<DeviceType,Integer>, JpaSpecificationExecutor<DeviceType> {
List<DeviceType> findByNameIgnoreCase(String name);
}
In my case adding IgnoreCase did not work at all.
I found that it is possible to provide options for the regular expression ,as well:
#Query(value = "{'title': {$regex : ?0, $options: 'i'}}")
Foo findByTitleRegex(String regexString);
The i option makes the query case-insensitive.
For those who uses custom JPA query Upper keyword and toUpperCase helps. The following code works for me
return entityManager.createQuery("select q from "table " q where upper(q.applicant)=:applicant")
.setParameter("applicant",applicant.toUpperCase().trim()).getSingleResult();

Resources