Postgres/jOOQ replace jsonb[] element - postgresql

I'm having a Spring application with jOOQ and Postgresql database having a table (issues) with the following two columns:
id (Long)
documents (jsonb[]) <- array of jsonb (not jsonb array)
The document json structure is on the following format:
{
"id": (UUID),
"name": (String),
"owner"; (String)
}
What I want to achieve is to be able to replace documents with matching id (normally only one) with a new document. I'm struggling with the jOOQ or even the plain SQL.
I guess I need to write some plain SQL in jOOQ to be able to do this but that is ok (to a minimum). I had an idea to do the following:
Unnest the document column
Filter out the document that should be updated of the array
Append the document that should be updated
Store the whole array
Raw SQL looks like this but missing the new document to be added:
UPDATE issues SET documents = (SELECT ARRAY_AGG(doc) FROM issues, UNNEST(issues.documents) AS doc WHERE doc->>'id' != 'e4e3422f-83a4-493b-8bf9-37980d532538') WHERE issues.id = 1;
My final goal is to write this in jOOQ and append the document to be replaced. I'm using jOOQ 3.11.4.

You should be able to just concatenate arrays in PostgreSQL:
UPDATE issues
SET documents = (
SELECT ARRAY_AGG(doc) || '{"id":"e4e3422f-83a4-493b-8bf9-37980d532538","name":"n"}'::jsonb
FROM issues, UNNEST(issues.documents) AS doc
WHERE doc->>'id' != 'e4e3422f-83a4-493b-8bf9-37980d532538'
)
WHERE issues.id = 1
Some common array functions will be added to jOOQ in the near future, e.g. array concatenation, but you can get away for now with plain SQL templating I suspect?

Related

Error when filtering some data with like and jsonb in PostgreSQL

