script command map to related java code in MongoTemplate - java

I have an aggregation command in script console as below,
> db.examSessionCol.aggregate( [ { $group : { _id : "$examinationId", avgScore: {$avg: "$score"}, co
unt: { $sum: 1 } } } ] )
Here is the result,
{ "_id" : NumberLong(4), "avgScore" : 81.4, "count" : 5 }
{ "_id" : NumberLong(1), "avgScore" : 17.2, "count" : 40 }
I need to apply it into my java based project, I found there is little documents about usage of aggregation in MongoTemplate, could anyone help me how I can write the related java code with good performance?
Following is the return object:
public class ExamParticipatedItem {
protected int examinationId;
protected int count;
protected double avgScore;
}
And the method written by me but doesn't work currently,
public List<ExamParticipatedItem> getExamParticipatedItems() {
return mongoTemplate.aggregate(Aggregation.newAggregation(ExaminationSession.class, GroupOperationBuilde), ExamParticipatedItem.class, collectionName).getMappedResults();//it's not completed
}
Thanks in advance.

The right answer for this project and solution is from this document:
Aggregation aggregation = newAggregation(ExaminationSession.class,
group("examinationId").first("examinationId").as("examinationId").avg("score").as("avgScore").count().as("count"));
List<ExamParticipatedItem> aggregationresults = mongoTemplate.aggregate(aggregation, collectionName, ExamParticipatedItem.class)
.getMappedResults();

Related

Springboot: How to execute custom mongodb query by springboot?

I want to find a list of models matched either field nameEnglish or nameChinese by a keyword. I spent more than an hour googling but I cannot do it. Please help.
Springboot Mongo starter example https://spring.io/guides/gs/accessing-data-mongodb/
The custom query I want to execute and return a list result
db.mymodel.aggregate([
{
$match: {
$or :[
{ nameChinese: /門/ },
{ nameEnglish: /cocina/i }
]
}
},
{ $sort: {nameEnglish: 1} }
])
My best trial so far
interface MyModelRepository : MongoRepository<MyModel, String> {
#Query(value = "{ '\$match': { \$or: [ {'nameEnglish': { \$regex: ?0 } }, {'nameChinese': { \$regex: ?0 } } ] }")
fun findByMyQuery(name: String): List<MyModel>
}
For the regex, I also want it to be case insensitive.
I found the answer after a hot water shower ( and one day of googling and reading documents). Hope this help someone in the future.
#Query(value = "{ \$or: [ {'nameEnglish': { \$regex: ?0, \$options: 'i'}}, {'nameChinese': { \$regex: ?0 }}] }", sort = "{nameEnglish: 1}")

How to use join at a same type or index in ElasticSearch like solr

Now I got a problem as I need to query some data from a type(or index) named 'product', and I have three parameters( $p1, $p2, $p3) which can determine some catagories,then I need get all the product in those catagories.
I know how it can be done in MySQL like this:
select *
from product
where catagory in (
select catagory
from product
where p1 = $p1 and p2=$p2 and p3=$p3
)
And I know I can do this in solr like
{!join from=catagory to=catagory}p1:$p1 AND p2:$p2 AND p3:$p3
But I want to know how can I do this in ElasticSearch.
BTW,sorry for my pool English. I do appreciate for your help.
after I look through official docs, this can be resolved like this:
1. create your index with a join type
put /product
{
"mappings":{
"cata" : {
"properties" : {
"join_field": {
"type": "join",
"relations": {
"header": "line"
}
}
}
}
}
}
create a header data
post /product
{
"p1":"1",
"p2":"2",
"p3":"3",
"join_field":"header"
}
return with "_id:"xxx"
create a line data
post /cata?routing=xxx (! routing must be header's '_id',so is 'parent' below)
{
"p1":"1",
"p2":"1",
"p3":"1",
"other":"others...",
"join_field":{
"name": "line",
"parent": "xxx"
}
}
4. query data
post /cata/_search
{
"query": {
"has_parent" : {
"parent_type" : "header",
"query" : {
"has_child" : {
"type" : "line",
"query" : {
"term" : {
"other" : "others..."
}
}
}
}
}
}
}
the way above fix my problem

Spring Data Mongo Template - Counting an array

Mongo document:
{
"_id" : "1",
"array" : [
{
"item" : "item"
},
{
"item" : "item"
}
]
}
My mongo shell query looks like so:
db.getCollection('collectionName').aggregate(
{$match: { _id: "1"}},
{$project: { count: { $size:"$array" }}}
)
Is there anyway to implement this using the Mongo Template from Spring?
So far I have this:
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
ProjectionOperation project = new ProjectionOperation();
Aggregation aggregate = Aggregation.newAggregation(match, project);
mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
I think I am only missing the project logic but I'm not sure if it is possible to do $size or equivalent here.
It's quite possible, the $size operator is supported (see DATAMONGO-979 and its implementation here). Your implementation could follow this example:
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
Aggregation agg = new Aggregation(
match(where("_id").is("1")), //
project() //
.and("array") //
.size() //
.as("count")
);
AggregationResults<IntegerCount> results = mongoTemplate.aggregate(
agg, collectionName, Integer.class
);
List<IntegerCount> intCount = results.getMappedResults();
Please find below the sample code. You can change it accordingly for your requirement with collection name, collection name class and array field name.
MatchOperation match = new MatchOperation(Criteria.where("_id").is("1"));
Aggregation aggregate = Aggregation.newAggregation(match, Aggregation.project().and("array").project("size").as("count"));
AggregationResults<CollectionNameClass> aggregateResult = mongoOperations.aggregate(aggregate, "collectionName", <CollectionNameClass>.class);
if (aggregateResult!=null) {
//You can find the "count" as an attrribute inside "result" key
System.out.println("Output ====>" + aggregateResult.getRawResults().get("result"));
System.out.println("Output ====>" + aggregateResult.getRawResults().toMap());
}
Sample output:-
Output ====>[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}]
Output ====>{serverUsed=127.0.0.1:27017, waitedMS=0, result=[ { "_id" : "3ea8e671-1e64-4cde-bd78-5980049a772b" , "count" : 47}], ok=1.0}
You can write query as
Aggregation aggregate = Aggregation.newAggregation(Aggregation.match(Criteria.where("_id").is(1)),
Aggregation.project().and("array").size().as("count")); mongoTemplate.aggregate(aggregate, collectionName, Integer.class);
It will execute the following query { "aggregate" : "collectionName" , "pipeline" : [ { "$match" : { "_id" : 1}} , { "$project" : { "count" : { "$size" : [ "$array"]}}}]}

