Specify multiple criteria's in spring mongo db query - mongodb

I am iterating over a list of key/value pairs and executing find for each key/value. Can I create a single query document to be kind of union in sql, So that there will be only one database call.
List<User> userList = new ArrayList<User>();
for (Map accounts:attributes) {
Query query = new Query();
List<Criteria> andCriteriaList = new ArrayList<Criteria>();
accounts.forEach((key, value) -> {
Criteria criteria = Criteria.where((String) key).is(value);
andCriteriaList.add(criteria);
});
query.addCriteria(new Criteria().andOperator(andCriteriaList.toArray(new Criteria[andCriteriaList.size()])));
if (mongoTemplate.exists(query, User.class)) {
userList.add((User)mongoTemplate.find(query, User.class));
//System.out.println(mongoTemplate.find(query, User.class));
}
Thanks,

You can refactor your code to create $or expressions. No explicit $and operator needed.
Something like
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()])));
List<User> userList = mongoOperations.find(orQuery, User.class);
This should output query like
{ "$or" : [{ "key1" : "value1", "key2" : "value2" }, { "key3" : "value3", "key4" : "value4" }] }

if you want to query multiple fields(field1, field2, ....) with values below is the solution
Query query = new Query();
List<Criteria> criteria = new ArrayList<>();
criteria.add(Criteria.where(field1).is(field1val));
criteria.add(Criteria.where(field2).is(field2val));
// you can add all your fields here as above
query.addCriteria(new Criteria().andOperator(criteria.toArray(new Criteria[criteria.size()])));
List<JSONObject> filteredVals = mongoOperations.find(query, JSONObject.class);
the above return filteredVals is JSONObject

Related

Build predicates for a postgres jsonb column with criteria builder exact match using JPA criteria

private void teamsCriteria(Root<Employee> root, CriteriaBuilder criteriaBuilder, List<Predicate> predicates) {
var teamsPredicateArr = new Predicate[filters.getTeams().size()];
for (var i = 0; i < filters.getTeams().size(); i++) {
teamsPredicateArr[i]=criteriaBuilder.like(criteriaBuilder.concat(root.get(teams), \\:\\:text), "%" + filters.getTeams().get(i) + "%");
}
var predicate = criteriaBuilder.or(teamsPredicateArr);
predicates.add(criteriaBuilder.and(predicate));
}
Example: I have jsonb column teams
{
"team": [
"DEFAULT"
]
}
{
"team": [
"EF"
]
}
If I execute above code I am getting both the teams
I want exact match of jsonb column value:
Expected result :I have to filter only "EF"

How to do OR inside of AND?

SQL query=>
where and ((subtype="dailyMessage" and registDate="today") or(subtype !="dailyMessage"))
i want to retrive this query by using mongodb.
i have this collection. (today is '2017-08-16T15:48:19.947Z')
{
"_id" : ObjectId("597768443b1fd6308c0350c0"),
"type" : "message",
"subtype" : "dailyMessage",
"message" : test1",
"registDate" : ISODate("2017-08-16T15:48:19.947Z")
},
{
"_id" : ObjectId("597768443b1fd6308c0350c1"),
"type" : "message",
"subtype" : "dailyPush",
"message" : test2",
"registDate" : ISODate("2017-07-25T15:48:19.947Z")
},
{
"_id" : ObjectId("597768443b1fd6308c0350c2"),
"type" : "message",
"subtype" : "dailyPush",
"message" : test3",
"registDate" : ISODate("2017-07-24T15:48:19.947Z")
}
here is my code(spring boot mongodb , using java 8)
List<String> listOfmessage = new ArrayList<String>();
listOfSubtype.add("dailyMessage");
criteria = Criteria.where("test").is("james");
criteria.andOperator(Criteria.where("registDate").gte(LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT)).lte(LocalDateTime.now()) , Criteria.where("subtype").in(listOfmessage) .orOperator(Criteria.where("subtype").nin(listOfmessage)));
my problem is that how can i insert orOperator on the andOperator?
As mentioned in the comment, the and outside the whole condition doesn't make sense. The below code is written for the following condition.
(condition1 AND condition2) OR condition3
Code:-
You may need to alter the below code to get the MongoOperations object based on your Spring configuration.
"register" is the collection name. You can change it accordingly as per your collection name.
public Boolean getRegisterData() {
MongoOperations mongoOperations = getMongoConnection();
List<String> listOfSubType = new ArrayList<String>();
listOfSubType.add("dailyMessage");
List<String> listOfMessage = new ArrayList<String>();
listOfMessage.add("test1");
Criteria criteriaSubTypeAndDate = new Criteria();
criteriaSubTypeAndDate.andOperator(Criteria.where("registDate")
.gte(LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT)).lte(LocalDateTime.now()),
Criteria.where("subtype").in(listOfSubType));
Criteria criteriaSubType = Criteria.where("subtype").ne("dailyMessage");
Criteria criteriaFull = new Criteria();
criteriaFull.orOperator(criteriaSubTypeAndDate, criteriaSubType);
Query query = new Query();
query.addCriteria(criteriaFull);
System.out.println(query);
mongoOperations.executeQuery(query, "register", new RegistryDocumentCallbackHandler());
return true;
}
Result set document processing class:-
processDocument() method will get executed for each document in the result set.
public class RegistryDocumentCallbackHandler implements DocumentCallbackHandler {
#Override
public void processDocument(DBObject dbObject) throws MongoException, DataAccessException {
System.out.println("Registry collections data ===>" + dbObject.toString());
}
}

