Turning Array of objects into a bag - kql

I have a dynamic list from a JSON file that I need to convert. Thanks to the setup I will always get the following output format, but I cannot be certain of the order of the list, just the contents.
[
{"#text": "Foo"
"#Name": "Bar"}
{"#text": "Woo"
"#Name": "Tar"}
{"#text": "Fuu"
"#Name": "Bear"}
]
And I would love to end up with:
{"Foo": "Bar", "Woo": "Tar", "Fuu": "Bear"}
I have tried to use mv-expand, however, I am not able to collect the columns as they are called #text and #Name, meaning that they are invalid to use in a query. I am very new to this language and haven't been able to find anything matching my problem.

print obj = dynamic([
{"#text": "Foo",
"#Name": "Bar"},
{"#text": "Woo",
"#Name": "Tar"},
{"#text": "Fuu",
"#Name": "Bear"}
])
| mv-apply obj on (summarize make_bag(pack(tostring(obj['#text']), tostring(obj['#Name']))))
bag_
{"Foo":"Bar","Woo":"Tar","Fuu":"Bear"}
Fiddle

Related

Query a column with jsonb data meeting certain criteria

Problem
I have a table like this:
product
tags (jsonb)
P001
[{"name": "LX","active": true}, {"name": "TX","active": true}]
P002
[{"name": "LX","active": true}]
I am trying to query against this table to get a list of products with tags that are the same.
I found the following to be insufficient for my query since it will match all products with at least the tag I query for:
SELECT product
FROM product_table
WHERE tags #> '[
{"name": "LX","active": true}
]';
Result
product
P001
P002
So I need the match to be more exact without being so strict as to demand order of tag objects in the array. For example:
[
{
"name": "LX",
"active": true
},
{
"name": "TX",
"active": true
}
]
-- Interpreted the same as
[
{
"name": "TX",
"active": true
},
{
"name": "LX",
"active": true
}
]
Desired Result
A list of products that match only the tags in the query.
Resources
A list of the resources I am using to try and solve the problem.
9.16. JSON Functions and Operators
How To Query a JSONB Array of Objects as a Recordset in PostgreSQL
Working with a JSONB Array of Objects in PostgreSQL
Base on the example you give, you can use simultaneous containment in both directions.
...WHERE tags <# :whatever and tags #> :whatever
If you don't like repeating the arguments, you could make a custom function, or operator:
create function equal_but_for_order(jsonb,jsonb) returns boolean language sql as $$
select $1 <# $2 and $1 #> $2
$$;
create operator <#> (function = equal_but_for_order, leftarg = jsonb, rightarg=jsonb);
...WHERE tags <#> :whatever

Karate json key list variable assignment

New to Karate, and JSON, for that matter, but I've got a variable like:
response {
entries {
products [
{
names [
"Peter Parker",
"Tony Stark",
"Captain America"
]
},
{
names [
"Thomas Tinker",
"Jimmy Johnson",
"Mama Martha"
]
}
]
}
}
match each response.entries.products[*].names returns a list like:
["Peter Parker","Tony Stark","Captain America","Thomas Tinker","Jimmy Johnson","Mama Martha"]
But I'd like to assign that output to a variable, such as:
* def variable = response.entries.products[*].names
that would hold a similar value. When I use the above line, I get the following error:
Expected an operand but found *
Is it possible to achieve that, or something similar? If so, how?
Thanks!
Yes, there is syntax for that:
* def variable = $response.entries.products[*].names
Read the docs: https://github.com/intuit/karate#get

How to check if json array already contains a certain key?

