Recursion closure in groovy json [duplicate] - recursion

This question already has answers here:
Groovy :: Map Find Recursive
(1 answer)
Recursively extracting JSON field values in Groovy
(1 answer)
Closed 5 years ago.
I am trying to fetch all the values from a json whose key name is "label" and want to store in a list.
My problem is, the position of label key is not fixed.Sometimes it comes under parent node sometimes in child and sometime under child to child.We can use recursive closure in groovy but i don't know how?
Json::
[
{
{
"id": "2",
"label": "NameWhatever"
},
{
"id": "123",
"name": "Some Parent Element",
"children": [{
"id": "123123",
"label": "NameWhatever"
},
{
"id": "123123123",
"name": "Element with Additional Children",
"children": [{
"id": "123123123",
"label": "WhateverChildName"
},
{
"id": "12112",
"name": "Element with Additional Children",
"children": [{
"id": "123123123",
"label": "WhateverChildName"
},
{
"id": "12112",
"name": "Element with Additional Children",
"children": [{
"id": "12318123",
"label": "WhateverChildName"
},
{
"id": "12112",
"label": "NameToMap"
}
]
}
]
}
]
}
]
}
]

Based on similar question
import groovy.json.JsonSlurper
def mapOrCollection (def it) {
it instanceof Map || it instanceof Collection
}
def findDeep(def tree, String key, def collector) {
switch (tree) {
case Map: return tree.each { k, v ->
mapOrCollection(v)
? findDeep(v, key, collector)
: k == key
? collector.add(v)
: null
}
case Collection: return tree.each { e ->
mapOrCollection(e)
? findDeep(e, key, collector)
: null
}
default: return null
}
}
def collector = []
def found = findDeep(new JsonSlurper().parseText(json), 'label', collector)
println collector
Assuming variable json contains the given json input from question, prints:
[NameWhatever, NameWhatever, WhateverChildName, WhateverChildName, WhateverChildName, NameToMap]

Related

Omitting Weaviate Objects Containing Specific Reference

Suppose I have a database of movies with some genres tagged to it. My Weaviate schema looks like this:
"classes": [{
"class": "Movie",
"properties": [{
"name": "name",
"dataType": ["string"],
}, {
"name": "inGenres",
"dataType": ["Genre"],
}],
}, {
"class": "Genre",
"properties": [{
"name": "name",
"dataType": ["string"],
}],
}]
I would like to exclude movies tagged with a specific genre from the search results. Specifically, for a database containing the following Movie objects:
{"name":"foo", "inGenres":[{"name":"drama"}]}
{"name":"bar", "inGenres":[{"name":"horror"},{"name":"thriller"}]}
{"name":"baz", "inGenres":[{"name":"horror"},{"name":"sci-fi"}]}
If I exclude the horror genre, the search results should only return the movie foo. Is there any way to perform such a query with GraphQL or the Python client?
You can use the where filter to achieve this.
In your specific case:
{
Get {
Article(
where: {
path: ["inGenres", "Genre", "name"],
operator: NotEqual,
valueString: "horror"
}
) {
name
inGenres {
... on Genre {
name
}
}
}
}
}
In Python
import weaviate
client = weaviate.Client("http://localhost:8080")
where_filter = {
"path": ["inGenres", "Genre", "name"],
"operator": "NotEqual",
"valueString": "horror"
}
query_result = client.query.get("Movie", ["name"]).with_where(where_filter).do()
print(query_result)

Storing optional attributes in DynamoDB's putItem via step functions

I have defined a state machine in AWS step functions and one of my states is storing an item to DynamoDB
...
"Store item": {
"End": true,
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:putItem",
"Parameters": {
"Item": {
"foo": {
"S.$": "$.data.foo"
},
"bar": {
"S.$": "$.data.bar"
},
"baz": {
"S.$": "$.data.baz"
},
},
"TableName": "nrp_items"
}
},
...
The problem starts from the fact that baz property is optional, ie not exist in some cases.
On those cases, the putItem task fails:
An error occurred while executing the state 'Store item' (entered at the event id #71). > The JSONPath '$.data.baz' specified for the field 'S.$' could not be found in the input
My backup plan is to use a lambda to perform that type of operation, but can I do it directly using the putItem task in steps function?
I was wondering if:
Is possible to somehow inject via JSONPath my whole $.data item to the "Item" property, something like:
...
"Store item": {
"End": true,
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:putItem",
"Parameters": {
"Item": "$.data",
"TableName": "nrp_items"
}
},
...
OR
2) Define that the baz property is optional
TL;DR We can deal with optional variables with a "Variable": "$.baz", "IsPresent": true Choice condition to handle no-baz cases.
The Amazon States Language spec does not have optional properties: Step Functions will throw an error if $.baz does not exist in the input. We can avoid undefined paths by inserting a two-branch Choice State, one branch of which handles baz-exists cases, the other no-baz cases. Each branch continues with a Pass State that reworks the data input into dynamo-format Item syntax, using Parameters. The put-item task's "Item.$": "$.data" (as in your #1) contains only foo-bar when baz is not defined, but all three otherwise.
{
"StartAt": "HasBazChoice",
"States": {
"HasBazChoice": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.baz",
"IsPresent": true,
"Next": "MakeHasBazItem"
}
],
"Default": "MakeNoBazItem"
},
"MakeHasBazItem": {
"Type": "Pass",
"Parameters": {
"data": {
"foo": { "S.$": "$.foo"},
"bar": { "S.$": "$.bar"},
"baz": { "S.$": "$.baz"}
}
},
"Next": "PutItemTask"
},
"MakeNoBazItem": {
"Type": "Pass",
"Parameters": {
"data": {
"foo": {"S.$": "$.foo"},
"bar": {"S.$": "$.bar"}
}
},
"Next": "PutItemTask"
},
"PutItemTask": {
...
"Parameters": {
"TableName": "my-table",
"Item.$": "$.data"
}
},
}
}
If you have more than one optional field, your lambda backup plan is the better option - the above workaround would become unwieldy.

