I'm writing a dynamic MongoDB query using Aggregation and Criteria like so.
Aggregation aggregation;
AggregationResults<DataContentResult> result;
List<DataContentResult> theResult;
try {
aggregation = Aggregation.newAggregation(
buildMatchCriteriaForAggregation(publisherId, filter)
, buildGroupOperationForAggregation());
result = mongoTemplate.aggregate(aggregation, DataContent.class, DataContentResult.class);
theResult = result.getMappedResults();
} catch (Exception e) {
e.printStackTrace();
}
#SuppressWarnings("static-access")
private AggregationOperation buildMatchCriteriaForAggregation(Integer publisherId, Filter filterCriteria){
Criteria criteria = new Criteria();
criteria.where("publisherId").is(publisherId);
if (filterCriteria.getTa() != null) criteria.where("type").is(filterCriteria.getTa());
if (filterCriteria.getPlz() != null) criteria.where("zip").is(ilterCriteria.getPlz());
if (filterCriteria.getTbVerlag() != null) criteria.where("tbVerlagNr").is(filterCriteria.getTbVerlag());
return Aggregation.match(criteria);
}
Now I know that I am supposed to access Criteria methods in a static way like Criteria.where("something").is("something"). But in my case, I need to specify my Filter criterion depending on their availability. And therefore I have to do it as above. This isn't working and the criteria object ends up empty every time for some reason.
What alternative method is recommended in this situation? Or is it that I'm doing something wrong?
Alright so the answer turned out to be quite simple as follows:
Criteria criteria = new Criteria();
criteria = criteria.where("publisherId").is(publisherId);
if(<condition>) criteria = criteria.orOperator(
Criteria.where("something").is("something"));
Related
I'm trying to return random element in Spring using Query.
I have this:
#Override
public List<AdventureHolidays> findRandomTrekking() {
Query query = new Query();
query.addCriteria(Criteria.where("typeOfAdventureHolidays").is("trekking"));
return mongoTemplate.find(query, AdventureHolidays.class);
}
But this return me all elements that match my criteria,
I tried with:
return mongoTemplate.findOne(query, AdventureHolidays.class); but then I have required type List provided AdventureHoliday
Also I was using and tried with this, but on this way elements appear twice sometimes:
#Aggregation(pipeline = {"{'$match':{'typeOfAdventureHolidays':'trekking'}}", "{$sample:
{size:1}}"})
So I find a way with this Query, but its listing me all documents while I want just one random from collection
After some discussion this is what OP asked for:
private static Queue<AdventureHolidays> elementsToReturn = new LinkedList<>();
public AdventureHolidays findRandomTrekking() {
if (elementsToReturn.size() == 0) { //fetch data from db
Query query = new Query();
query.addCriteria(Criteria.where("typeOfAdventureHolidays")
.is("trekking"));
List<AdventureHolidays> newData = mongoTemplate.find(query, AdventureHolidays.class)
Collections.shuffle(newData);
elementsToReturn.addAll(newData);
}
return elementsToReturn.poll(); //this will crash if database is empty
}
Original answer.
You need to change return type of a method:
public AdventureHolidays findRandomTrekking() {
Query query = new Query();
query.addCriteria(Criteria.where("typeOfAdventureHolidays").is("trekking"));
return mongoTemplate.findOne(query, AdventureHolidays.class);
}
I have a function that returns a list of templates(unsorted); can someone tell me how I can get the list in a sorted format?
public List<FormTemplate> listDomainTemplates(Integer id) {
Domain domain = domainService.getDomain(id);
if (domain == null) {
return new ArrayList<>();
}
CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
CriteriaQuery<FormTemplate> query = cb.createQuery(FormTemplate.class);
Root<FormTemplate> application = query.from(FormTemplate.class);
query.select(application);
Predicate predicate = cb.equal(application.get("domain"), domain);
query.where(predicate);
Query<FormTemplate> q = sessionFactory.getCurrentSession().createQuery(query);
return q.getResultList();
}
Please experiment with following:
query.orderBy(cb.asc(application.get(...));
... - should point to FormTemplate field to sort, I suppose.
I am trying to add multiple 'and' conditions to criteria in Spring Data but not able to figure out what am I doing wrong.
Please refer the following code :
Criteria criteria = new Criteria();
criteria.andOperator(Criteria.where("siteCode").is(siteCode));
if(paymentMode != null) {
criteria.andOperator(Criteria.where("paymentMode").is(paymentMode));
}
if(planCode != null) {
criteria.andOperator(Criteria.where("packageCode").is(planCode));
}
if(status){
criteria.andOperator(Criteria.where("expiryDateTime").gt(new Date()));
} else {
criteria.andOperator(Criteria.where("expiryDateTime").lte(new Date()));
}
Query query = new Query(criteria);
List<UserPackage> userPackageList = mongoTemplate.find(query, UserPackage.class);
I found out what I was doing wrong. You don't need to use method 'andOperator' and another Criteria inside.
It would simply work by using 'and' method and then chain the following operator method you want to use.
Sharing the working solution to help other newcomers like me.
Criteria criteria = new Criteria();
criteria = criteria.and("siteCode").is(siteCode);
if (paymentMode != null) {
criteria = criteria.and("paymentMode").is(paymentMode);
}
if (planCode != null) {
criteria = criteria.and("packageCode").is(planCode);
}
if (status) {
criteria = criteria.and("expiryDateTime").gt(new Date());
} else {
criteria = criteria.and("expiryDateTime").lte(new Date());
}
Query query = new Query(criteria);
Just in case if you want to add conditions for a date between and match another field, below is what I used.
Criteria criteria = Criteria.where("date").gte(from).andOperator(Criteria.where("date").lt(to));
if (StringUtils.isNotBlank(manager)) {
criteria = criteria.and("manager").is(manager);
}
I use spring-data-elasticsearch framework to get query result from elasticsearch server, the java code like this:
public void testQuery() {
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withFields("createDate","updateDate").withQuery(matchAllQuery()).withPageable(new PageRequest(0,Integer.MAX_VALUE)).build();
List<Entity> list = template.queryForList(searchQuery, Entity.class);
for (Entity e : list) {
System.out.println(e.getCreateDate());
System.out.println(e.getUpdateDate());
}
}
I get the raw query log in server, like this:
{"from":0,"size":10,"query":{"match_all":{}},"fields":["createDate","updateDate"]}
As per the query log, spring-data-elasticsearch will add size limit to the query. "from":0, "size":10, How can I avoid it to add the size limit?
You don't want to do this, you could use the findAll functionality on a repository that returns an Iterable. I think the best way to obtain all items is to use the scan/scroll functionality. Maybe the following code block can put you in the right direction:
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withIndices("customer")
.withTypes("customermodel")
.withSearchType(SearchType.SCAN)
.withPageable(new PageRequest(0, NUM_ITEMS_PER_SCROLL))
.build();
String scrollId = elasticsearchTemplate.scan(searchQuery, SCROLL_TIME_IN_MILLIS, false);
boolean hasRecords = true;
while (hasRecords) {
Page<CustomerModel> page = elasticsearchTemplate.scroll(scrollId, SCROLL_TIME_IN_MILLIS, CustomerModel.class);
if (page != null) {
// DO something with the records
hasRecords = (page.getContent().size() == NUM_ITEMS_PER_SCROLL);
} else {
hasRecords = false;
}
}
I have an entity loaded by Hibernate (via EntityManager):
User u = em.load(User.class, id)
This class is audited by Hibernate Envers. How can I load the previous version of a User entity?
Here's another version that finds the previous revision relative to a "current" revision number, so it can be used even if the entity you're looking at isn't the latest revision. It also handles the case where there isn't a prior revision. (em is assumed to be a previously-populated EntityManager)
public static User getPreviousVersion(User user, int current_rev) {
AuditReader reader = AuditReaderFactory.get(em);
Number prior_revision = (Number) reader.createQuery()
.forRevisionsOfEntity(User.class, false, true)
.addProjection(AuditEntity.revisionNumber().max())
.add(AuditEntity.id().eq(user.getId()))
.add(AuditEntity.revisionNumber().lt(current_rev))
.getSingleResult();
if (prior_revision != null)
return (User) reader.find(User.class, user.getId(), prior_revision);
else
return null
}
This can be generalized to:
public static T getPreviousVersion(T entity, int current_rev) {
AuditReader reader = AuditReaderFactory.get(JPA.em());
Number prior_revision = (Number) reader.createQuery()
.forRevisionsOfEntity(entity.getClass(), false, true)
.addProjection(AuditEntity.revisionNumber().max())
.add(AuditEntity.id().eq(((Model) entity).id))
.add(AuditEntity.revisionNumber().lt(current_rev))
.getSingleResult();
if (prior_revision != null)
return (T) reader.find(entity.getClass(), ((Model) entity).id, prior_revision);
else
return null
}
The only tricky bit with this generalization is getting the entity's id. Because I'm using the Play! framework, I can exploit the fact that all entities are Models and use ((Model) entity).id to get the id, but you'll have to adjust this to suit your environment.
maybe this then (from AuditReader docs)
AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);
List<Number> revNumbers = reader.getRevisions(User.class, user_rev1);
User user_previous = reader.find(User.class, user_rev1.getId(),
revNumbers.get(revNumbers.size()-1));
(I'm very new to this, not sure if I have all the syntax right, maybe the size()-1 should be size()-2?)
I think it would be this:
final AuditReader reader = AuditReaderFactory.get( entityManagerOrSession );
// This could probably be declared as Long instead of Object
final Object pk = userCurrent.getId();
final List<Number> userRevisions = reader.getRevisions( User.class, pk );
final int revisionCount = userRevision.size();
final Number previousRevision = userRevisions.get( revisionCount - 2 );
final User userPrevious = reader.find( User.class, pk, previousRevision );
Building off of the excellent approach of #brad-mace, I have made the following changes:
You should pass in your EntityClass and Id instead of hardcoding and assuming the Model.
Don't hardcode your EntityManager.
There is no point setting selectDeleted, because a deleted record can never be returned as the previous revision.
Calling get single result with throw and exception if no results or more than 1 result is found, so either call resultlist or catch the exception (this solution calls getResultList with maxResults = 1)
Get the revision, type, and entity in one transaction (remove the projection, use orderBy and maxResults, and query for the Object[3] )
So here's another solution:
public static <T> T getPreviousRevision(EntityManager entityManager, Class<T> entityClass, Object entityId, int currentRev) {
AuditReader reader = AuditReaderFactory.get(entityManager);
List<Object[]> priorRevisions = (List<Object[]>) reader.createQuery()
.forRevisionsOfEntity(entityClass, false, false)
.add(AuditEntity.id().eq(entityId))
.add(AuditEntity.revisionNumber().lt(currentRev))
.addOrder(AuditEntity.revisionNumber().desc())
.setMaxResults(1)
.getResultList();
if (priorRevisions.size() == 0) {
return null;
}
// The list contains a single Object[] with entity, revinfo, and type
return (T) priorRevision.get(0)[0];
}
From the docs:
AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);