How to create a Jolt specification - jolt

I am having issues to create a Jolt specification for the following input, I want to convert the Score to an array called Scores, what is happening is when I have only one score it is not send as array, so I want to convert to an array of scores.
[
{
"Page": 320301,
"ScoreInfo": {
"StadiumID": 126,
"Stadium": "abcded",
"Inn": 3,
"TB": 2,
"Team": [
{
"HV": 1,
"ID": 1,
"NameS": "abdcd",
"NameES": "abdcd",
"R": 1,
"PA": 10,
"H": 2,
"BB": 3,
"SB": 0,
"E": 0,
"Score": {
"Inn": 1,
"TB": 2,
"R": 0,
"H": 0,
"BB": 1
}
},
{
"HV": 2,
"ID": 3,
"NameS": "afewqe",
"NameES": "dadfa",
"R": 1,
"PA": 12,
"H": 3,
"BB": 0,
"SB": 0,
"E": 0,
"Score": [
{
"Inn": 1,
"TB": 1,
"R": 1,
"H": 2,
"BB": 0
},
{
"Inn": 2,
"TB": 1,
"R": 0,
"H": 0,
"BB": 0
}
]
}
],
"HV": null
}
}
]
The output expected is:
{
"ScoreInfo" : {
"Team" : [ {
"HV" : 1,
"ID" : 1,
"NameS" : "abdcd",
"NameES" : "abdcd",
"R" : 1,
"PA" : 10,
"H" : 2,
"BB" : 3,
"SB" : 0,
"E" : 0,
"Scores" : [ {
"Inn" : 1,
"TB" : 2,
"R" : 0,
"H" : 0,
"BB" : 1
} ]
}, {
"HV" : 2,
"ID" : 3,
"NameS" : "afewqe",
"NameES" : "dadfa",
"R" : 1,
"PA" : 12,
"H" : 3,
"BB" : 0,
"SB" : 0,
"E" : 0,
"Scores" : [ {
"Inn" : 1,
"TB" : 1,
"R" : 1,
"H" : 2,
"BB" : 0
}, {
"Inn" : 2,
"TB" : 1,
"R" : 0,
"H" : 0,
"BB" : 0
}]
} ]
},
"Type" : "ScoreInfo",
"Datasource" : "Vendor"
}
I have tried it so far, but it is not going anywhere:
[
{
"operation": "shift",
"spec": {
"*": {
"ScoreInfo": {
"Team": {
"*": {
"#": "ScoreInfo.&2",
"Score": {
"*": {
"#": "ScoreInfo.Team.Scores.[]"
}
}
}
}
}
}
}
},
{
"operation": "default",
"spec": {
"Datasource": "Vendor",
"Type": "ScoreInfo"
}
}
]
Thanks in advance,

The common key name(Score) leads to a confusion, so need to seperate the first(0) index of the Team array and the other members while evaluation, and use [] extension only for the first index at the end of the "Score": "&3.&2.[#2].&s[]" key-value pair
[
{
"operation": "shift",
"spec": {
"*": {
"ScoreInfo": {
"Team": {
"0": {
"*": "&3.&2.[#2].&",
"Score": "&3.&2.[#2].&s[]"
},
"*": {
"*": "&3.&2.[#2].&",
"Score": "&3.&2.[#2].&s"
}
}
}
}
}
},
{
"operation": "default",
"spec": {
"Datasource": "Vendor",
"Type": "ScoreInfo"
}
}
]
where no need to explicitly write the name of the keys, you can rather use ampersand placeholders along with integer suffixes(such as &2,&3..) in order to grab the related value from the outer levels, or just a single ampersand(&) to grab the current value of the key.

Related

Access Nested Objects using Painless for Elasticsearch/Opensearch Alerting to Create Trigger Condition