Conditional select using jq

I have this below json format, I want to take the list of "id" which satisfies the condition
in this below I want to take the id which has matchers.value as dev-stack and status.state as active
{
"status": "success",
"data": [
{
"id": "b5e7f85d",
"matchers": [
{
"name": "stack",
"value": "dev-stack",
"isRegex": true
}
],
"startsAt": "2020-07-13T07:17:36Z",
"endsAt": "2020-07-15T07:15:44Z",
"updatedAt": "2020-07-13T07:15:59.643692023Z",
"createdBy": "api",
"comment": "Silence",
"status": {
"state": "active"
}
},
{
"id": "1fdaa4b5",
"matchers": [
{
"name": "stack",
"value": "qa-stack",
"isRegex": true
}
],
"startsAt": "2020-07-10T13:19:12Z",
"endsAt": "2020-07-10T13:20:55.510739499Z",
"updatedAt": "2020-07-10T13:20:55.510739499Z",
"createdBy": "api",
"comment": "Silence",
"status": {
"state": "expired"
}
}
]
}
Here is a solution which uses update assignment |=, map and select to update .data.
Note it avoids an undesirable cartesian product if multiple .matchers meet the criteria by using any.
.data |= map(select(
(.matchers | any(.value=="dev-stack")) and (.status.state=="active")
))
Try it online!

CosmosDB, help flatten and filter by nested array

