Mongodb query to Spring mongoTemplate - spring

i have this mongo query:
db.getCollection('My_collection_name').aggregate([
{ $project: { warehouses: { $objectToArray: "$outputVariables" } } },
{ $unwind: "$warehouses" },
{ $group: { _id: "$warehouses.k" }}
])
someone could help me to translate in spring mongoTemplate?
Thanks

The query should be write as below:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
...
AggregationOperation project = project()
.and(ObjectToArray.valueOfToArray("outputVariables")).as("warehouses");
AggregationOperation unwind = unwind("warehouses");
AggregationOperation group = Aggregation.group("warehouses.k");
Aggregation aggregation = Aggregation.newAggregation(
project,
unwind,
group);
String collectionName = "My_collection_name";
System.out.println("aggregation=" + aggregation);
this.mongoTemplate.aggregate(aggregation, collectionName, Output.class);
It generate output:
aggregation={ "aggregate" : "__collection__", "pipeline" : [{ "$project" : { "warehouses" : { "$objectToArray" : "$outputVariables" } } }, { "$unwind" : "$warehouses" }, { "$group" : { "_id" : "$warehouses.k" } }] }

Related

Translate ElasticSearch query to Nest c#

I need some help in creating an AggregationDictionary from the following elasticsearch query
GET organisations/_search
{
"size": 0,
"aggs": {
"by_country": {
"nested": {
"path": "country"
},
"aggs": {
"by_country2": {
"filter": {
"bool": {
"must": [
{
"term": {
"country.isDisplayed": "true"
}
}
]
}
},
"aggs": {
"by_country3": {
"terms": {
"field": "country.displayName.keyword",
"size": 9999
}
}
}
}
}
}
}
}
I managed to write this horrible piece of code which I am pretty sure it is wrong, I am totally new to this.
AggregationDictionary aggs = new AggregationDictionary()
{
{
"countries_step1",
new NestedAggregation("countries_step1")
{
Path = "country",
Aggregations = new AggregationDictionary()
{
{
"countries_step2",
new FilterAggregation("countries_step2")
{
Filter = new BoolQuery
{
Must = new QueryContainer[] {
new NestedQuery
{
Query = new TermQuery
{
Field = "country.isDisplayed",
Value = true
}
}
}
},
Aggregations = new AggregationDictionary
{
{
"countries_step3",
new TermsAggregation("countries_step3")
{
Field = "country.displayName.keyword",
Size = 9999
}
}
}
}
}
}
}
}
};
Can someone tell me if I am in the correct direction? I am using Nest 6.6.0. Is there any tool that helps with these translations?
What you have so far is pretty solid, but when you try to execute this aggregation with the following call
var searchAsync = await client.SearchAsync<Document>(s => s.Size(0).Aggregations(aggs));
you will get this error
{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "query malformed, empty clause found at [14:22]"
}
],
"type" : "illegal_argument_exception",
"reason" : "query malformed, empty clause found at [14:22]"
},
"status" : 400
}
Checking request which was sent to elasticsearch give us the answer why it happened
{
"aggs": {
"countries_step1": {
"aggs": {
"countries_step2": {
"aggs": {
"countries_step3": {
"terms": {
"field": "country.displayName.keyword",
"size": 9999
}
}
},
"filter": {}
}
},
"nested": {
"path": "country"
}
}
},
"size": 0
}
filter clause is empty, this is because you tried to used nested query but you didn't pass path parameter. We don't need nested query here (as shown in your example query), we can simplify the whole query to
var aggs = new AggregationDictionary()
{
{
"countries_step1",
new NestedAggregation("countries_step1")
{
Path = "country",
Aggregations = new AggregationDictionary()
{
{
"countries_step2",
new FilterAggregation("countries_step2")
{
Filter = new BoolQuery
{
Must = new QueryContainer[]
{
new TermQuery
{
Field = "country.isDisplayed",
Value = true
}
}
},
Aggregations = new AggregationDictionary
{
{
"countries_step3",
new TermsAggregation("countries_step3")
{
Field = "country.displayName.keyword",
Size = 9999
}
}
}
}
}
}
}
}
};
Now we have a valid request sent to elasticsearch.
There are a couple of things we can improve here:
1. Remove unnecessary bool query
Filter = new BoolQuery
{
Must = new QueryContainer[]
{
new TermQuery
{
Field = "country.isDisplayed",
Value = true
}
}
},
to
Filter =
new TermQuery
{
Field = "country.isDisplayed",
Value = true
},
2. Replace string field names
Usually, when doing calls from .Net there is some kind of POCO type which is helping us with writing strongly-typed requests to elasticsearch which helps us managing clean code and refactoring. With this, we can change field definition from
"country.displayName.keyword"
to
Infer.Field<Document>(f => f.Country.FirstOrDefault().DisplayName.Suffix("keyword"))
my types definition
public class Document
{
public int Id { get; set; }
[Nested]
public List<Country> Country { get; set; }
}
public class Country
{
public bool IsDisplayed { get; set; }
public string DisplayName { get; set; }
}
3. Consider using a fluent syntax
With NEST you can write queries in two ways: using object initializer syntax (which you did) or with help of fluent syntax. Have a look. Trying to write above query with the fluent syntax you will get something like
var searchResponse = await client.SearchAsync<Document>(s => s
.Size(0)
.Aggregations(a => a.Nested("by_country", n => n
.Path(p => p.Country)
.Aggregations(aa => aa
.Filter("by_country2", f => f
.Filter(q => q
.Term(t => t
.Field(field => field.Country.FirstOrDefault().IsDisplayed)
.Value(true)))
.Aggregations(aaa => aaa
.Terms("by_country3", t => t
.Field(field => field.Country.FirstOrDefault().DisplayName.Suffix("keyword"))
.Size(9999)
)))))));
which I find a little bit easier to follow and write, maybe it will be better for you as well.
As a final note, have a look into docs and check how you can debug your queries.
Hope that helps.

