jsonb find a value in an array - sql

There is such a data structure:
Column "recipient" type jsonb
{
"phoneNumbers": [
{
"isDefault": true,
"type": "MOBILE",
"number": "3454654645"
},
{
"isDefault": true,
"type": "MOBILE",
"number": "12423543645"
}
]
}
I need to write a search request by number. In the postgres documentation, I did not find a search by value in an array, only it is obtained by an index. It doesn't suit me
I made a query like this, it gets executed, but are there any other ways to search through an array?
SELECT *
FROM my_table
WHERE recipient -> 'phoneNumbers' #> '[{"number":3454654645}]'

That's pretty much the best way, yes.
If you have a (GIST) index on recipient the index would not be used by your condition. But the following could make use of such an index:
SELECT *
FROM my_table
WHERE recipient #> '["phoneNumbers": {"number":3454654645}]}'
If you are using Postgres 12 or later, you can also use a JSON path expression:
SELECT *
FROM my_table
WHERE recipient ## '$.phoneNumbers[*].number == "12423543645"'
If you can't pass a JSON object to your query, you can use an EXISTS sub-select:
SELECT mt.*
FROM my_table mt
WHERE EXISTS (SELECT *
FROM jsonb_array_elements_text(mt.recipient -> 'phoneNumbers') as x(element)
WHERE x.element ->> 'number' = '3454654645')
The '3454654645' can be passed as a parameter to your query. This will never make use of an index though.

Related

Check if row contains a nested item in DynamoDB?

I am trying to use PartiQL with DynamoDB to perform SQL queries to check if a device is inactive and contains an error. Here's is the query I am using:
SELECT *
FROM "table"
WHERE "device"."active" = 0 AND "device"."error" IS NOT NULL
However I've noticed that even if a device doesn't have the error item, the query still returns a row. How can I query a device that only contains the error item?
With error item
{
"id": "value",
"name": "value,
"device": {
"active": 0,
"error": {
"reason": "value"
}
}
}
Without error item
{
"id": "value",
"name": "value,
"device": {
"active": 0
}
}
You're looking for IS NOT MISSING :) That's the partiql version of the filter expression operator function attribute_exists.
Given a table with a primary key PK, sort key SK, and the following data:
PK
SK
myMap
foo
1
{}
foo
2
{"test": {}}
-- Returns both foo 1 and foo 2
SELECT *
FROM "my-table"
WHERE "PK" = 'foo' AND "myMap"."test" IS NOT NULL
-- Returns just foo 2
SELECT *
FROM "my-table"
WHERE "PK" = 'foo' AND "myMap"."test" IS NOT MISSING
Also made sure my example specifies the PK in the WHERE clause - otherwise, your query will be a full scan. Maybe that's what you want, though. Just something to be aware of.

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

Postgres - query JSON column value of nested object

I'm using following schema for the JSONB column of my table (named fields). There are several of these field entries.
{
"FIELD_NAME": {
"value" : "FIELD_VALUE",
"meta": {
"indexable": true
}
}
}
I need to find all the fields that contain this object
"meta": {
"indexable": true
}
Here is a naive attempt at having json_object_keys in where clause, which doesn't work, but illustrates what I'm trying to do.
with entry(fields) as (values('{
"login": {
"value": "fred",
"meta": {
"indexable": true
}
},
"password_hash": {
"value": "88a3d1c7463d428f0c44fb22e2d9dc06732d1a4517abb57e2b8f734ce4ef2010",
"meta": {
"indexable": false
}
}
}'::jsonb))
select * from entry where fields->jsonb_object_keys(fields) #> '{"meta": {"indexable": "true"}}'::jsonb;
How can I query on the value of nested object? Can I somehow join the result of json_object_keys with the table iself?
demo:db<>fiddle
First way: using jsonb_each()
SELECT
jsonb_build_object(elem.key, elem.value) -- 3
FROM
entry,
jsonb_each(fields) as elem -- 1
WHERE
elem.value #> '{"meta": {"indexable": true}}' -- 2
Expand all subobjects into one row per "field". This creates 2 columns: the key and the value (in your case login and {"meta": {"indexable": true}, "value": "fred"})
Filter the records by checking the value column for containing the meta object using the #> as you already mentioned
Recreate the JSON object (combining the key/value columns)
Second way: Using jsonb_object_keys()
SELECT
jsonb_build_object(keys, fields -> keys) -- 3
FROM
entry,
jsonb_object_keys(fields) as keys -- 1
WHERE
fields -> keys #> '{"meta": {"indexable": true}}' -- 2
Finding all keys as you did
and 3. are very similar to the first way

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