Spring data mongo aggregation mapping to object

I want to map the results of an aggregation to an POJO without iterating through the raw results. The POJO is a field in the Collection on which I'm running the aggregation.
MatchOperation match = match(Criteria.where("drill").is(drill));
SortOperation sort = sort(DESC, "creationDate");
GroupOperation group = group("athlete").first("athlete").as("athlete");
LimitOperation limit = limit(10);
ProjectionOperation project = project("athlete");
Aggregation aggregation = newAggregation(match, sort, group, limit, project);
AggregationResults<Athlete> results = mongoTemplate.aggregate(aggregation, DrillResultInfo.class, Athlete.class);
List<Athlete> mappedResult = results.getMappedResults();
It returns the correct number of objects, but they have as the id the map of the object and the other properties are null.
The result:
id: { "_id" : { "$oid" : "57cd46780348276373579821"} , "_class" : "Athlete" , "firstName" : "Jenny" , "lastName" : "Smith" ....}
The rest of the properties are null.
The Collection:
public class DrillResultInfo {
#Id
private String id;
private Long resultId;
#DBRef
private Athlete athlete;
#DBRef
private Drill drill;
....
}
(.... represents left out data)
Update:
I've made some updates to the code to get it to work:
List<Athlete> respone = new ArrayList<>();
MatchOperation match = match(Criteria.where("drill").is(drill));
SortOperation sort = sort(DESC, "creationDate");
GroupOperation group = group("athlete");
LimitOperation limit = limit(5);
SkipOperation skip = skip(skipElements);
ProjectionOperation project = project("_id");
TypedAggregation<DrillResultInfo> agg = newAggregation(DrillResultInfo.class, match, sort, group, skip, limit, project);
AggregationResults<Object> results = mongoTemplate.aggregate(agg, Object.class);
List<Object> mappedResult = results.getMappedResults();
for (Object obj : mappedResult) {
Athlete ath = (Athlete) ((LinkedHashMap) obj).get("_id");
respone.add(ath);
}
return respone;
I would like to get rid of that for.

how to calculate average of a particular field in mongodb spring?

how to calculate average of a field in mongoDB and spring. we have $avg() function for terminal use but how to execute it with mongotemplate.
for example in
db.sales.aggregate(
[
{
$group:
{
_id: "$item",
avgAmount: { $avg: { $multiply: [ "$price", "$quantity" ] } },
avgQuantity: { $avg: "$quantity" }
}
}
]
)
we are calculating average here so how can we execute it with mongotemplate.
Now I am using a function to get average rating
i am using function like this..
public List getrating() {
TypedAggregation<RatingReviewModel> agg = newAggregation(RatingReviewModel.class,
group("hospitalid")
.avg("rating").as("avgrating")
);
AggregationResults<DBObject> result = operations.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();
return resultList;
}
but at the time of debugging resultList is Empty so it is returning nothing.
Suppose your Sale object is defined as:
class Sale {
String id;
String item;
double price;
int quantity;
}
Using the mongotemplate you would need a $project stage in the pipeline before hand to get the calculated fields, which can be a bit counter-intuitive because with the native MongoDB aggregation all is done in one $group operation pipeline rather than splitting the aggregation into two stages, thus:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
TypedAggregation<Sale> agg = newAggregation(Sale.class,
project("quantity")
.andExpression("price * quantity").as("totalAmount"),
group("item")
.avg("totalAmount").as("avgAmount")
.avg("quantity").as("avgQuantity")
);
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
List<DBObject> resultList = result.getMappedResults();
The above can also be achieved using the native Java Driver implementation:
ApplicationContext context = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
MongoOperations operation = (MongoOperations) context.getBean("mongoTemplate");
BasicDBList pipeline = new BasicDBList();
String[] multiplier = { "$price", "$quantity" };
pipeline.add(
new BasicDBObject("$group",
new BasicDBObject("_id", "$item")
.append("avgAmount", new BasicDBObject(
"$avg", new BasicDBObject(
"$multiply", multiplier
)
))
.append("avgQuantity", new BasicDBObject("$avg", "$quantity"))
)
);
BasicDBObject aggregation = new BasicDBObject("aggregate", "sales")
.append("pipeline", pipeline);
System.out.println(aggregation);
CommandResult commandResult = operation.executeCommand(aggregation);