Spring Boot MongoDB Aggregation with ReplaceRoot / How to get the Most Recent Item in a Group

I want to get the result of this MongoDB query in a Spring Boot Application.
db.getCollection('contentSource').aggregate( [ { $sort: { "modified": -1 } },
{ $group: { _id: "$sourceId", cs: { $push: "$$ROOT" } }},
{ $replaceRoot: { newRoot: { $arrayElemAt: ['$cs', 0] } }} ] )
Does anyone know how to add the replaceRoot to my Aggregation?
SortOperation sortOperation = new SortOperation(new Sort(Sort.Direction.ASC,"modified"));
GroupOperation groupOperation = group("sourceId").push("$$ROOT").as("cs");
Aggregation aggregation = newAggregation( sortOperation , groupOperation);
AggregationOperation replaceRoot = Aggregation.replaceRoot().withValueOf(ArrayOperators.ArrayElemAt.arrayOf("cs").elementAt(0));
AggregationResults<Document> result = mongoTemplate.aggregate(aggregation,"contentSource", replaceRoot, Document.class);
return result.getMappedResults();

Spring data ElasticSearch #Query

Im struggling building this query with the #Query annotation for spring-data:
{
"bool" : {
"must" : [
{
"query_string" : {
"query" : "123",
}
},
{
"bool" : {
"should" : [
{
"term" : {
"username" : {
"value" : "admin"
}
}
},
{
"terms" : {
"groups" : ["abc"],
}
}
],
}
}
]
}
}
I tried this:
#Query("{\"bool\": {\"must\": [{\"query_string\": {\"query\": \"?0\"}}, {\"bool\": { \"should\": [ {\"term\" : {\"username\" : \"?2\"}}, {\"terms\" : {\"groups\" : \"?1\"} } ] } } ] }}")
Page<Device> findByTermAndGroupOrUser(String term, List<String> groups, String username, Pageable pageable);
But it fails with:
ParsingException[[terms] query does not support [groups]]
Tried changing the query to:
#Query("{\"bool\": {\"must\": [{\"query_string\": {\"query\": \"?0\"}}, {\"bool\": { \"should\": [ {\"term\" : {\"username\" : \"?2\"}}, {\"terms\" : {\"groups\" : [\"?1\"]} } ] } } ] }}")
Page<Device> findByTermAndGroupOrUser(String term, List<String> groups, String username, Pageable pageable);
This works but the groups seem not to be evaluated. Documents containing the given group are not found.
Same query built with QueryBuilder like this works (but I´m missing the spring-data paging in this case)
BoolQueryBuilder qb = QueryBuilders.boolQuery();
QueryBuilder should1 = QueryBuilders.termQuery("username", username);
QueryBuilder should2 = QueryBuilders.termsQuery("groups", groups);
BoolQueryBuilder should = qb.should(should1).should(should2);
QueryStringQueryBuilder termQuery = QueryBuilders.queryStringQuery(term);
BoolQueryBuilder must = QueryBuilders.boolQuery().must(termQuery).must(should);
SearchResponse searchResponse = elasticsearchTemplate.getClient()
.prepareSearch()
.setQuery(must)
.setFrom(pageable.getPageNumber() * pageable.getPageSize())
.setSize(pageable.getPageSize())
.get();
What am I doing wrong?

how to write criteria Query for Mongodb Group Aggregation

Mongodb $group :
db.careLogBean.aggregate([{
$unwind: "$comments"
}, {
$sort: {
"comments.time": -1
}
}, {
$group: {
_id: "$_id",
careGiverId: {
$first: "$careGiverId"
},
careGiverName: {
$first: "$careGiverName"
},
comments: {
$push: "$comments"
}
}
}])
In mongodb $group aggretion things... how to write in java criteria
query language...
You can try the below aggregation.
import static org.springframework.data.domain.Sort.Direction.DESC;
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation aggregation = newAggregation(
unwind("comments"),
sort(DESC, "comments.time"),
group("_id")
.first("careGiverId").as("careGiverId")
.first("careGiverName").as("careGiverName")
.push("comments").as("comments"));
List<BasicDBObject> dbObjects = mongoOperations.aggregate(aggregation , collection_name, BasicDBObject.class).getMappedResults();
You can adjust the last statement based on your set up.

New (2.6) $cond Aggregation Framework with c#?

I'm going crazy with this one...
I have this aggregation framework expression working like a charm in mongo shell:
{ $group :
{
_id : '$Code' ,
'Special' : { $sum : { $cond: [{ $eq: [ '$Special', 'Success']},1,0]}}
}
}
I need to do it in c#, I tried a lot of combinations but without success.
Has anyone any clue?
Thx
Give this a try:
var group = new BsonDocument
{
{
"$group",
new BsonDocument
{
{
"_id", "$Code"
},
{
"Special", new BsonDocument
{
{ "$sum", new BsonDocument
{
{"$cond", new BsonArray
{
new BsonDocument
{
{
"$eq", new BsonArray {"$Special", "Success"}
}
},
1,
0
}
}
}
}
}
}
}
}
};

Resources