I'm trying to flatten and filter my json data that is in a CosmosDB.
The data looks like below and I would like to flatten everything in the array Variables and then filter by specific _id and Timestamp inside of the array:
{
"_id": 21032,
"FirstConnected": {
"$date": 1522835868346
},
"LastUpdated": {
"$date": 1523360279908
},
"Variables": [
{
"_id": 99999,
"Values": [
{
"Timestamp": {
"$date": 1522835868347
},
"Value": 1
}
]
},
{
"_id": 99998,
"Values": [
{
"Timestamp": {
"$date": 1523270312001
},
"Value": 8888
}
]
}
]
}
If you want to flatten data from the Variables array with properties from the root object you can query your collection like this:
SELECT root._id, root.FirstConnected, root.LastUpdated, var.Values
FROM root
JOIN var IN root.Variables
WHERE var._id = 99998
This will result into:
[
{
"_id": 21032,
"FirstConnected": {
"$date": 1522835868346
},
"LastUpdated": {
"$date": 1523360279908
},
"Values": [
{
"Timestamp": {
"$date": 1523270312001
},
"Value": 8888
}
]
}
]
If you want to even flatten the Values array you will need to write something like this:
SELECT root._id, root.FirstConnected, root.LastUpdated,
var.Values[0].Timestamp, var.Values[0]["Value"]
FROM root
JOIN var IN root.Variables
WHERE var._id = 99998
Note that CosmosDB considers "Value" as a reserved keyword and you need to use an escpape syntax. The result for this query is:
[
{
"_id": 21032,
"FirstConnected": {
"$date": 1522835868346
},
"LastUpdated": {
"$date": 1523360279908
},
"Timestamp": "1970-01-01T00:00:00Z",
"Value": 8888
}
]
Check for more details https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sql-query#Advanced
If you're only looking for filtering by the nested '_id' property then you could use ARRAY_CONTAINS w/ the partial_match argument set to true. The query would look something like this:
SELECT VALUE c
FROM c
WHERE ARRAY_CONTAINS(c.Variables, {_id: 99998}, true)
If you also want to flatten the array, then you could use JOIN
SELECT VALUE v
FROM v IN c.Variables
WHERE v._id = 99998

Watson Assistant (Conversation) service behaves differently when created via API versus created in the Browser

We are seeing different behaviour when we create a workspace in the WebInterface versus when we create the same conversation via the API.
The JSON export for the dialognode is the same:
{ "type": "standard",
"title": "SmallTalk: weerbericht",
"output": {
"text": {
"values": [],
"selection_policy": "sequential" } },
"parent": "smalltalk_container",
"context": { "user_weer": "#weerbericht",
"user_location": "#plaatsnamen" },
"metadata": {
"_customization": {
"mcr": false } },
"next_step": { "behavior": "jump_to", "selector": "condition", "dialog_node": "node_33_1519129633532"
},
"conditions": "#ST_weersbericht",
"description": null,
"dialog_node": "node_9_1517408489377",
"previous_sibling": "node_3_1518680265483" },
But the behaviour is different, which can be explained when we look at the UI
there is a difference
This is the UI for the dialogNode created via the browser
This is the UI for the same dialogNode created via the API
One difference we found is the Multiple Reponse switch:
It should be OFF (image on the right) and as per the JSON (mcr:false).
But even when we switch it on manually, the context variables don't show.
What should I be looking for in the API to fix this ?
The dialog model for multiple condition responses is that the parent node needs to be either standard dialog node or frame. Now when adding a multiple condition response to this node (and hence making an MCR node out of that parent node) you need to add a dialog node with "type":"response_condition" under this node.
This is a way how to create multiple condition responses through the api.
To give you and example to create MCR node:
The JSON of dialog nodes that need to pushed through the API will look like:
{
"type": "standard",
"title": "mcr node",
"output": {
},
"parent": null,
"context": null,
"metadata": {
"_customization": {
"mcr": true
}
},
"next_step": null,
"conditions": "#book_flight",
"digress_in": "does_not_return",
"description": null,
"dialog_node": "node_8_1525086089064",
"digress_out": "allow_all",
"previous_sibling": null
},
{
"type": "response_condition",
"title": null,
"output": {
"text": {
"values": ["I see city entity!"]
}
},
"parent": "node_8_1525086089064",
"context": null,
"metadata": {
},
"next_step": null,
"conditions": "#city",
"description": null,
"dialog_node": "node_9_1525086100114",
"previous_sibling": null
},
{
"type": "response_condition",
"title": null,
"output": {
"text": {
"values": ["I don't see anything."]
}
},
"parent": "node_8_1525086089064",
"context": null,
"metadata": {
},
"next_step": null,
"conditions": "anything_else",
"description": null,
"dialog_node": "node_10_1525086122332",
"previous_sibling": "node_9_1525086100114"
}

Resources