Filter Search query in Spring Mongo DB - spring

In feed collection "likeCount" and "commentCount" are two column. I want to get all document where "likeCount" + "commentCount" greater than 100. How can I write the search filter query in Spring Mongo DB?
Below is my sample feed collection data.
{
"_id" : ObjectId("55deb33dcb9be727e8356289"),
"channelName" : "Facebook",
"likeCount" : 2,
"commentCount" : 10,
}
For compare single field we can write search query like :
BasicDBObject searchFilter = new BasicDBObject();
searchFilter.append("likeCount", new BasicDBObject("$gte",100));
DBCursor feedCursor = mongoTemplate.getCollection("feed").find(searchFilter);

Try this
db.collection.aggregate([{$project:{total:{'$add':["$likeCount","$commentCount"]}}},{$match:{total:{$gt:100}}}])

You would need to use the MongoDB Aggregation Framework with Spring Data MongoDB. In Spring Data the following returns all feeds with a combined likes and comments counts greater than 100, using the aggregation framework. :
Entities
class FeedsCount {
#Id String id;
String channelName;
long likeCount;
long commentCount;
long totalLikesComments;
//...
}
Aggregation
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = newAggregation(Feed.class,
project("id", "channelName", "likeCount", "commentCount")
.andExpression("likeCount + commentCount").as("totalLikesComments"),
match(where("totalLikesComments").gt(100))
);
//Convert the aggregation result into a List
AggregationResults<FeedsCount> groupResults
= mongoTemplate.aggregate(agg, FeedsCount.class);
List<FeedsCount> results = groupResults.getMappedResults();
In the code above, first create a new aggregation via the newAggregation static factory method to which you pass a list of aggregation operations. These aggregate operations define the aggregation pipeline of your Aggregation.
As a first step, select the "id", "channelName", "likeCount", "commentCount" fields from the input collection with the project operation and add a new field "totalLikesComments" which is a computed property that stores the sum of the "likeCount" and "commentCount" fields.
Finally in the second step, filter the intermediate result by using a match operation which accepts a Criteria query as an argument.
Note that you derive the name of the input-collection from the Feed-class passed as first parameter to the newAggregation-Method.

Related

Spring Data elastic search with out entity fields

I'm using spring data elastic search, Now my document do not have any static fields, and it is accumulated data per qtr, I will be getting ~6GB/qtr (we call them as versions). Lets say we get 5GB of data in Jan 2021 with 140 columns, in the next version I may get 130 / 120 columns, which we do not know, The end user requirement is to get the information from the database and show it in a tabular format, and he can filter the data. In MongoDB we have BasicDBObject, do we have anything in springboot elasticsearch
I can provide, let say 4-5 columns which are common in every version record and apart from that, I need to retrieve the data without mentioning the column names in the pojo, and I need to use filters on them just like I can do in MongoDB
List<BaseClass> getMultiSearch(#RequestBody Map<String, Object>[] attributes) {
Query orQuery = new Query();
Criteria orCriteria = new Criteria();
List<Criteria> orExpression = new ArrayList<>();
for (Map<String, Object> accounts : attributes) {
Criteria expression = new Criteria();
accounts.forEach((key, value) -> expression.and(key).is(value));
orExpression.add(expression);
}
orQuery.addCriteria(orCriteria.orOperator(orExpression.toArray(new Criteria[orExpression.size()])));
return mongoOperations.find(orQuery, BaseClass.class);
}
You can define an entity class for example like this:
public class GenericEntity extends LinkedHashMap<String, Object> {
}
To have that returned in your calling site:
public SearchHits<GenericEntity> allGeneric() {
var criteria = Criteria.where("fieldname").is("value");
Query query = new CriteriaQuery(criteria);
return operations.search(query, GenericEntity.class, IndexCoordinates.of("indexname"));
}
But notice: when writing data into Elasticsearch, the mapping for new fields/properties in that index will be dynamically updated. And there is a limit as to how man entries a mapping can have (https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-settings-limit.html). So take care not to run into that limit.

Spring MongoDB query with or operator and text search

How can i build this MongoDB query with Spring Criteria?
{
$or: [
{ "$text" : { "$search" : "570-11024" } },
{"productDetails.code": "572-R110"}
]
}
It combines a fulltext index search with normal Where criteria with an orOperator.
Query's orOperator(Criteria... criteria) method takes only Criteria and no TextCriteria and also no CriteriaDefinition interface.
Yeah you are right, in spring data mongo you could do this,
final TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matchingAny("570-11024");
final DBObject tc = textCriteria.getCriteriaObject();
final Criteria criteria = Criteria.where("productDetails.code").is("572-R110");
final DBObject co = criteria.getCriteriaObject();
BasicDBList or = new BasicDBList();
or.add(tc);
or.add(co);
DBObject qq = new BasicDBObject("$or", or);
// Use MongoTemplate to execute command
mongoTemplate.executeCommand(qq);
Yes, you currently cannot use the Query's orOperator method to combine Criteria and TextCriteria. A workaround involves converting both the Criteria and TextCriteria objects to its Document representations, adding it to a BasicDbList and then converting back to a "$or" Criteria object.
TextCriteria textCriteria = TextCriteria.forDefaultLanguage().matchingAny("570-11024");
Criteria criteria = Criteria.where("productDetails.code").is("572-R110");
BasicDBList bsonList = new BasicDBList();
bsonList.add(criteria.getCriteriaObject());
bsonList.add(textCriteria.getCriteriaObject());
Query query = new Query();
query.addCriteria(new Criteria("$or").is(bsonList));
mongoTemplate.find(query, YourEntity.class);
PS: Someone has raised this issue in the spring-data-mongodb repo with a proposed fix by
changing the parameter types of orOperator from Criteria to CriteriaDefinition.
https://github.com/spring-projects/spring-data-mongodb/issues/3895.