Let's say I have this json in my jsonb column
{
"fields": [
{
"name": "firstName",
},
{
"name": "lastName",
},
...
}
How can I know if the "firstName" already exist?
I've tried this so far
SELECT field->>'fields'
from person where (field->'name')::jsonb ? 'firstName';
Use the containment operator #>:
select field->>'fields'
from person
where field->'fields' #> '[{"name": "firstName"}]'
you can use json_array_elements to generate fields elements so you can filter based on 'name'.
SELECT field->>'fields', obj.*
from person, jsonb_array_elements_text(field->'fields') obj
where obj = '{"name": "firstName"}'
see dbfiddle

jsonb LIKE query on nested objects in an array

My JSON data looks like this:
[{
"id": 1,
"payload": {
"location": "NY",
"details": [{
"name": "cafe",
"cuisine": "mexican"
},
{
"name": "foody",
"cuisine": "italian"
}
]
}
}, {
"id": 2,
"payload": {
"location": "NY",
"details": [{
"name": "mbar",
"cuisine": "mexican"
},
{
"name": "fdy",
"cuisine": "italian"
}
]
}
}]
given a text "foo" I want to return all the tuples that have this substring. But I cannot figure out how to write the query for the same.
I followed this related answer but cannot figure out how to do LIKE.
This is what I have working right now:
SELECT r.res->>'name' AS feature_name, d.details::text
FROM restaurants r
, LATERAL (SELECT ARRAY (
SELECT * FROM json_populate_recordset(null::foo, r.res#>'{payload,
details}')
)
) AS d(details)
WHERE d.details #> '{cafe}';
Instead of passing the whole text of cafe I want to pass ca and get the results that match that text.
Your solution can be simplified some more:
SELECT r.res->>'name' AS feature_name, d.name AS detail_name
FROM restaurants r
, jsonb_populate_recordset(null::foo, r.res #> '{payload, details}') d
WHERE d.name LIKE '%oh%';
Or simpler, yet, with jsonb_array_elements() since you don't actually need the row type (foo) at all in this example:
SELECT r.res->>'name' AS feature_name, d->>'name' AS detail_name
FROM restaurants r
, jsonb_array_elements(r.res #> '{payload, details}') d
WHERE d->>'name' LIKE '%oh%';
db<>fiddle here
But that's not what you asked exactly:
I want to return all the tuples that have this substring.
You are returning all JSON array elements (0-n per base table row), where one particular key ('{payload,details,*,name}') matches (case-sensitively).
And your original question had a nested JSON array on top of this. You removed the outer array for this solution - I did the same.
Depending on your actual requirements the new text search capability of Postgres 10 might be useful.
I ended up doing this(inspired by this answer - jsonb query with nested objects in an array)
SELECT r.res->>'name' AS feature_name, d.details::text
FROM restaurants r
, LATERAL (
SELECT * FROM json_populate_recordset(null::foo, r.res#>'{payload, details}')
) AS d(details)
WHERE d.details LIKE '%oh%';
Fiddle here - http://sqlfiddle.com/#!15/f2027/5

ActiveRecord Query to find JSON Value Array for a given Key

In Rails 4 I have a table with a JSON type column. I have a method that works well like so
def self.that_match_property(key: "default", value: "default")
where("properties ->> ? = ?", key, value)
end
So for Model.first.properties I get {"name": "Bill", "user_id": "1"}
I can do this just fine.
Model.that_match_property(key: "name", value: "Bill")
And I get the record or records that match on the key/value pair in my json properties column.
But... let's say I want the value to be an array of ids. So I have...
user1.properties = {"name": "Bill", "user_id": "1"}
user2.properties = {"name": "Ted", "user_id": "2"}
user3.properties = {"name": "Rufus", "user_id": "3"}
Now I also have bill_and_ted_ids = ["1", "2"]
I want to be able to do this:
Model.that_match_property(key: "user_id", value: bill_and_ted_ids)
But this doesn't work. Normally I could pass in an array of IDs to an active record query and it converts to the proper sql.
What is the correct way to do the above with a JSON data type in Rails?
Maybe something like this will be helpfull:
def build_where_query_string_from_hash(hash)
hash.collect do |k,v|
if v.is_a?(Array)
string = v.collect{|e| "(properties ->> '#{k}'='#{e}')"}.join(" OR ")
"(#{string})"
else
"(properties ->> #'{k}'='#{v}')"
end
end.join(" AND ")
end
then you can pass: build_where_query_string_from_hash(user_id: [1,2], name: ["Bill", "Ted"])
which returns:
((properties ->> 'user_id'='1') OR (properties ->> 'user_id'='2')) AND ((properties ->> 'name'='Bill') OR (properties ->> 'name'='Ted'))`