I tried creating Trigger condition in Monitor in Opensearch.I was trying to extract fields present in _source which is giving error "reason" : "Illegal list shortcut value [_source].".
DSL Extraction Query-
{
"size": 1,
"sort": {"Date" : "desc" },
"query": {
"bool": {
"filter": [
{
"range": {
"Date": {
"from": "{{period_end}}||-3d",
"to": "{{period_end}}",
"include_lower": true,
"include_upper": true,
"format": "epoch_millis",
"boost": 1
}
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"aggregations": {
"metric": {
"avg": {
"field": "difference"
}
}
}
}
Output-
{
"_shards": {
"total": 4,
"failed": 0,
"successful": 4,
"skipped": 2
},
"hits": {
"hits": [
{
"_index": "reliance_cal_spread",
"_source": {
"symbol": "RELIANCE",
"std_dev": 2.4,
"upper_range": 10.3,
"current_month_close": 2536.3,
"mean": 7.9,
"weekday": "Tuesday",
"difference": 3,
"Date": "2022-06-28T00:00:00",
"next_month_close": 2539.3,
"lower_range": 5.5
},
"_id": "E2-cqoEB0vMP3J-sGCIB",
"sort": [
1656374400000
],
"_score": null
}
],
"total": {
"value": 2,
"relation": "eq"
},
"max_score": null
},
"took": 8,
"timed_out": false,
"aggregations": {
"metric": {
"value": 2.799999952316284
}
}
}
Trigger Condition-
return ctx.results[0].aggregations.metric.value == null ? false :ctx.results[0].hits.hits._source.difference > 6
Error-
{
"type" : "script_exception",
"reason" : "runtime error",
"script_stack" : [
"return ctx.results[0].aggregations.metric.value == null ? false :ctx.results[0].hits.hits._source.difference > 6",
" ^---- HERE"
],
"script" : "return ctx.results[0].aggregations.metric.value == null ? false :ctx.results[0].hits.hits._source.difference > 6",
"lang" : "painless",
"position" : {
"offset" : 89,
"start" : 0,
"end" : 112
},
"caused_by" : {
"type" : "illegal_argument_exception",
"reason" : "Illegal list shortcut value [_source]."
}
}
While I am able to extract other conditions such as-aggregations.metric.value and hits.total.value.
Trigger Condition
return ctx.results[0].aggregations.metric.value == null ? false :ctx.results[0].aggregations.metric.value < 6
Trigger Condition Response-
True

Multiple group in aggregate

In my db there are two types of sessions - "studio", "classroom". I want the count of number of studio sessions and number of classroom sessions AND count of subjects in a classroom session and count of subjects in studio session.
Eg data:
[
{
"key": 1,
"type": "studio",
"sub": "english"
},
{
"key": 2,
"type": "studio",
"sub": "history"
},
{
"key": 3,
"type": "classroom",
"sub": "english"
},
{
"key": 4,
"type": "studio",
"sub": "english"
},
{
"key": 5,
"type": "classroom",
"sub": "english"
},
{
"key": 5,
"type": "classroom",
"sub": "geography"
}
]
This is the query I have to group by session type.
db.collection.aggregate([
{
$group: {
_id: "$type",
groupedData: {
$push: {
key: "$key",
"sub": "$sub",
"type": "$type"
}
}
}
}
])
By this I get
[
{
"_id": "classroom",
"groupedData": [
{
"key": 3,
"sub": "english",
"type": "classroom"
},
{
"key": 5,
"sub": "english",
"type": "classroom"
},
{
"key": 5,
"sub": "geography",
"type": "classroom"
}
]
},
{
"_id": "studio",
"groupedData": [
{
"key": 1,
"sub": "english",
"type": "studio"
},
{
"key": 2,
"sub": "history",
"type": "studio"
},
{
"key": 4,
"sub": "english",
"type": "studio"
}
]
}
]
But I want the count of a subject in studio and class room and total count of sessions in studio and classroom
Eg.
{
studio: { count: 3, englishInStudio: 2, historyInStudio: 1 },
classroom: {count: 3, englishInClassroom: 2, geographyInClassroom: 1}
}
You can do this by specifying a document as your key to group on
db.test.aggregate([
{ $group: {
_id: { "type": "$type", "sub" : "$sub"},
count: { $sum: 1}
}
}]);
This will output the following, which get your required values but it's in a different format:
{ "_id" : { "type" : "classroom", "sub" : "english" }, "count" : 2 }
{ "_id" : { "type" : "studio", "sub" : "history" }, "count" : 1 }
{ "_id" : { "type" : "classroom", "sub" : "geography" }, "count" : 1 }
{ "_id" : { "type" : "studio", "sub" : "english" }, "count" : 2 }

Mongodb Document : Is there any way to count sub-documents with a condition?

I have documents with subdocuments where the field "stars" in the subdocuments have either "1" or "0". I want to count them at the document level based on those values in the field "stars".
This is how the current document looks like:
{
"name": "Hotel A",
"category": "hotel",
"reviews": [
{
"title": "A",
"stars": 1
},
{
"title": "B",
"stars": 1
},
{
"title": "C",
"stars": 0
}
],
"total_reviews": 3
},{
"name": "Hotel B",
"category": "hotel",
"reviews": [
{
"title": "A",
"stars": 1
},
{
"title": "B",
"stars": 1
},
{
"title": "C",
"stars": 0
},
{
"title": "D",
"stars": 0
},
{
"title": "E",
"stars": 1
},
{
"title": "F",
"stars": 0
}
],
"total_reviews": 6
}
And this is the expected output:
{
"name": "Hotel A",
"category": "hotel",
"reviews": [
{
"title": "A",
"stars": 1
},
{
"title": "B",
"stars": 1
},
{
"title": "C",
"stars": 0
}
],
"positive_reviews": 2,
"negative_reviews": 1,
"total_reviews": 3
},{
"name": "Hotel B",
"category": "hotel",
"reviews": [
{
"title": "A",
"stars": 1
},
{
"title": "B",
"stars": 1
},
{
"title": "C",
"stars": 0
},
{
"title": "D",
"stars": 0
},
{
"title": "E",
"stars": 1
},
{
"title": "F",
"stars": 0
}
],
"positive_reviews": 3,
"negative_reviews": 3,
"total_reviews": 6
}
By adding two new fields: "positive_reviews" if {"reviews.stars":1} and "negative_reviews" if {"reviews.stars":0} with the count values
Try as below:
db.collection.aggregate([
{
$project: {
"_id" : 1,
"name" : 1,
"category" : 1,
"reviews" : 1,
"positive_reviews":
{
$reduce:
{
input: "$reviews",
initialValue: 0,
in: {
$add: ["$$value", { $cond:[{ $eq: ["$$this.stars", 1]} , 1, 0 ] } ]
}
}
},
"negative_reviews":
{
$reduce:
{
input: "$reviews",
initialValue: 0,
in: {
$add: ["$$value", { $cond:[{ $eq: ["$$this.stars", 0]} , 1, 0 ] } ]
}
}
},
"total_reviews": { $size: "$reviews"}
}
}
])
Result Response:
/* 1 createdAt:12/04/2019, 18:24:50*/
{
"_id" : ObjectId("5cb08a9a952e3a179190d996"),
"name" : "Hotel A",
"category" : "hotel",
"reviews" : [
{
"title" : "A",
"stars" : 1
},
{
"title" : "B",
"stars" : 1
},
{
"title" : "C",
"stars" : 0
}
],
"positive_reviews" : 2,
"negative_reviews" : 1,
"total_reviews" : NumberInt(3)
},
/* 2 createdAt:12/04/2019, 18:24:50*/
{
"_id" : ObjectId("5cb08a9a952e3a179190d997"),
"name" : "Hotel B",
"category" : "hotel",
"reviews" : [
{
"title" : "A",
"stars" : 1
},
{
"title" : "B",
"stars" : 1
},
{
"title" : "C",
"stars" : 0
},
{
"title" : "D",
"stars" : 0
},
{
"title" : "E",
"stars" : 1
},
{
"title" : "F",
"stars" : 0
}
],
"positive_reviews" : 3,
"negative_reviews" : 3,
"total_reviews" : NumberInt(6)
}

Sort multiple condition MongoDB

I have collection of user, and this is the following of documents :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 }
I want to show the list of users with some of criterias and ordering with some conditons, online users and most liked in the top of the list,after online user list is show offline users with most scored & most liked. The following of rules :
If online is 1 must be sort by descending of like.
If online is 0 and score is 1 must be sort by descending of score.
If online is 0 and score is 0 must be sort by descending of like.
So, the result can be like :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 }
I have finished until point 2, my query following :
db.users.aggregate([
{
$project :
{
"id" : 1,
"name" : 1,
"online: 1,
"like" : 1,
"score" : 1,
"sort" : {
$cond:
{
"if" :
{
$eq : ["$online", true]
},
"then" : "$like",
"else" : "$score"
}
}
}
},
{
$sort :
{
"online" : -1,
"sort" : -1,
"id" : 1
}
},
{
$skip : 0
},
{
$limit : 9
}
])
But I have the current result following :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 },
You can see, based on point 3, instance { "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 } should be on top with score is 0
First create additional column call point with value (1 - online)*score
After this sort data by:
online desc
point desc (online = 1 pointbe always 0, online is 0 point is score)
like desc
You can use this query
db.yourtable.aggregate(
[
{ $project:{
"id" : 1,
"name" : 1,
"online": 1,
"like" : 1,
"score" : 1,
point: { $multiply: [
{$subtract: [1,"$online"]}
, "$score"
]}
}
}
,{ $sort : { online: -1, point : -1, like : -1 } }
]
);
Please check below query :
db.getCollection('yourTable').aggregate([
{
$project :
{
"id" : 1,
"name" : 1,
"online": 1,
"like" : 1,
"score" : 1,
onlineSortLike: {
$cond: {
if: { $and: [{ $eq: ['$online',1 ] }] },
then: '$like',
else: 0,
},
},
sortOfflineScore: {
$cond: {
if: { $and: [{ $eq: ['$online',0] }] },
then: '$score',
else: 0,
},
},
sortOfflineScoreLike: {
$cond: {
if: { $and: [{ $eq: ['$online', 0] }] },
then: '$like',
else: 0,
},
},
}
},
{
$sort :
{
"online" : -1,
"onlineSortLike" : -1,
"sortOfflineScore" : -1,
"sortOfflineScoreLike" : -1
}
},
{
$skip : 0
},
{
$limit : 9
}
])

Mongo returning an array element

I have the following JSON document in my mongoDB which I added with mingoimport.
I am trying to return a single element from the questions array where theQuestion equals "q1".
{
"questions": [
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q1",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o1"
},
{
"type": 1,
"value": "o1"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes"
]
}
},
{
"questionEntry": {
"id": 1,
"info": {
"seasonNumber": 1,
"episodeNumber": 1,
"episodeName": "Days Gone Bye"
},
"questionItem": {
"theQuestion": "q2",
"attachedElement": {
"type": 1,
"value": ""
}
},
"options": [
{
"type": 1,
"value": "o2"
},
{
"type": 1,
"value": "o2"
}
],
"answer": {
"questionId": 1,
"answer": 1
},
"metaTags": [
"Season 1",
"Episode 1",
"Rick Grimmes",
"Glenn Rhee"
]
}
}
]
}
I ran the query db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}) but this retruned the whole document (both questionEntry's in question array!
I have tried db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"}, _id:0," questions.questionItem": {$elemMatch : {theQuestion: "q1"}}})
But get the following error:
Error: error: {
"$err" : "Can't canonicalize query: BadValue Cannot use $elemMatch projection on a nested field.", "code" : 17287
Is there a way I could limit the result to just the array element which contains it?
Thanks
db.questions.find({},{"questions.questionEntry.questionItem.theQuestion" : "q1"});
or
db.questions.find({"questions.questionEntry.questionItem.theQuestion" : "q1"},{'questions.$':1});
please try these.
If you want to use $elemMatch the query should be:
db.questions.find(
{"questions.questionEntry.questionItem.theQuestion" : "q1"},
{
'_id':0,
"questions": {
$elemMatch : {"questionEntry.questionItem.theQuestion": "q1"}
}
}
)