I am getting a sql resultset in the form of flat data Which is in the following structure
L0|L1|L2|L3|value
n1|o1|p1|c1|3
n1|o1|p1|c2|2
n1|o1|p2|c1|1
n1|o2|p1|c1|0
n2|o2|p1|c1|5
Here L0,L1,L2,Value.. are column names and we can have more L's as well(it is dynamic)
I want it to convert into the following form
[{name:"n1",children:
[{name:o1,children:
[{name:"p1",children:
[{name:"c1",value:3},
{name:"c2",value:2}]
},{name:"p2",children:
[{name:"c1",value:"1"}]
}],]}.....
I want the result preferably in JSONArray or List structure.
Does anyone have algo or Code to do that in Java?
Thanks
Recursion is your friend.
The code below builds up the hierarchy from the input data into an intermediate model (A tree of "Nodes").
This is then turned into JSON using a recursive method...
import java.util.HashMap;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Stack {
public static void main(String[] args) {
// Can have as many columns as you want, always assume the last column is the value.
Object[][] dataFromResultSet = {
{ "n1", "o1", "p1", "c1", 3 },
{ "n1", "o1", "p1", "c2", 2 },
{ "n1", "o1", "p2", "c1", 1 },
{ "n1", "o2", "p1", "c1", 0 },
{ "n2", "o2", "p1", "c1", 5 }
};
Node root = new Node();
// Add all records to the hierachy
for (Object[] row : dataFromResultSet) {
addToHierachy(root, row);
}
// Convert hierachy to JSON
try {
JSONArray json = convertToJSON(root);
System.out.println(json);
} catch (JSONException e) {
System.out.println("Something went wrong converting hierachy to JSON");
e.printStackTrace();
}
}
private static void addToHierachy(Node root, Object[] row) {
Node current = root;
// Go through each column in the row
for(Object col : row) {
// If this column is a string, then it is a Branch node, not a value one
// (It might be better to iterate through the array using a counter instead
// and change this condition to say "if it isn't the last column"...)
if(col instanceof String) {
// Get (or create) the child node for this column
current = current.getOrCreateChild((String) col);
} else {
// Otherwise, set the value
current.setValue((Integer) col);
}
}
}
private static JSONArray convertToJSON(Node root) throws JSONException {
// Use recursion to build the result JSON
JSONArray array = new JSONArray();
// Starting at this root, go through all of the child entries
for(Map.Entry<String, Node> child : root.getChildren().entrySet()) {
Node childNode = child.getValue();
// New object for this entry...
JSONObject object = new JSONObject();
// Set the name
object.put("name", child.getKey());
// Set the value if it is present on this node
if(childNode.getValue() != null) {
object.put("value", childNode.getValue());
}
// Generate the child hierarchy if it has children
if(!childNode.getChildren().isEmpty()) {
JSONArray childHierachy = convertToJSON(childNode);
object.put("children", childHierachy);
}
array.put(object);
}
return array;
}
// Class used to build the hierarchy
static class Node {
// The map of children, LABEL -> NODE
private Map<String, Node> children = new HashMap<>();
// The value (kept as null if this does not have a value set)
private Integer value;
public Node getOrCreateChild(String key) {
Node node = children.get(key);
if(node == null) {
node = new Node();
children.put(key, node);
}
return node;
}
public Map<String, Node> getChildren() {
return children;
}
public Integer getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
}
Output:
[ {
"name" : "n1",
"children" : [ {
"name" : "o2",
"children" : [ {
"name" : "p1",
"children" : [ {
"name" : "c1",
"value" : 0
} ]
} ]
}, {
"name" : "o1",
"children" : [ {
"name" : "p2",
"children" : [ {
"name" : "c1",
"value" : 1
} ]
}, {
"name" : "p1",
"children" : [ {
"name" : "c1",
"value" : 3
}, {
"name" : "c2",
"value" : 2
} ]
} ]
} ]
}, {
"name" : "n2",
"children" : [ {
"name" : "o2",
"children" : [ {
"name" : "p1",
"children" : [ {
"name" : "c1",
"value" : 5
} ]
} ]
} ]
} ]
Related
I have try to create criteria that fetch from data base items.
Here is the code that fetches items from mongo db:
public List<Location> findByListOfId(List<String> locationsIds){
Query query = new Query();
query.addCriteria(Criteria.where("id").in(locationsIds));
return template.find(query, Location.class);
}
here is Location class defenition:
#Document("loaction")
#Data
public class Location {
#Id
private String id;
private long order;
private Date createdAt;
private Date updatedAt;
}
And here is the value of input(List locationsIds) in findByListOfId function:
List<String> locationsIds = {"5d4eee8047206b6d2df212bb","5d4eee8047206b6d2df212bb","5d4eee8047206b6d2df212bb"}
as you can see the input contains the same value three times.
The result that I get from findByListOfId function is a single item with id equal to 5d4eee8047206b6d2df212bb,
while I need to get the numbers of items with the same id as a number of times that exists with in variable(in my case I expect 3 fetched items with id = 5d4eee8047206b6d2df212bb ).
Any idea how this query can be created?
Not sure why you want to do it, but you can do it this way (in Mongo Query Language, you can then translate it in Java).
MongoDB Playground
db.collection.aggregate([
{
$match: {
key: {
$in: [
"5d4eee8047206b6d2df212bb",
"5d4eee8047206b6d2df212bb",
"5d4eee8047206b6d2df212bb"
]
}
}
},
{
"$addFields": {
"itemsArray": [
"5d4eee8047206b6d2df212bb",
"5d4eee8047206b6d2df212bb",
"5d4eee8047206b6d2df212bb"
]
}
},
{
"$unwind": "$itemsArray"
},
])
Using aggregation pipeline, you will add the array as a field using $addFields and then $unwind it (will give you x number of times).
I agree with others it's not something you want to do in production code, but I find the question interesting.
#Yahya's answer works with an assumption that the $match stage returns exactly 1 document.
The more generic pipeline to fetch exact number of documents regardless of how unique the key is and how many duplicates are in the query https://mongoplayground.net/p/546QnaFn4lV :
db.collection.aggregate([
{
$limit: 1
},
{
$project: {
_id: 1,
list: [
"5d4eee8047206b6d2df212bb",
"5d4eee8047206b6d2df212bb",
"6d4eee8047206b6d2df212bc",
"7d4eee8047206b6d2df212bd"
]
}
},
{
"$unwind": "$list"
},
{
"$lookup": {
"from": "collection",
"localField": "list",
"foreignField": "key",
"as": "match"
}
},
{
$project: {
match: {
$cond: [
{
$eq: [
"$match",
[]
]
},
[
{
_id: null,
"key": "$list"
}
],
"$match"
]
}
}
},
{
"$replaceWith": {
$first: "$match"
}
}
])
The first $project passes the list of requested ids to mongo.
The last $project stage returns "null" for requested ids that don't have a matching document.
Here is an aggregate query with required result:
Consider a collection with these documents:
{ _id: 1, a: 11 }
{ _id: 2, a: 22 }
{ _id: 3, a: 99 }
The query in mongo shell with input documents:
var INPUT_IDS = [ 1, 2, 1, 1 ]
db.collection.aggregate([
{
$match: {
_id: { $in: INPUT_IDS }
}
},
{
$group: {
_id: null,
docs: { $push: "$$ROOT" }
}
},
{
$project: {
docs: {
$map: {
input: INPUT_IDS,
as: "inid",
in: {
$let: {
vars: {
matched: {
$filter: {
input: "$docs", as: "doc", cond: { $eq: [ "$$inid", "$$doc._id" ] }
}
}
},
in: { $arrayElemAt: [ "$$matched", 0 ] }
}
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceWith: "$docs"
}
])
The output:
{ "_id" : 1, "a" : 11 }
{ "_id" : 2, "a" : 22 }
{ "_id" : 1, "a" : 11 }
{ "_id" : 1, "a" : 11 }
This is the string that I get which I want to parse as json and get the values of "s", "o", "c" and "p" .
{
"head": {
"vars": [
"s",
"c",
"o",
"p"
]
},
"results": {
"bindings": [
{
"s": {
"type": "uri",
"value": "http://example.org/data/window"
},
"c": {
"type": "uri",
"value": "http://www.w3.org/ns/sosa/FeatureOfInterest"
},
"o": {
"type": "uri",
"value": "http://example.org/data/window104state"
},
"p": {
"type": "uri",
"value": "http://www.w3.org/ns/ssn/hasProperty"
}
},
{
"s": {
"type": "uri",
"value": "http://example.org/data/earth"
},
"c": {
"type": "uri",
"value": "http://www.w3.org/ns/sosa/FeatureOfInterest"
},
"o": {
"type": "uri",
"value": "http://example.org/data/VCAB-DP1-BP-40location"
},
"p": {
"type": "uri",
"value": "http://www.w3.org/ns/sosa/hasSample"
}
}
]
}
}
This is the code I have tried so far:
JsonParser jsonParser = new JsonParser();
JsonElement element = jsonParser.parse(str);
JsonObject obj = element.getAsJsonObject();
JsonObject results = obj.get("results").getAsJsonObject();
for(Map.Entry<String, JsonElement> entry : results.entrySet()) {
JsonArray array = entry.getValue().getAsJsonObject().getAsJsonArray("bindings");
for (JsonElement jsonElement : array) {
for (Map.Entry<String, JsonElement> entry1 : jsonElement.getAsJsonObject().entrySet()) {
System.out.println("Key = " + entry1.getKey() + " Value = " + entry1.getValue() );
}
}
What I want to get is the values of the inside array as such:
"s": "http://example.org/data/earth"
"c": "http://www.w3.org/ns/sosa/FeatureOfInterest"
etc.
Instead I get an error:
Exception in thread "main" java.lang.IllegalStateException: Not a JSON Object: [{"s":{"type":"uri","value":"http://example.org/data/window"},"c":{"type":"uri","value":"http://www.w3.org/ns/sosa/FeatureOfInterest"},"o":{"type":"uri","value":"http://example.org/data/window104state"},
(the whole string).
UPDATE
Thanks to #Deadpool I managed to get the values but now I need to get the "inner" values of the bindings meaning the "value" part of each binding(s,c,p and o). I need only this part and not the "type" part.
This is the result thanks to #Deadpool:
Key = s Value = {"type":"uri","value":"http://example.org/data/window"}
Key = c Value = {"type":"uri","value":"http://www.w3.org/ns/sosa/FeatureOfInterest"}
Key = p Value = {"type":"uri","value":"http://www.w3.org/ns/ssn/hasProperty"}
Key = o Value = {"type":"uri","value":"http://example.org/data/window104state"}
SOLUTION
OK for those interested I managed to get it this the statement that was needed:
System.out.println("Key = " + entry1.getKey() + " Value = " + entry1.getValue().getAsJsonObject().get("value"));
And this is the desired result:
Key = s Value = "http://example.org/data/earth"
Key = c Value = "http://www.w3.org/ns/sosa/FeatureOfInterest"
Key = o Value = "http://example.org/data/VCAB-DP1-BP-40location"
Key = p Value = "http://www.w3.org/ns/sosa/hasSample"
The problem is in this statement bindings is a JsonArray, get it as JsonArray directly
JsonArray array = entry.getValue().getAsJsonObject().getAsJsonArray("bindings");
Solution
for(Map.Entry<String, JsonElement> entry : results.entrySet()) {
JsonArray array = entry.getValue().getAsJsonArray();
for (JsonElement jsonElement : array) {
for (Map.Entry<String, JsonElement> entry1 : jsonElement.getAsJsonObject().entrySet()) {
System.out.println("Key = " + entry1.getKey() + " Value = " + entry1.getValue() );
}
}
You can use Declarative Stream Mapping (DSM) stream parsing library to easily capture data from XML or JSON
First of all, you must define the mapping between JSON data and your fields in yaml or JSON format.
Here are the mapping definitions:
result:
type: array
path: /results/bindings/(s|c|o|p) // regex
fields:
key:
path: type
value:
Java class that you want to deserialize:
public class KeyValue{
public String key;
public String value
}
Java Code to parse JSON:
DSM dsm=new DSMBuilder(new File("path/to/mapping.yaml")).create(KeyValue.class);
List<KeyValue> list= (List<KeyValue>)dsm.toObject(jsonData);
// write root object as json
dsm.getObjectMapper().writerWithDefaultPrettyPrinter().writeValue(System.out, list);
Here is output:
[ {
"key" : "uri",
"value" : "http://example.org/data/window"
}, {
"key" : "uri",
"value" : "http://www.w3.org/ns/sosa/FeatureOfInterest"
}, {
"key" : "uri",
"value" : "http://example.org/data/window104state"
}, {
"key" : "uri",
"value" : "http://www.w3.org/ns/ssn/hasProperty"
}, {
"key" : "uri",
"value" : "http://example.org/data/earth"
}, {
"key" : "uri",
"value" : "http://www.w3.org/ns/sosa/FeatureOfInterest"
}, {
"key" : "uri",
"value" : "http://example.org/data/VCAB-DP1-BP-40location"
}, {
"key" : "uri",
"value" : "http://www.w3.org/ns/sosa/hasSample"
} ]
I currently have a class with several attributes describing a certain type of object in my system. This class is known as EnrollmentInfo.
I also have a hashmap that is structured as follows;
HashMap<EnrolmentInfo, List<EnrolmentInfo>> devices = new HashMap<>();
As it can be seen, the value properties in this hashmap contain an ArrayList of the EnrollmentInfo class type.
To provide some context, this hashmap is used to hold the parent nodes and associated child nodes of a tree structure as key, value pairs.
I generated this hashmap by traversing and extracting details from a child/parent table such as the following:
Child : Parent
1 : 0
2 : 0
3 : 2
4 : 0
5 : 4
6 : 4
7 : 1
8 : 6
The code for extracting the parents and children and putting them into the HashMap is as follows:
// Extracts the parents and assigns them to the key values
for (EnrolmentInfo enrolmentInfo : enrolmentInfos) {
Integer nodeParentId = enrolmentInfo.getParentId();
EnrolmentInfo parentEnrolmentInfo = dms.getDevice(nodeParentId).getEnrolmentInfo();
devices.put(parentEnrolmentInfo, new ArrayList<EnrolmentInfo>());
}
// Extracts the children and assigns them to the children arraylist of each associated parent.
for (EnrolmentInfo enrolmentInfo : enrolmentInfos) {
int nodeId = enrolmentInfo.getId();
Integer parentId = enrolmentInfo.getParentId();
EnrolmentInfo nodeEnrolmentInfo = dms.getDevice(nodeId).getEnrolmentInfo();
for (Map.Entry<EnrolmentInfo, List<EnrolmentInfo>> parentDevice : devices.entrySet()) {
if (parentDevice.getKey().getId() == parentId) {
parentDevice.getValue().add(nodeEnrolmentInfo);
break;
}
}
}
My issue now is to compose this hashmap into an actual tree structure such that it can be compiled into a human-readable form via a JSON library.
More specifically how can a nested tree structure be generated based on the HashMap mentioned above?
EDIT:
Shown below is an example structure of the kind of JSON format I'm expecting at the end.
{
"id" : 0,
"children" : [
{
"id" : 1,
"children" : [
{
"id" : 7,
"children" : []
}
]
},
{
"id" : 2,
"children" : [
{
"id" : 3,
"children" : []
}
]
},
{
"id" : 4,
"children" : [
{
"id" : 5,
"children" : []
},
{
"id" : 6,
"children" : [
{
"id" : 8,
"children" : []
}
]
}
]
}
]
}
EDIT:
So far I've created a bean class that is as follows:
public class DeviceHierarchyNode implements Serializable {
#ApiModelProperty(name = "id", value = "ID of the node generated. Same as Device ID",
required = true)
private int id;
#ApiModelProperty(name = "label", value = "Device name as suggested by the user.",
required = true)
private String label;
#ApiModelProperty(name = "children", value = "List of child devices associated with device if any",
required = true)
private List<DeviceHierarchyNode> children;
My plan is to use this to create the final nested structure.
Warning: hacky.
Could you create a node type that wraps your underlying:
public class EnrolmentInfoNode {
private EnrolmentInfo info;
private List<EnrolmentInfoNode> children;
public EnrolmentInfoNode(EnrolmentInfo contents) {
this.info = contents;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + info.getId();
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EnrolmentInfoNode other = (EnrolmentInfoNode) obj;
if (info.getId() != other.info.getId())
return false;
return true;
}
public void addChild(EnrolmentInfoNode child) {
if (children == null) {
children = new ArrayList<>();
}
children.add(child);
}
}
And then re-map thus:
Map<EnrolmentInfo, EnrolmentInfoNode> nodeMap = new HashMap<>();
for (Entry<EnrolmentInfo, List<EnrolmentInfo>> entry : map.entrySet()) {
for (EnrolmentInfo child : entry.getValue()) {
EnrolmentInfoNode childNode = nodeMap.computeIfAbsent(child, EnrolmentInfoNode::new);
nodeMap.computeIfAbsent(entry.getKey(), EnrolmentInfoNode::new)
.addChild(childNode);
}
}
Assuming you know node 0 is the parent:
String json = new GsonBuilder().setPrettyPrinting()
.create()
.toJson(nodeMap.get(enrolmentInfo0));
System.out.println(json);
If you don't, you can add a "parentNode" field to the EnrolmentInfoNode and then scan the node map to find the first one that has a null parent (therefore, root), and you're off to the races.
I'm new to working with MongoDb and do not know a lot of things.
I need to write an aggregation request.
Here is the JSON document structure.
{
"_id" : ObjectId("5a72f7a75ef7d430e8c462d2"),
"crawler_id" : ObjectId("5a71cbb746e0fb0007adc6c2"),
"skill" : "stack",
"created_date" : ISODate("2018-02-01T13:19:03.522+0000"),
"modified_date" : ISODate("2018-02-01T13:22:23.078+0000"),
"connects" : [
{
"subskill" : "we’re",
"weight" : NumberInt(1),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec11")
]
},
{
"subskill" : "b1",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec11"),
ObjectId("5a71d88d5ef7d41964fbec1b")
]
},
{
"subskill" : "making",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec1b"),
ObjectId("5a71d88d5ef7d41964fbec1c")
]
},
{
"subskill" : "delivery",
"weight" : NumberInt(2),
"parser_id" : [
ObjectId("5a71d88d5ef7d41964fbec1c"),
ObjectId("5a71d88d5ef7d41964fbec1e")
]
}
]
}
I need the result return the name of skill and the number of unique parser_id.
In this case, the result should be:
[
{
"skill": "stack",
"quantity": 4
}
]
where "stack" - skill name,
and "quantity" - count of unique parser_id.
ObjectId("5a71d88d5ef7d41964fbec11")
ObjectId("5a71d88d5ef7d41964fbec1b")
ObjectId("5a71d88d5ef7d41964fbec1c")
ObjectId("5a71d88d5ef7d41964fbec1e")
Can some one help me with this request ???
Given the document supplied in your question, this command ...
db.collection.aggregate([
{ $unwind: "$connects" },
// count all occurrences
{ "$group": { "_id": {skill: "$skill", parser_id: "$connects.parser_id"}, "count": { "$sum": 1 } }},
// sum all occurrences and count distinct
{ "$group": { "_id": "$_id.skill", "quantity": { "$sum": 1 } }},
// (optional) rename the '_id' attribute to 'skill'
{ $project: { 'skill': '$_id', 'quantity': 1, _id: 0 } }
])
... will return:
{
"quantity" : 4,
"skill" : "stack"
}
The above command groups by skill and connects.parser_id and then gets a distinct count of those groups.
Your command includes the java tag so I suspect you are looking to execute the same command using the MongoDB Java driver. The code below (using MongoDB Java driver v3.x) will return the same result:
MongoClient mongoClient = ...;
MongoCollection<Document> collection = mongoClient.getDatabase("...").getCollection("...");
List<Document> documents = collection.aggregate(Arrays.asList(
Aggregates.unwind("$connects"),
new Document("$group", new Document("_id", new Document("skill", "$skill").append("parser_id", "$connects.parser_id"))
.append("count", new Document("$sum", 1))),
new Document("$group", new Document("_id", "$_id.skill").append("quantity", new Document("$sum", 1))),
new Document("$project", new Document("skill", "$_id").append("quantity", 1).append("_id", 0))
)).into(new ArrayList<>());
for (Document document : documents) {
logger.info("{}", document.toJson());
}
Note: this code deliberately uses the form new Document(<pipeline aggregator>, ...) instead of the Aggregators utilities to make it easier to see the translation between the shell command and its Java equivalent.
try $project with $reduce
$setUnion is used to keep only the distinct ids and finally $size used to get the distinct array count
db.col.aggregate(
[
{$project : {
_id : 0,
skill : 1,
quantity : {$size :{$reduce : {input : "$connects.parser_id", initialValue : [] , in : {$setUnion : ["$$value", "$$this"]}}}}
}
}
]
).pretty()
result
{ "skill" : "stack", "quantity" : 4 }
Using ElasticSearch 5.2 and a group by is being done similer to
select city,institutionId, SUM(appOpenCount) from XYZ where ( time > 123 && appOpenCount > 0 ) group by city, institutionId.
I have it working when i do using curl method, but when the same is being converted to java api i am missing something that is causing me not get the last part of sum aggregation.
I have a type temp_type with mapping given below.
{
"temp_index" : {
"mappings" : {
"temp_type" : {
"properties" : {
"appOpenCount" : {
"type" : "integer"
},
"city" : {
"type" : "keyword"
}
"institutionId" : {
"type" : "keyword"
},
"time" : {
"type" : "long"
}
}
}
}
}
}
and my aggregation XGET call looks like this.
curl -XGET "http://localhost:9200/temp_index/temp_type/_search?pretty" -d'
{
"size":0,
"_source":false,
"from" : 0,
"query": {
"bool": {
"must": [
{"range": { "time": { "gte": 1513744603000 } } },
{ "range": { "appOpenCount": { "gt": 0 } } }
]
}
},
"aggregations": {
"city-aggs": {
"terms": { "field": "city"},
"aggregations": {
"intitution-agg": {
"terms": { "field": "institutionId" },
"aggregations": {
"appOpenCount": { "sum": { "field": "appOpenCount" }}}
}
}
}
}
}'
The response is perfect ( the aggregated number mathematically makes sense )
{
"took" : 57,
"timed_out" : false,
"_shards" : { ... },
"hits" : {... },
"aggregations" : {
"city-aggs" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "city-1",
"doc_count" : 25,
"intitution-agg" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "inst-1",
"doc_count" : 5,
"appOpenCount" : {
"value" : 15.0
}
}
]
}
}
]
}
}
Using this as template i converted this to Java API call and it i am able to execute it and access city-agg key and institution-agg key but am not sure how to access the appOpenCount agg. Basically getting null for Sum aggregation.
// bool query
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
List<QueryBuilder> mustQueries = boolQueryBuilder.must();
mustQueries.add(QueryBuilders.rangeQuery("time").gte(startTime));
mustQueries.add(QueryBuilders.rangeQuery("appOpenCount").gt(0));
queryBuilder = boolQueryBuilder;
// aggregationbuilder
AggregationBuilder aggregationBuilder = null;
TermsAggregationBuilder cityAggs = AggregationBuilders.terms("city-aggs").field("city");
TermsAggregationBuilder institutionAggs = AggregationBuilders.terms(
"institution-agg").field("institutionId");
SumAggregationBuilder fieldAggBuilder = AggregationBuilders.sum("appOpenCount").field("appOpenCount");
aggregationBuilder = cityAggs.subAggregation(institutionAggs).subAggregation(fieldAggBuilder);
// search call
SearchResponse searchResponse = client.prepareSearch(indexName)
.setTypes(typeName)
.setQuery(queryBuilder)
.addAggregation(aggregationBuilder)
.setFrom(0)
.setSize(0)
.execute().actionGet();
// Iterate the searchResponse
Terms cityAggsTerms = searchResponse.getAggregations().get("city-aggs");
List<Terms.Bucket> mainCityBuckets = cityAggsTerms.getBuckets();
for (Terms.Bucket mainCityBucket : mainCityBuckets) {
String cityName = mainCityBucket.getKeyAsString();
LOGGER.info("CityName : " + cityName); // all good
Terms institutionTerms = mainCityBucket.getAggregations().get("institution-agg");
List<Terms.Bucket> institutionBuckets = institutionTerms.getBuckets();
for (Terms.Bucket institutionBucket : institutionBuckets) {
String institutionName = institutionBucket.getKeyAsString();
LOGGER.info("InstitutionName : " + institutionName ); // all good
Sum appOpenCountSum = institutionBucket.getAggregations().get("appOpenCount");
if(appOpenCountSum != null) {
double appOpenCount = appOpenCountSum.getValue();
LOGGER.info("InstitutionName : " + institutionName +
" and appOpenCount is " + appOpenCount);
} else {
LOGGER.info("appOpenCountSum is null");
}
} // institution for
}// city for
How can i access the value of appOpenCount aggregation. I am hitting the case where my "appOpenCountSum" variable is null. Any help would be appreciated. I am able to access the city-agg and institution-agg and get proper values too. Not sure how to access the appOpenCount aggregation inside Term.Bucket
I followed the example provided in elastic search docs for this
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/_metrics_aggregations.html#java-aggs-metrics-sum
Have given in-depth breakdown and hopefully it helps others too.
EDIT : Issue was the way i was building the aggregation query in java. The fieldAggBuilder should be added to institutionAggs and not the way i had done previously. The corrected code below.
// aggregationbuilder
AggregationBuilder aggregationBuilder = null;
TermsAggregationBuilder cityAggs = AggregationBuilders.terms("cityaggs").field("city");
TermsAggregationBuilder institutionAggs = AggregationBuilders.terms(
"institution-agg").field("institutionId");
SumAggregationBuilder fieldAggBuilder =
AggregationBuilders.sum("appOpenCount").field("appOpenCount");
institutionAggs.subAggregation(fieldAggBuilder); // this was missing previously
aggregationBuilder = cityAggs.subAggregation(institutionAggs);