Query a column with jsonb data meeting certain criteria - sql

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

Related

LIKE in Array of Objects in JSONB column

I have JSONB data in a Postgres column like this:
{
"Id": "5c6d3210-1def-489b-badd-2bcc4a1cda28",
"Name": "Jane Doe",
"Tags": [
{
"Key": "Project",
"Value": "1004345"
}
]
}
How can I query data where Name contains "Jane" or "Tags.Key" contains "4345"?
I tried this but this only matches the exact "Key" value:
select * from documents where data->'Tags' #> '[{ "Value":"1004345"}]';
You can use a JSON path operator using like_regex
select *
from documents
where data ## '$.Tags[*].Value like_regex "4345"'
you can do this way
select *
from documents
where 'Tags' ->> 'Value' = '1004345';

PostgreSQL how to query jsonb by a value?

SELECT * FROM some_table;
I can query to get the following results:
{
"sku0": {
"Id": "18418",
"Desc": "yes"
},
"sku1": {
"Id": "17636",
"Desc": "no"
},
"sku2": {
"Id": "206714",
"Desc": "yes"
},
"brand": "abc",
"displayName": "something"
}
First, the number of skus is not fixed. It may be sku0, sku1, sku2, sku3, sku4 ... but they all start with sku.
Then, I want to query Id with 17636 and determine whether its value of Desc is yes or no. After reading the PostgreSQL JSON Functions and Operators documentation, Depressing I didn't find a good way.
I can convert the result into a Python dictionary, and then use python's method can easily achieve my requirements.
If the requirements can also be achieved with postgresql statements, which method is more recommended than the Python dictionary?
I am not sure I completely understand what the result is you want. But if you want to filter on the Id, you need to unnest all the elements inside the JSON column:
select d.v ->> 'Desc' as description
from the_table t
cross join jsonb_each(t.data) as d(k,v)
where d.v ->> 'Id' = '17636'
You could use the new jsonpath notation of PostgreSQL v12:
SELECT data ## '$.* ? (#.Id == "17636").Desc == "yes"'
FROM some_table;
That will start with the root of data ($), find any attribute in it (*), filter only those attributes that contain an Id with value "17636", get their Desc attribute and return TRUE only if that attribute is "yes".
Nice, isn't it?
This will probably give you what you need.
select value->>'Desc' from jsonb_each('{
"sku0": {
"Id": "18418",
"Desc": "yes"
},
"sku1": {
"Id": "17636",
"Desc": "no"
},
"sku2": {
"Id": "206714",
"Desc": "yes"
},
"brand": "abc",
"displayName": "something"
}'::jsonb)
where key like 'sku%'
and value->>'Id'='17636'
Best regards,
Bjarni

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

Create Couchbase Index on Array field

Hi how to create index on array field my sample doc is
{
"name": [ {
"family": "Smith",
"given": [
"Kam"
],
"prefix": [
"Mrs."
],
"use": "official"
},
{
"family": "Johns",
"given": [
"Kam"
],
"use": "maiden"
}
]
}
I want to write a search query (like) on family and given fields ...How to create a index and suggest query ..Im new to couchbase
This query that selects the customers with family name "Smith" and given name "Kam":
select * from customer
where any n in name satisfies n.family = 'Smith' and
any fn in n.given satisfies fn = 'Kam' end end
Note the use of a nested ANY clause because of the use of a nested array in the data.
You can then create an index on the family name like this:
CREATE INDEX customer_name ON customer
( DISTINCT ARRAY n.family FOR n IN name END)
The index gets used without any hints. You can see that it is being used by adding EXPLAIN to the beginning of the query. That will get you a query plan in JSON that includes an index scan operator.
You can learn more about array indexing here:
https://developer.couchbase.com/documentation/server/current/n1ql/n1ql-language-reference/indexing-arrays.html

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