How to perform Sum on a Map Key in the Mongo DB document within Spring - spring

My MongoDB document looks something like as following:
{
"_class" : "com.foo.foo.FooClass",
"_id" : ObjectId("5441948f3004e65fbda72d9c"),
"actionType" : "LOGIN",
"actor" : "bolt",
"extraDataMap" : {
"workHours" : NumberLong(11869)
},
}
Where extraDataMap is a HashMap stored from the java code. I have to get all the documents where "actionType" is "Login", group on "actor" and sum all the "workHours" for those individual actors
If I do below query on MongoDB directly it works:
db.activityLog.aggregate([
{$match : { actionType : "LOGIN" }},
{$group : { "_id" : "$actor", "hours" : { "$sum" : "$extraDataMap.workHours" } } },
{$sort : {_id : 1}}
]);
But If I run the query from Java Code
TypedAggregation<ActivityLog> agg = Aggregation.newAggregation(ActivityLog.class,
buildCriteria(),
group("actor").sum("extraDataMap.workHours").as("hours"),
sort(Sort.Direction.ASC, MongoActivityLogRepository.DOCUMENT_ID_FIELD_NAME)
);
AggregationResults<ActivityLog> result = mongoOperations.aggregate(agg, ActivityLog.class);
List<ActivityLog> results = result.getMappedResults();
It gives below error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property work found for type java.lang.String
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:75)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:327)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:353)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:307)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:290)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:274)
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:245)
at org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext.getReference(TypeBasedAggregationOperationContext.java:91)
at org.springframework.data.mongodb.core.aggregation.GroupOperation$Operation.getValue(GroupOperation.java:359)
at org.springframework.data.mongodb.core.aggregation.GroupOperation$Operation.toDBObject(GroupOperation.java:355)
at org.springframework.data.mongodb.core.aggregation.GroupOperation.toDBObject(GroupOperation.java:300)
at org.springframework.data.mongodb.core.aggregation.Aggregation.toDbObject(Aggregation.java:228)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1287)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1264)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1253)
Really appreciate all the prompt responses :)

I had the same problem than you and I found this solution
Instead of using TypedAggregation, use a plain Aggregation. This way, spring data won't perform a type checking.
It would be as follows:
Aggregation agg = Aggregation.newAggregation(
buildCriteria(),
group("actor").sum("extraDataMap.workHours").as("hours"),
sort(Sort.Direction.ASC, MongoActivityLogRepository.DOCUMENT_ID_FIELD_NAME)
);
List<ActivityLog> results = mongoOperations.aggregate(agg, mongoOperations.getCollectionName(ActivityLog.class), ActivityLog.class).getMappedResults();
See that I used a different mongoOperations.aggregate signature, because since we are not using a TypedAggregation, we have to indicate over which collection we are performing the aggregation.
I hope this helps you.

Related

How to insert an element into already present list in elastic search

Say I have documents stored like below.
document 1
{
id : '1',
title : "This is a test document1",
list : ["value1" , "value2"],
...
}
document 2
{
id : '2',
title : "This is a test document2",
valueList : ["value1" , "value2"],
...
}
I need to add some more elements to the valueList in the documents with a list of document ids using bulk api. The resulting should look like
document 1
{
id : '1',
title : "This is a test document1",
list : ["value1" , "value2", "value3"],
...
}
document 2
{
id : '2',
title : "This is a test document2",
valueList : ["value1" , "value2" , "value3"],
...
}
What can I do to achieve this?
I tried using the scripts but it only updates a single document.
Sorry am really new to elastic search. I could even be stupid on this question. Please forgive and make me clear with this question.
See Updating Document. It should be straightforward. You need to use _update and just to give you an idea, even though the documentation is nearly perfect, it could look like this:
POST /your_index/your_type/document1/_update
{
id : '1',
title : "This is a test document1",
list : ["value1" , "value2", "value3"]
}
This will update document1.
In case of bulk updates you should read Batch Processing and have a look at the Bulk API.
From the docs:
POST /your_index/your_type/_bulk
{ "update" : {"_id" : "document1", "_type" : "your_type", "_index" : "your_index"}}
{ "doc" : {"myfield" : "newvalue"} }
{ "update" : {"_id" : "document2", "_type" : "your_type", "_index" : "your_index"}}
{ "doc" : {"myfield" : "newvalue"} }
Please note that you can just use _update for Partial Updates.
The simplest form of the update request accepts a partial document as
the doc parameter, which just gets merged with the existing document.
Objects are merged together, existing scalar fields are overwritten,
and new fields are added.

OrOperator in MongoTemplate not returning values?