How to update a inner/embedded document in a mongodb using mongotemplate

Can some one help me to write a code to update "coordinates". I was able to update the address but not the coordinates.
{
"_id": "2c9080e54b4ee7ac014b4ee8e5100000",
"_class": "com.myparking.dataservice.mongodb.documents.ParkingSiteDocument",
"address": {
"streetAddress": "bellandur",
"locality": "ORR",
"region": "bangalore",
"country": "india",
"postalCode": "560102"
},
"geoLocation": {
"coordinates": [ 12.934292, 77.680215 ],
"type": "Point"
}
}
My code goes like this: When I update the address it is working but I am not able to update the coordinates.
public ParkingSiteDocument updateParkingSite(final ParkingSpaceDTO pSpace) {
ParkingSiteDocument parkingSpace = null;
try{
// If the collection doesn't exist return.
if (!mongoTemplate.collectionExists(ParkingSiteDocument.class)) {
// return.
return null;
}
Query query = new Query();
// query to fetch the parking site based on the id.
query.addCriteria(Criteria.where("pSiteId").is(pSpace.getpSpaceId()));
parkingSpace = mongoTemplate.findOne(query, ParkingSiteDocument.class);
// If the parking space is not available return;
if(parkingSpace == null) {
return null;
}
// Update address and coordinates
Update update = new Update();
// Updating the address.
if(pSpace.getAddress() != null) {
Address newAddress = new Address();
newAddress.setCountry(pSpace.getAddress().getCountry());
newAddress.setLocality(pSpace.getAddress().getLocality());
newAddress.setPostalCode(pSpace.getAddress().getPostalCode());
newAddress.setRegion(pSpace.getAddress().getRegion());
newAddress.setStreetAddress(pSpace.getAddress().getStreetAddress());
// converting it into mongo document.
MongoConverter converter = mongoTemplate.getConverter();
DBObject newRec = (DBObject) converter.convertToMongoType(newAddress);
update.set("address", newRec);
}
// Update the geolocation coordinates
if(pSpace.getGeoCoordinates() != null) {
// creating new coordinates from the input DTO.
Double[] coordinates = new Double[]{pSpace.getGeoCoordinates().getLongitude(),
pSpace.getGeoCoordinates().getLatitude()};
MongoConverter converter = mongoTemplate.getConverter();
DBObject newRec = (DBObject) converter.convertToMongoType(coordinates);
update.set("geoLocation.coordinates", newRec);
}
// update query.
mongoTemplate.updateFirst(query, update, ParkingSiteDocument.class);
} catch(Exception e) {
logger.error(this.getClass().getSimpleName(), "updateParkingSite | Exception" + e.getMessage());
}
return parkingSpace;
}
if (!mongoTemplate.collectionExists(ParkingSiteDocument.class))//or document name
mongoTemplate.createCollection(ParkingSiteDocument.class);//or document name
DBCollection db=mongoTemplate.getCollection(ParkingSiteDocument.class);//document name
BasicDBObject updateDocument = new BasicDBObject();
DBObject update = new BasicDBObject("$set",
new BasicDBObject("geoLocation",
new BasicDBObject("coordinates", "12.934292,77.680215")));
BasicDBObject searchQuery= new BasicDBObject().append("_id", new ObjectId("54d1d939e4b044860afcdf6d"));
db.update(searchQuery, update);