postgresql jsonb - from list of integers to list of Objects - postgresql

I have a question regarding jsonb in postgresql.
I have a table that has a column of type jsonb, where I store a list of integers.
For example list_integers column:
[1, 2, 3, 4]
I want to add a new column in this table, and insert in this column the same IDs, but the form would be list of objects, where the ID field corresponds to the integer.
For example list_ids column:
[{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]
What would be the best way to do this?

To transform:
test=> SELECT jsonb_agg(jsonb_build_object('id', id))
test-> FROM jsonb_array_elements(jsonb '[1, 2, 3, 4]') id;
jsonb_agg
----------------------------------------------
[{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}]
(1 row)```

Related

Using subquery results as a condition of jsonb field indexed search

I have two following tables:
CREATE TABLE elements(
id INT PRIMARY KEY,
element_type INT,
api_name VARCHAR(100) UNIQUE
);
CREATE TABLE entries(
id INT PRIMARY KEY,
elements JSONB
)
entries.elements field stored a jsonb-array of elements in following format:
[
{
"id": elements.id
"type": elements.element_type
"value": <some_value>
},
{
"id": elements.id
"type": elements.element_type
"value": <some_value>
}
...
]
The value field can be one of 3 types:
Literal (i.e. "abc", 12, true)
Array of literals (i.e. ["abc", "cbd"], [12, 21])
Array of objects (i.e. [{"node_id": <uuid>, "value": "abc"}, {"node_id": <uuid>, "value": "cba"}])
And I have index (maybe incorrect)
CREATE INDEX some_idx_name
ON entries
USING GIN (elements jsonb_ops)
For example suppose that I have data in these tables:
INSERT INTO elements(id, element_type, api_name) VALUES
(1, 1, 'el_number_1'),
(2, 2, 'el_datetime'),
(3, 3, 'el_files'),
(4, 1, 'el_number_2');
INSERT INTO entries(id, elements) VALUES
(1, '[{"id": 1, "type": 1, "value": 12}, {"id": 3, "type": 3, "value": [1, 2]}]'::JSONB),
(2, '[{"id": 4, "type": 1, "value": 12}, {"id": 2, "type": 2, "value": [{"date": "20.12.2022", "time": "16:18"}]}]'::JSONB);
So if I want to find entry by elements I need to do something like this:
SELECT id
FROM entries
WHERE elements #? '$[*] ? (#.id == 4 && #.value = 12)'
But how can I find entry by value of elements which was found by api_name?
-- DOES NOT WORK (only for example what I need)
SELECT id
FROM entries
WHERE elements #? '$[*] ? (#.id == (SELECT id FROM elements WHERE api_name = 'el_number_2') && #.value = 12)'
Fiddle link

How do I create a Postgres table with a column that holds an array of objects?

The documentation shows that arrays can be created as either text or integer. I did not find an example for creating an object array. An example is the items column:
CREATE TABLE cart (
_id serial PRIMARY KEY,
user_id Integer UNIQUE NOT NULL,
items varchar [],
FOREIGN KEY (user_id)
REFERENCES users (_id)
Object stored in the items array have quotes around them making them strings instead of objects. A code segment creating objects in items is shown below:
update cart
set items =
array_append(items,
'{product_id: ${cartItem.productId}, size: ${cartItem.size}, quantity: ${cartItem.quantity}}')
where _id = ${cart._id}
and user_id = ${userId}
RETURNING *
I am compelled to put quotes around the object value. As a result it is stored as a string in the column with quotes around it.
cart {
_id: 1,
user_id: 1,
items: [
'{product_id: 1, size: Small, quantity: 1}',
'{product_id: 1, size: Small, quantity: 1}',
'{product_id: 1, size: Small, quantity: 1}'
]
}
Because the items in the items column are stored as string instead of objects, I cannot correctly iterate over them in my program. For example,
items.product_id does not exist.
How do I fix this?
I wrote some examples for you
Example 1. (Insert into JSONB type using converting String to JSONB)
INSERT INTO cart
(
product_id,
quantity,
"size",
user_id,
items
)
values
(
1,
1,
'Small',
1,
'[
{"size": "Small", "quantity": 1, "product_id": 1},
{"size": "Small", "quantity": 1, "product_id": 1},
{"size": "Small", "quantity": 1, "product_id": 1}
]'::jsonb
);
Example 2. (Convert fields of selected table to JSONB using row_to_json)
select row_to_json(t1) as your_json from (
select product_id, "size", quantity from cart
) t1
Result:
your_json
--------------------------------------------
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
Example 3. (Concat all rows of type JSONB)
select jsonb_agg(row_to_json(t1)) as your_json from (
select product_id, "size", quantity from cart
) t1
Result:
your_json
---------------------------------------------------------------------------------------------------------------------------------------------------------
[{"size": "Small", "quantity": 1, "product_id": 1}, {"size": "Small", "quantity": 1, "product_id": 1}, {"size": "Small", "quantity": 1, "product_id": 1}]
Example 4. (Similar of row_to_json)
select
json_build_object('product_id', product_id, 'size', size, 'quantity', quantity) as your_json
from cart;
Result:
your_json
--------------------------------------------
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
Example 5. (Concatenate many JSONB objects using ||)
select jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb) || jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb) as your_json
-- or
select jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb, '{"product_id":1,"size":"Small","quantity":1}'::jsonb) as your_json
Result:
your_json
------------------------------------------------------------------------------------------------------
[{"product_id": 1, "size": "Small", "quantity": 1}, {"product_id": 1, "size": "Small", "quantity": 1}]
Example 6. (Similar example to your update query (using concatenating JSONB types)
update cart
set
items = jsonb_build_array(items) || jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb)
where id = 1
Result:
items
------------------------------------------------------------------------------------------------------
[{"product_id": 1, "size": "Small", "quantity": 1}, {"product_id": 1, "size": "Small", "quantity": 1}]
Recommended using JSON or JSONB types. In JSON types you can perform any operations on keys or values, such as filtering, selecting, joining. If you need you can even show JSON keys and values as table record view.
Examples:
items: '[
{"product_id": 1, "size": "Small", "quantity": 1},
{"product_id": 1, "size": "Small", "quantity": 1},
{"product_id": 1, "size": "Small", "quantity": 1}
]'
select
json_array_elements(items)->'product_id' as product_id,
json_array_elements(items)->'size' as "size",
json_array_elements(items)->'quantity' as quantity
from cart
where user_id = 1;
Result:
product_id
size
quantity
1
"Small"
1
1
"Small"
1
1
"Small"
1

MongoDB - Is it possible to only insert a record when the record doesn't exist

I have a database with custom ids and I only want to insert a new record if the id is different from the other ids. If the id exists I don't want to update the value (so I think upsert isn't a solution for me).
I'm using the pymongo connector.
Example Database:
[
{"_id": 1, "name": "john"},
{"_id": 2, "name": "paul"}
]
Trap and ignore the DuplicateKeyError, e.g.:
pymongo import MongoClient
from pymongo.errors import DuplicateKeyError
db = MongoClient()['mydatabase']
records = [
{"_id": 1, "name": "john"},
{"_id": 2, "name": "paul"},
{"_id": 3, "name": "ringo"}
]
for record in records:
try:
db.mycollection.insert_one(record)
print (f'Inserted {record}')
except DuplicateKeyError:
print (f'Skipped duplicate {record}')
pass
Result (something like):
Skipped duplicate {'_id': 1, 'name': 'john'}
Skipped duplicate {'_id': 2, 'name': 'paul'}
Inserted {'_id': 3, 'name': 'ringo'}

Reconstruct JSONB Nested Arrays

I have the following JSONB column called value.
create temp table settings as
select
'{"favorites": [
{
"listings": [
{"id": "aa92f346-7a93-4443-949b-4eab0badd983", "version": 1},
{"id": "cd92e346-6b04-3456-050a-5eeb0bddd027", "version": 3},
{"id": "cd92e346-6b04-3456-050a-5eeb0bddd333", "version": 2}
]
}
]}'::jsonb as value;
So I'm trying to remove items from this nested array and reconstruct it like so
select jsonb_set(value,'{favorites}',jsonb_set('{}','{listings}',
jsonb_agg(new_elems)) ) as ne from settings s ,
lateral ( select jsonb_array_elements(elem->'listings')as new_elems
from jsonb_array_elements(value->'favorites')elem) sets where
sets.new_elems->>'id' != 'aa92f346-7a93-4443-949b-4eab0badd983'
group by s.value
Which works but the "favorites" array is flattened.
Instead of
"{"favorites": [{"listings": [{"id": "cd92e346-6b04-3456-050a-5eeb0bddd027", "version": 3}, {"id": "cd92e346-6b04-3456-050a-5eeb0bddd333", "version": 2}]}]}"
I get
"{"favorites": {"listings": [{"id": "cd92e346-6b04-3456-050a-5eeb0bddd027", "version": 3}, {"id": "cd92e346-6b04-3456-050a-5eeb0bddd333", "version": 2}]}}"
Does anyone have an idea on how can I solve this?.
you can create jsonb arrays by using the functions JSONB_AGG or JSON_BUILD_ARRAY. JSONB_AGG is an aggregate function, whereas JSONB_BUILD_ARRAY puts all provided arguments in an array.
Below, I used JSONB_BUILD_ARRAY to make favorites into an json array rather than a key-value
SELECT
JSONB_SET(
'{}', '{favorites}',
JSONB_BUILD_ARRAY(
JSONB_SET('{}', '{listings}', JSONB_AGG(listings))
)
)
FROM settings,
LATERAL (SELECT
JSONB_ARRAY_ELEMENTS(JSONB_ARRAY_ELEMENTS(value->'favorites')->'listings') listings
FROM settings) listings
WHERE listings.listings->>'id' != 'aa92f346-7a93-4443-949b-4eab0badd983'

QueryingJSON PostgreSQL

CREATE TABLE tableTestJSON (
id serial primary key,
data jsonb
);
INSERT INTO tableTestJSON (data) VALUES
('{}'),
('{"a": 1}'),
('{"a": 2, "b": ["c", "d"]}'),
('{"a": 1, "b": {"c": "d", "e": true}}'),
('{"b": 2}');
I can select the values. There is no problem this.
SELECT * FROM tableTestJSON;
I can test that two JSON objects are identical this query.
SELECT * FROM tableTestJSON WHERE data = '{"a":1}';
This query's output is :
id | data
----+------
1 | {"a": 1}
(1 row)
But i have a problem :
Lets say I have a column:
{a: 30}
{a: 40}
{a: 50}
In this case, how can i query for all the elements containing a = 30 or a = 40. I was not able to find any solution for 'or', e.g.
select * from table where a in (10,20); // ??
or
How can I query on such condition?
Extract a value of a json object using the operator ->>:
select *
from tabletestjson
where (data->>'a')::int in (1, 2)
id | data
----+--------------------------------------
2 | {"a": 1}
3 | {"a": 2, "b": ["c", "d"]}
4 | {"a": 1, "b": {"c": "d", "e": true}}
(3 rows)