I keep having a problem when filtering some data in postgresql.
For example, I want to filter by a json.
My jsons are saved in the following way
"[{\"Brand\":\"Leebo\"},{\"Housing Color\":\"Black\"},{\"Beam Type\":\"High Beam, Low Beam\"}]"
And let's say that I want to filter after
[{\"Brand\":\"Leebo\"}]
Shouldn't I write something like that in the query?
SELECT * FROM public.products
WHERE attributes is not NULL
AND attributes::text LIKE '%{\"Brand\":\"Leebo\"}%';
I tried also
SELECT * FROM public.products WHERE attributes::jsonb #> '"[{\"Material\":\"Artificial Leather\"}]"'
Because I won't receive data
Do you know how I could proceed differently?
But it only works if the column has all the data (eg if I give the exact data that is in the column)
Also, how could I search with whereIn?
You have an array in your JSONB because those characters ([ and ]) are array characters. If you are sure that you will always have only one array in your JSONB so you can use this:
SELECT * FROM public.products
WHERE attributes is not NULL
AND attributes[0]->>'Brand' = 'Leebo'
But if you can have several arrays inside JSONB then use jsonb_array_elements for extracting array elements, after then you can use JSONB operations like as ->>

Is there a way to quickly check every table in a mongodb database with the column "title"?

Is there a way to quickly check every table in a mongodb database with the column "title"? I need to identify every table or rather collection where there's a column with the word "title", is there a way to do this using a mongodb query?
In Mongo there is no straight forward query to check all collections and fields. Instead, you can get a list of all collections using getCollectionInfos and then query each collection to see if there exists the field that you are looking for.
db.getCollectionInfos().forEach(function(c){
result = db.getCollection(c.name).findOne({"title":{$exists:true}});
if(result != null){
print(c.name);
}
}
);
This will not look for nested documents, though.

MongoDB Insert Entity vs a BsonDocument using via C# Driver

I am using the .NET MongoDB driver to insert values into my collection in MongoDB .
I noticed when I insert an entity the objects get stored as simple columns like in the second document below where as when I used a BsonDocument to insert documents they get inserted as an object under the _v column in the first document below.
Can someone explain whats the difference between the two?
Also is it possible to insert the BsonDocument like in the second document via the .NET driver? In my case I have to build the document Dynamically since I dont have a concrete entity to insert with.
The result you're seeing is related to dynamic handling. Mongo driver doesn't know which type you use and save this information in _t field. To fix it you just need to create collection based on BsonDocument directly like:
var coll = db.GetCollection<BsonDocument>("coll");
coll.InsertOne(new BsonDocument("name", "value"));
See this doc
So with the .NET driver:
If I insert the bson object directly the document is inserted as an object with column "_v"
If I insert the bson object after deserializing it using the BsonSerializer it can be inserted as simple columns in MongoDB.
var collection = database.GetCollection<dynamic>(collectionName);
var bson = new BsonDocument();
...
// Fill bson object
...
collection.InsertOne(bson); // Image 1
collection.InsertOne(BsonSerializer.Deserialize<dynamic>(bson)); // Image 2

Index on JSON field with dynamic keys

I'm on PG 9.5 and I have a table Visitors(id, data::json)
Example:
Visitor(id: 1, data: {name: 'Jack', age: 33, is_user: true })
I'd like to perform queries like
Give me all visitors named Jack and age > 25
Give me all visitors who are users, but where name is unspecified (key not in json)
The keys inside the data column user-specified and as such are dynamic.
Which index makes the most sense in this situation?
You can use a GIN index on a jsonb column, which gives you generalized, dynamic indexing of keys and values inside JSON value.
CREATE TABLE visitors (
id integer,
data jsonb
);
CREATE INDEX idx_visitors_data ON cards USING GIN (data);
SELECT * FROM visitors
WHERE data -> 'is_user' AND NOT data ? 'name';
Unfortunately, GIN indexes don't support numeric range comparisons. So while you could still issue a query for visitors named Jack aged over 25:
SELECT * FROM visitors
WHERE data #> '{"name": "Jack"}' AND ((data ->> 'age')::integer) > 25;
This will only use the index to find the name "Jack", and possibly to find rows which have an "age" key, but the actual test that the ages are over 25 will be done as a scan over the matching rows.
Note that if you really need range comparisons, you can still add non-GIN indexes on specific paths inside the JSON value, if you expect them to appear often enough to make that worthwhile. For example, you could add an index on data -> 'age' that supports range comparisons:
CREATE INDEX idx_visitors_data_age ON visitors ( ((data ->> 'age')::integer) );
(note the extra parentheses; you'll get an error without them).
See this excellent blog post for further information.
You can look at additional extension JsQuery – is a language to query jsonb data type, it provides an additional functionality to jsonb (currently missing in PostgreSQL), such as a simple and effective way to search in nested objects and arrays, more comparison operators with indexes support. Read more here: https://github.com/postgrespro/jsquery.
In your cases, you can create jsonb_path_value_ops index:
CREATE INDEX idx_visitors ON visitors USING GIN (jsonb jsonb_path_value_ops);
and use the next queries:
select * from visitors where jsonb ## 'name = "Jack" and age > 25';
select * from visitors where jsonb ## 'not name = * and is_user=true';
I believe the best approach here is to create a raw sql migration:
Run ./manage.py makemigrations --empty yourApp where yourApp is the app of the model you want to change indexes for.
Edit the migration i.e.
operations = [
migrations.RunSQL("CREATE INDEX idx_content_taxonomies_categories ON common_content((taxonomies->>'categories'));")
]
Where idx_content_taxonomies_categories is the name of the index, common_content is your table, taxonomies is your JSONField, and categories in this case is the key you want to index.
That should do it. Cheers!

Using MongoDB to query selected field

I am trying to query out the data from my MongoDB database but there are some fields which I would like to omit as MongoDB will query the whole collections with id, n out.
I did this to limit the query but unfortunately only one field could be omitted but not the other which is the 'n' field. How can I omit two fields?
data = collection.find_one({"files_id": file_id},{"_id":0,"data":1})
And I also realized that my query for data has the field name (u'data') too, how can I query it so that it only returns the data? for this case it's a binary data
Example:
{u'data': Binary('\x00\x00\xed\x00\n\x00\x00\xd5\xa9\x00\x000\x00\x00\x00#\x00\x00\x0f\xff\xf0\x00\x0b\x80\x00\x00\x00
Kindly assist thanks!