Aggregating using Spring mongo group after a project is not finding the correct field

I have a collection with a dataset that looks like:
{
"resource" : "10.10.10.10",
"statistics" : {
"connections" : 17
}
}
{
"resource" : "10.10.10.10",
"statistics" : {
"connections" : 24
}
}
I want to use Mongo's $group/$push mechanism to return a dataset that looks like:
{ "_id" : "10.10.10.10", "statTotals" : [ 17, 24 ] }
In Mongo shell, I can that by doing:
db.testcol.aggregate([ { "$project" : { "resource" : 1 , "total" : "$statistics.connections"}} , { "$group" : { "_id" : "$resource" , "statTotals" : { "$push" : "$total"}}} ])
Now I want to do this using Spring's Mongo data solution in Java. The operations I'm currently trying to use are:
ProjectionOperation projOper = Aggregation.project("resource").and("statistics.connections").as("total");
GroupOperation groupOper = Aggregation.group("resource").push("total").as("statTotals");
Unfortunately, this is generating a pipeline which looks like:
{ "aggregate" : "event" , "pipeline" : [ { "$project" : { "resource" : 1 , "total" : "$statistics.connections"}} , { "$group" : { "_id" : "$resource" , "statTotals" : { "$push" : "$statistics.connections"}}}]}
In the $group, the $push is being done against $statistics.connections instead of $total so the results come back blank.
Any help would be greatly appreciated on this. At first I thought this might be https://jira.spring.io/browse/DATAMONGO-1254 but I've tried using both 1.7.2 as well as 1.8.1 and get the same results.

mongo + spring data + aggragate sum

I am looking for a solution without spring data. My project requirement is to do without spring data.
To calculate the sum using aggregate function by mongo command, able to get output. But same by using spring data getting exception.
Sample mongo query :
db.getCollection('events_collection').aggregate(
{ "$match" : { "store_no" : 3201 , "event_id" : 882800} },
{ "$group" : { "_id" : "$load_dt", "event_id": { "$first" : "$event_id" }, "start_dt" : { "$first" : "$start_dt" }, "count" : { "$sum" : 1 } } },
{ "$sort" : { "_id" : 1 } },
{ "$project" : { "load_dt" : "$_id", "ksn_cnt" : "$count", "event_id" : 1, "start_dt" : 1, "_id" : 0 } }
)
Same thing done in java as,
String json = "[ { \"$match\": { \"store_no\": 3201, \"event_id\": 882800 } }, { \"$group\": { \"_id\": \"$load_dt\", \"event_id\": { \"$first\": \"$event_id\" }, \"start_dt\": { \"$first\": \"$start_dt\" }, \"count\": { \"$sum\": 1 } } }, { \"$sort\": { \"_id\": 1 } }, { \"$project\": { \"load_dt\": \"$_id\", \"ksn_cnt\": \"$count\", \"event_id\": 1, \"start_dt\": 1, \"_id\": 0 } } ]";
BasicDBList pipeline = (BasicDBList) JSON.parse(json);
System.out.println(pipeline);
AggregationOutput output = col.aggregate(pipeline);
exception is :
com.mongodb.CommandFailureException: { "serverUsed" : "somrandomserver/10.10.10.10:27001" , "errmsg" : "exception: pipeline element 0 is not an object" , "code" : 15942 , "ok" : 0.0}
Could someone please suggest how to use aggregate function with spring?
Try the following (untested) Spring Data MongoDB aggregation equivalent
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
MongoTemplate mongoTemplate = repository.getMongoTemplate();
Aggregation agg = newAggregation(
match(Criteria.where("store_no").is(3201).and("event_id").is(882800)),
group("load_dt")
.first("event_id").as("event_id")
.first("start_dt").as("start_dt")
.count().as("ksn_cnt"),
sort(ASC, previousOperation()),
project("ksn_cnt", "event_id", "start_dt")
.and("load_dt").previousOperation()
.and(previousOperation()).exclude()
);
AggregationResults<OutputType> result = mongoTemplate.aggregate(agg,
"events_collection", OutputType.class);
List<OutputType> mappedResult = result.getMappedResults();
As a first step, filter the input collection by using a match operation which accepts a Criteria query as an argument.
In the second step, group the intermediate filtered documents by the "load_dt" field and calculate the document count and store the result in the new field "ksn_cnt".
Sort the intermediate result by the id-reference of the previous group operation as given by the previousOperation() method.
Finally in the fourth step, select the "ksn_cnt", "event_id", and "start_dt" fields from the previous group operation. Note that "load_dt" again implicitly references an group-id field. Since you do not want an implicit generated id to appear, exclude the id from the previous operation via and(previousOperation()).exclude().
Note that if you provide an input class as the first parameter to the newAggregation method the MongoTemplate will derive the name of the input collection from this class. Otherwise if you don’t not specify an input class you must provide the name of the input collection explicitly. If an input-class and an input-collection is provided the latter takes precedence.

Categories