JsonPath For multiple object - jsonpath

I want to know the name who purchased items "abc" and "def" from the bellow json data.
(Expected result is "tom")
Please tell me how to do using JsonPath.
[
{
"name": "tom",
"purchased": [
{
"name": "abc"
},
{
"name": "def"
}
]
},
{
"name": "bob",
"purchased": [
{
"name": "xyz"
}
]
}
]

If you are doing this in Java you could select the name of the buyer like this:
$..[?(#.purchased && #..name contains 'abc' && #..name contains 'def' )].name
In JavaScript, you can use this a query like this:
$.[?(#.purchased && #.purchased.some(e => e.name==='abc') && #.purchased.some(e => e.name==='def') )].name
Both queries use a similar approach:
We filter for a purchased-property, then use a function to search the keys in the map for certain values
Jayway's JsonPath offers a contains function to do so;
In JavaScript we leverage script evaluation to search the map/dict for our key/values
So, the .some() is not a JsonPath function but some modern JavaScript (:D).
This is kind of a workaround in both cases but it gets the job done.
You can test both online here. Select the tab JayWay and click Go for the first query, switch to tab Goessner to run the JavaScript sample (you can run the second here as well).

Related

filtering Dynamo DB in Step functions JSONPath

I am trying to build a step function that has a choice state based on a map in a result of a dynamo db map. An example result from my dynamo GetItem request would be.
{
"Item": {
"organisationId": {
"S": "Andys-test"
},
"id": {
"S": "Andy2"
},
"states": {
"L": [
{
"M": {
"year": {
"N": "2021"
},
"status": {
"S": "started"
}
}
},
{
"M": {
"year": {
"N": "2022"
},
"status": {
"S": "started"
}
}
}
]
},
},
My condition will be checking the status of the states map against the year 2021. I have attempted to use this JSONPath which from what I can tell is valid, although I am getting nothing in the data flow simulator in the step functions console. I have tried various iterations of the below, with quotes escaped quotes etc and can't get anything to parse the correct value out.
I have been doing this in the input selector as I can see that the result path does not support the [?()] notation.
$.Item.states.L..M[?(#.year.N == 2022) ]
Without an example choice state with some rules, it'll be hard to answer definitively but I think I'm following.
JSONPath with expression filters
I run into this sort of problem more often than I'd like when using JSONPath filter expressions and have to add "helper" tasks to get things moving (wasting valuable state transitions 😣 ).
When you specify a Path that includes a filter expression, the result is always going to be a list (and you won't be able to reference index afterwards either).
ChoiceRules don't really have a comparator that deals with arrays/lists (at least i haven't been able to get it to work, so let me know if you do 😄).
The hack I've found easiest to reason about/maintain is creating a simple Pass Task that "pops" the values I need from the filter expression and then pass that along to the Choice Task.
Here's an example from one of my previous answers (the pattern used in the PassDef task would be defined before your choice task in your scenario, instead of after)
I've found it easier to start with https://jsonpath.herokuapp.com then progress to the data flow simulator. Just remember to always keep in mind whether
you're trying implement a Path or a Reference Path!

modify nested object but maintain the full object after

I need to modify an element --an array-- (e.g.: "group-xyz") within a nested object in a JSON tree using JQ but once that's done then I need the entire object back with the modified data.
The goal is to update a JSON tree and save it in full.
e.g.: add array element, empty array, etc.
{
"group-abc": {"users": ["tina.turner"]},
"group-def": {"users": ["someone.else"]},
"group-xyz": {"users": ["that.thing"]
}
Then I am interested in returning an object like this:
{
"group-abc": {"users": ["tina.turner"]},
"group-def": {"users": []},
"group-xyz": {"users": ["that.thing","well.done"]
}
I have changed my requirements to fit a more complex form. To add a user to any of these groups' users this is what I did:
jq '. |= map( if ( .group=="abc") then .users+=["final.answer",] else . end)' source.json
which produced a result
[
{
"group": "abc",
"users": [
"user1",
"user2",
"final.answer"
]
},
{
"group": "def",
"users": [
"user4",
"user5"
]
}
]

Elastic Search in ASP.NET - using ampersand sign

I'm new to Elastic Search in ASP.NET, and I have a problem which I'm, so far, unable to resolve.
From documentation, I've seen that & sign is not listed as a special character. Yet, when I submit my search ampersand sign is fully ignored. For example if I search for procter & gamble, & sign is fully ignored. That makes quite a lot of problems for me, because I have companies that have names like M&S. When & sign is ignored, I get basically everything that has M or S in it. If I try with exact search (M&S), I have the same problem.
My code is:
void Connect()
{
node = new Uri(ConfigurationManager.AppSettings["Url"]);
settings = new ConnectionSettings(node);
settings.DefaultIndex(ConfigurationManager.AppSettings["defaultIndex"]);
settings.ThrowExceptions(true);
client = new ElasticClient(settings);
}
private string escapeChars(string inStr) {
var temp = inStr;
temp = temp
.Replace(#"\", #"\\")
.Replace(#">",string.Empty)
.Replace(#"<",string.Empty)
.Replace(#"{",string.Empty)
.Replace(#"}",string.Empty)
.Replace(#"[",string.Empty)
.Replace(#"]",string.Empty)
.Replace(#"*",string.Empty)
.Replace(#"?",string.Empty)
.Replace(#":",string.Empty)
.Replace(#"/",string.Empty);
return temp;
}
And then inside one of my functions
Connect();
ISearchResponse<ElasticSearch_Result> search_result;
var QString = escapeChars(searchString);
search_result = client.Search<ElasticSearch_Result>(s => s
.From(0)
.Size(101)
.Query(q =>
q.QueryString(b =>
b.Query(QString)
//.Analyzer("whitespace")
.Fields(fs => fs.Field(f => f.CompanyName))
)
)
.Highlight(h => h
.Order("score")
.TagsSchema("styled")
.Fields(fs => fs
.Field(f => f.CompanyName)
)
)
);
I've tried including analyzers, but then I've found out that they change the way tokenizers split words. I haven't been able to implement changes to the tokenizer.
I would like to be able to have following scenario:
Search: M&S Company Foo Bar
Tokens: M&S Company Foo Bar + bonus is if it's possible to have M S tokens too
I'm using elastic search V5.0.
Any help is more than welcome. Including better documentation than the one found here: https://www.elastic.co/guide/en/elasticsearch/client/net-api/5.x/writing-queries.html.
By default for a text field the analyzer applied is standard analyzer. This analyzer applies standard tokenizer along with lowercase token filter. So when you are indexing some value against that field, the standard analyzer is applied on that value and the resultant tokens are indexed against the field.
Let's understand this by e.g. For the field companyName (text type) let us assume that the value being passed is M&S Company Foo Bar while indexing a document. The resultant tokens for this value after the application of standard analyzer will be:
m
s
company
foo
bar
What you can notice is that not just whitespace but also & is used as delimiter to split and generate the tokens.
When you query against this field and don't pass any analyzer in the search query, it by default apply the same analyzer for search as well which is applied for indexing against the field. Therefore, if you search for M&S it get tokenised to M and S and thus actual search query search for these two tokens instead of M&S.
To solve this, you need to change the analyzer for the field companyName. Instead of standard analyzer you can create a custom analyzer which use whitespace tokenizer and lowercase filter (to make search case insensitive). For this you need to change the setting and mapping as below:
{
"settings": {
"analysis": {
"analyzer": {
"whitespace_lowercase": {
"tokenizer": "whitespace",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"companyName": {
"type": "text",
"analyzer": "whitespace_lowercase",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
Now for the above input the tokens generated will be:
m&s
company
foo
bar
This will ensure that when searching for M&S, & is not ignored.

Complex object in a query string

How can I have this structure in a query string?
"properties": {
"list": [
{
"label": "bye",
"value": "world"
},
{
"label": "hello",
"value": "mars"
}
]
}
I've tried it with properties[][list][label]=bye&properties[][list][value]=world&properties[0][label]=hello&properties[0][value]=mars and also with properties[][list][label]=bye&properties[][list][value]=world&properties[][list][label]=hello&properties[][list][value]=mars, none of them worked. I built them in php with http_build_query.
I need to have this structure in a query string because I have to send the data along with some other stuff with POST to a PHP site.
I see two errors in your query string:
properties is an object, so there's no need to use [] to add elements.
list is an array, so you must use numeric indexes in the query string.
The correct query string is:
?properties[list][0][label]=bye
&properties[list][0][value]=world
&properties[list][1][label]=hello
&properties[list][1][value]=mars
(multi-lined for readability)

How do I make a Hasura data API query to fetch rows based on the length of the their array relationship's value?

Referring to the default sample schema mentioned in https://hasura.io/hub/project/hasura/hello-world/data-apis i.e. to the following two tables:
1) author: id,name
2) article: id, title, content, rating, author_id
where article:author_id has an array relationship to author:id.
How do I make a query to select authors who have written at least one article? Basically, something like select author where len(author.articles) > 0
TL;DR:
There's no length function that you can use in the Hasura data API syntax right now. Workaround 1) filter on a property that is guaranteed to be true for every row. Like id > 0. 2) Build a view and expose APIs on your view.
Option 1:
Use an 'always true' attribute as a filter.
{
"type": "select",
"args": {
"table": "author",
"columns": [
"*"
],
"where": {
"articles": {
"id": {
"$gt": "0"
}
}
}
}
}
This reads as: select all authors where ANY article has id > 0
This works because id is an auto-incrementing int.
Option 2:
Create a view and then expose data APIs on them.
Head to the Run SQL window in the API console and run a migration:
CREATE VIEW author_article_count as (
SELECT au.*, ar.no_articles
FROM
author au,
(SELECT author_id, COUNT(*) no_articles FROM article GROUP BY author_id) ar
WHERE
au.id = ar.author_id)
Make sure you mark this as a migration (a checkbox below the RunSQL window) so that this gets added to your migrations folder.
Now add data APIs to the view, by hitting "Track table" on the API console's schema page.
Now you can make select queries using no_articles as the length attribute:
{
"type": "select",
"args": {
"table": "author_article_count",
"columns": [
"*"
],
"where": {
"no_articles": {
"$gt": "0"
}
}
}
}

Resources