Spring data mongo db count nested objects with a specific condition

I have a document like that:
'subject' : {
'name' :"...."
'facebookPosts':[
{
date:"14/02/2017 20:20:03" , // it is a string
text:"facebook post text here",
other stuff here
}
]
}
and I want to count the facebookPosts within a specific objects that their date field contains e.g "23/07/2016".
Now, I do that by extracting all the documents and count in the client side (spring ) , But I think that's not efficient.
You need to aggregate your results.
final Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("facebookPosts.date").regex(REGEX)),
Aggregation.unwind("facebookPosts"),
Aggregation.group().count().as("count"));
Regex might not be the best solution, just an example.
unwind will split array into separate elements you can then count.
Create a class that will hold the count, something like:
public class PostCount {
private Long count;
// getters, setters
}
And then execute it like this:
AggregationResults<PostCount> postCount = mongoTemplate.aggregate(aggregation, Subject.class, PostCount.class);
long count = postCount.getMappedResults().get(0).getCount();

Mongo Aggregation of $where: function() using MongoTemplate in SpringBoot [duplicate]

i'm trying to compare two string in mongoDB spring Data.
My Code:
#GET
#Path("/reqvolatility")
#Produces(MediaType.APPLICATION_JSON)
public long getReqVolatility() throws JsonParseException, JsonMappingException, IOException{
String query = "{},{_id:0,levelId:1,reqID:1,creationTime:1,lastModified:1}";
Query query1 = new BasicQuery(query);
query1.addCriteria(Criteria.where("creationTime").ne("lastModified"));
long reqvolatility = getMongoOperation().count(query1,RequirmentVO.class);
return reqvolatility;
}
In the above code "creationTime" & "lastModified" columns in mongoDB.I'm comparing those two fields, but its not giving correct count.
Is this correct? if it is wrong, How can i compare two fileds?
Standard query operations do not compare the values of one field against another. In order to do this, you need to employ the JavaScript evaluation server side which can actually compare the two field values:
Assuming both fields are ISODate instances
BasicQuery query = new BasicQuery(
new BasicDBObject("$where", "this.creationTime.getTime() != this.lastModified.getTime()")
);

How to get distance - MongoDB Template Near function

I'm trying to find Near by places.
Below code is working fine.
But i'm not able to get actual distance of place from my given lat,lng.
Criteria criteria = new Criteria("coordinates")
.near(new Point(searchRequest.getLat(),searchRequest.getLng()));
Query query = new Query();
query.addCriteria(criteria);
query.addCriteria(criteriaName);
query.limit(5);
List<Place> ls = (List<Place>) mongoTemplate.find(query, Place.class);
You can do it with geoNear aggregation. In spring-data-mongodb GeoNearOperation is representing this aggregation.
Extend or create inherit Place class with field where you would like to have distance information (example with inheritance):
public class PlaceWithDistance extends Place {
private double distance;
public double getDistance() {
return distance;
}
public void setDistance(final double distance) {
this.distance = distance;
}
}
Instead of Criteria with Query use aggregation. Second argument of geoNear is name of field where distance should be set:
final NearQuery nearQuery = NearQuery
.near(new Point(searchRequest.getLat(), searchRequest.getLng()));
nearQuery.num(5);
nearQuery.spherical(true); // if using 2dsphere index, otherwise delete or set false
// "distance" argument is name of field for distance
final Aggregation a = newAggregation(geoNear(nearQuery, "distance"));
final AggregationResults<PlaceWithDistance> results =
mongoTemplate.aggregate(a, Place.class, PlaceWithDistance.class);
// results.forEach(System.out::println);
List<PlaceWithDistance> ls = results.getMappedResults();
Just to make it easier - associated imports:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.geoNear;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.GeoNearOperation;
import org.springframework.data.mongodb.core.query.NearQuery;
Walery Strauch's example was useful for me...
However I wanted to :
run aggregate query to get all the points in 2dsphere index with-in given distance in Kilometers or Meters. You can use Metrics.KILOMETERS & Metrics.MILES
collection name is not specified as part of pojo
I have 2dsphere index with old way of representation in MongoDB. I am using Mongo as sharded databased for Geo-Spatial queries. My nearSphere query (without aggregation) was failing only when there is a shard key added into the same collection where I have 2dsphere index.
After using below implementation with shard key in the same collection. I am successfully able to fetch the required data.
Here is the sample :
import org.springframework.data.geo.Metrics;
final NearQuery query = NearQuery.near(new Point(longitude, latitude), Metrics.KILOMETERS)
.num(limit)
.minDistance(distanceInKiloMeters)
.maxDistance(maxNearByUEDistanceInKiloMeters)
.spherical(true);
final Aggregation a = newAggregation(geoNear(query, "distance"));
final AggregationResults<PlaceWithDistance> results = offlineMongoTemplate.aggregate(a, "myCollectionName", PlaceWithDistance.class);
final List<PlaceWithDistance> measurements = new ArrayList<PlaceWithDistance>(results.getMappedResults());

Resources