I am working on Spring MVC with MongoDB.
I have added criteria for some fields with orOperator, Each Fields has value in database, but List returning is Empty.
criteriaQuery.addCriteria(Criteria.where("First_Name").is(docName.trim())
.orOperator(Criteria.where("Middle_Name").is(docName.trim())
.orOperator(Criteria.where("Last_Name").is(docName.trim()))));
Query printing in console is
Query: { "First_Name" : "ESTHER" , "$or" : [ { "Middle_Name" : "ESTHER" , "$or" : [ { "Last_Name" : "ESTHER"}]}]}, Fields: null, Sort: null
I tried the Like query, but that also results the same
criteriaQuery.addCriteria(Criteria.where("First_Name").regex(docName)
.orOperator(Criteria.where("Middle_Name").regex(docName))
.orOperator(Criteria.where("Last_Name").regex(docName)));
Mongo Query
db.doctor_details.find({ "$or" : {"First_Name" : { "$regex" : "ESTHER"} }, "$or" : [ { "Middle_Name" : { "$regex" : "ESTHER"} , "$or" : [ { "Last_Name" : { "$regex" : "ESTHER"}}]}]})
Help me to get the result, I dont know where my code goes wrong. Help is appreciated!
Thanks
As far as I remember in order to use orOperator you should do:
Query query = new Query(new Criteria().orOperator(criteriaV1,criteriaV2));
it is based on this answer

Elastic search Update by Query to Update Complex Document

I have a use case of elastic search to update a doc.
My doc is something like this-
{
"first_name" : "firstName",
"last_name" : "lastName",
"version" : 1234,
"user_roles" : {
"version" : 12345,
"id" : 1234,
"name" : "role1"},
},
"groups" : {
"version" : 123,
"list": [
{"id":123, "name" : "ashd"},
{"id":1234, "name" : "awshd"},
]
}
}
Now depepeding on some feed I will either will be updating the parent doc or will be updating the nested doc.
I am able to find how to update the basic attributes like firstName and lastName but unable to get how to update complex/nested ones
I did something like from REST client-
"script": {
"inline": "ctx._source.user_roles = { "id" : 5678, "name" :"hcsdl"}
}
but its giving me exception-
Actual use case-
I will actually be getting a Map in java.
This key can be simple key like "first_name" or can be complex key like "user_role" and "groups"
I want to update the document using update by query on version.
The code I wrote is something like-
for (String key : document.keySet()) {
String value = defaultObjectMapper.writeValueAsString(document.get(key));
scriptBuilder.append("ctx._source.");
scriptBuilder.append(key);
scriptBuilder.append('=');
scriptBuilder.append(value);
scriptBuilder.append(";");
}
where document is the Map
Now I might get the simple fields to update or complex object.
I tried giving keys like user_roles.id and user_roles.name and also tried giving complete user_roles but nothing is working.
Can someone helpout
Try this with groovy maps instead of verbatim JSON inside your script:
"script": {
"inline": "ctx._source.user_roles = [ 'id' : 5678, 'name' : 'hcsdl']}
}

elastic search filter by documents count in nested document

I have this schema in elastic search.
79[
'ID' : '1233',
Geomtries:[{
'doc1' : 'F1',
'doc2' : 'F2'
},
(optional for some of the documents)
{
'doc2' : 'F1',
'doc3' : 'F2'
}]
]
the Geometries is a nested element.
I want to get all of the documents that have one object inside Geometries.
Tried so far :
"script" : {"script" : "if (Geomtries.size < 2) return true"}
But i get exceptions : no such property GEOMTRIES
If you have the field as type nested in the mapping, the typical doc[fieldkey].values.size() approached does not seem to work. I found the following script to work:
{
"from" : 0,
"size" : <SIZE>,
"query" : {
"filtered" : {
"filter" : {
"script" : {
"script" : "_source.containsKey('Geomtries') && _source['Geomtries'].size() == 1"
}
}
}
}
}
NB: You must use _source instead of doc.
The problem is in the way you access fields in your script, use:
doc['Geometry'].size()
or
_source.Geometry.size()
By the way for performance reasons, I would denormalize and add GeometryNumber field. You can use the transform mapping to compute size at index time.

Check if value exists in mongodb

I'm trying to write a method that will check if a user_id is in the DB and then operate on the user_id.The portion of code that I'm stuck on is this:
DBObject query = new BasicDBObject(user_id,new BasicDBObject("$regex", true));
DBCursor result = dBcollection.find(query);
if (result.equals("true")) {
System.out.println("found");
//do stuff
}
else{
//do other stuff
}
My database is set up this way:
{ "_id" : { "$oid" : "53b4443ad121894f16ea3699"} , "user_id" : "1683777896" , "countries" : { "JA" : 1}}
{ "_id" : { "$oid" : "53b4443ad121894f16ea369a"} , "user_id" : "453121657" , "countries" : { "TU" : 1}}
I want to be able to query on the user_id and then operate on the record associated with that user_id but I can't figure out the correct syntax in java for the "if" statement.
Use this to query for the document with that user id:
DBObject query = new BasicDBObject("user_id", user_id);
Use this for the if statement to determine if the find actually found that user id:
if (result.hasNext())
If that doesn't go into the if statement, show us the code that gets the database and dBcollection, and make sure you are connecting to the right database and collection name.

Resources