Count jsonb column where data is LIKE a sting in PostgreSQL 12 - postgresql

I have data in a jsonb column that looks like this...
{
"1": {
"Answer": "Incorrect:No"
},
"2": {
"Answer": "Correct:The troubleshooting steps are correct",
"Comment": "Computer was not restarted."
},
"3": {
"Answer": "Correct:The actions taken were correct"
},
"4": {
"Answer": "Correct:Clear next steps were provided.",
"Comment": "Followup on fixing this issue."
}
}
What I want to do is get a count by question (1-4) of how many records have start with "Incorrect". I have the following query...
SELECT Count(reviews) FROM reviews WHERE review->'1'->>'Answer' LIKE 'Incorrect:%'
This will give me a count for that one question but I don't want to have 4 queries if I can help it. I've tried...
SELECT
Count(review->'1'->>'Answer' LIKE 'Incorrect:%') AS "Count1",
Count(review->'2'->>'Answer' LIKE 'Incorrect:%') AS "Count2"
FROM reviews;
But that counted all columns. Any ideas?

demo:db<>fiddle (Note that I made answer 4 incorrect as well for testing purposes)
SELECT
COUNT(*)
FROM mytable,
json_each(mydata) as data
WHERE data.value ->> 'Answer' LIKE 'Incorrect%'
json_each() extracts all JSON elements into an own row. This can be used to check them separately and group their results afterwards.

Related

Query by object property in child array

I have jsonb documents in Postgres table like this:
{
"Id": "267f9e75-efb8-4331-8220-932b023b3a34",
"Name": "Some File",
"Tags": [
{
"Key": "supplier",
"Value": "70074"
},
{
"Key": "customer",
"Value": "1008726"
}
]
}
My working query to find documents where Tags.Key is supplier is this:
FROM docs
WHERE EXISTS(
SELECT TRUE
FROM jsonb_array_elements(data -> 'Tags') x
WHERE x ->> 'Key' IN ('supplier')
I wanted to find a shorter way and tried this:
select * from docs where data->'Tags' #> '[{ "Key":"supplier"}]';
But then I get this error for the syntax of #>:
<operator>, AT, EXCEPT, FETCH, FOR, GROUP, HAVING, INTERSECT, ISNULL, LIMIT, NOTNULL, OFFSET, OPERATOR, ORDER, TIME, UNION, WINDOW, WITH or '[' expected, got '#'
My questions are: is there a shorter working query and what's wrong with my second query?
It's actually been an IDE issue: https://youtrack.jetbrains.com/issue/RIDER-83829/Postgres-json-query-error-but-works-in-DataGrip

Mongodb query getting slow even after indexing

I have a collection let's say Fruits in db. which has following fields
{
"_id": ObjectId(...),
"item": "Banana",
"category": ["food", "produce", "grocery"],
"location": "4th Street Store",
"stock": 4,
"type": "cases"
}
There is an index by default on _id, and i added another index which is,
{
"item": "1",
"category": "1",
"stock": "1",
"type": "1"
}
this collection has data of thousands , and my query response is slow. My query is.
After the index which I mentioned above, Do I need to add all these
checks in my query or I can use any on the keys added in the index ?
Like, currently my queries are like
fruits.find({item: 'new'});
fruits.find({item: 'new', category: 'history'});
fruits.find({stock: '5', category: 'drama'});
fruits.find({type: 'new'});
Is my index which has all these keys is enough for this or I need to
created different indexes for all these combination of keys which I
mentioned above?
Sometimes I am using query and sometimes I am using aggregation on some other collections and lookup for this fruits collections and then doing search etc..
{
"item": "1",
"category": "1",
"stock": "1",
"type": "1"
}
This index will partially work for the following.
fruits.find({item: 'new'}); **Will work (Partially)**
fruits.find({item: 'new', category: 'history'}); **Will work (Partially)**
fruits.find({stock: '5', category: 'drama'}); **Won't work**
fruits.find({type: 'new'}); **Won't work**
Partially => The index is basically an addition in a B-Tree data structure in MongoDB which maps a document in the system. The index prefix on item allows the index to work for first and second query you mentioned but it would be a collection scan for third and last one.
Read about prefixes here.
You need to properly understand indexes in the long run, for queries specifically you can seek help but the knowledge gap will become a problem. This brief read will be really useful.
Edit
Aggregation => Depends on part of the query, mostly only for match you can use index thereafter everything else happens in memory(Check this for more details). For lookup you fetch the data using index on other collection if you have the index on it (again the match part) but after fetching that data whatever you do extra on it would be done in memory. Logically, mostly the fetching of data will be where indexes will be used anyway, for sorting part read the document linked above.

Aggregate results based on array of strings in JSON?

I have a table with a field called 'keywords'. It is a JSONB field with an array of keyword metadata, including the keyword's name.
What I would like is to query the counts of all these keywords by name, i.e. aggregate on keyword name and count(id). All the examples of GROUP BY queries I can find just result in the grouping occuring on the full list (i.e. only giving me counts where the two records have the same set of keywords).
So is it possible to somehow expand the list of keywords in a way that lets me get these counts?
If not, I am still at the planning stage and could refactor my schema to better handle this.
"keywords": [
{
"addedAt": "2017-04-07T21:11:00+0000",
"addedBy": {
"email": "foo#bar.com"
},
"keyword": {
"name": "Animal"
}
},
{
"addedAt": "2017-04-07T20:54:00+0000",
"addedBy": {
"email": "foo#bar.comm"
},
"keyword": {
"name": "Mammal"
}
}
]
step-by-step demo:db<>fiddle
SELECT
elems -> 'keyword' ->> 'name' AS keyword, -- 2
COUNT(*) AS count
FROM
mytable t,
jsonb_array_elements(myjson -> 'keywords') AS elems -- 1
GROUP BY 1 -- 3
Expand the array records into one row per element
Get the keyword's names
Group these text values.

Building query in Postgres 9.4.2 for JSONB datatype using builtin function

I have a table schema as follows:
DummyTable
-------------
someData JSONB
All my values will be a JSON object. For example, when you do a select *
from DummyTable, it would look like
someData(JSONB)
------------------
{"values":["P1","P2","P3"],"key":"ProductOne"}
{"values":["P3"],"key":"ProductTwo"}
I want a query which will give me result set as follows:
[
{
"values": ["P1","P2","P3"],
"key": "ProductOne"
},
{
"values": ["P4"],
"key": "ProductTwo"
}
]
I'm using Postgres version 9.4.2. I looked at documentation page of the same, but could not find the query which would give the above result.
However, in my API, I can build the JSON by iterating over rows, but I would prefer query doing the same. I tried json_build_array, row_to_json on a result which would be given by select * from table_name, but no luck.
Any help would be appreciated.
Here is the link I looked for to write a query for JSONB
You can use json_agg or jsonb_agg:
create table dummytable(somedata jsonb not null);
insert into dummytable(somedata) values
('{"values":["P1","P2","P3"],"key":"ProductOne"}'),
('{"values":["P3"],"key":"ProductTwo"}');
select jsonb_pretty(jsonb_agg(somedata)) from dummytable;
Result:
[
{
"key": "ProductOne",
"values": [
"P1",
"P2",
"P3"
]
},
{
"key": "ProductTwo",
"values": [
"P3"
]
}
]
Although retrieving the data row by row and building on client side can be made more efficient, as the server can start to send data much sooner - after it retrieves first matching row from storage. If it needs to build the json array first, it would need to retrieve all the rows and merge them before being able to start sending data.

Firebase Combining Query and Pagination

Query can be used to filter a large items down to a smaller number suitable for synchronizing to the client.
Pagination is a also serve the same purpose, that is to limit the items to a smaller numbers suitable to be be fetched by the client.
Consider the following database schema:
"users": {
"-KRyXWjI0X6UvffIB_Gc": {
"active": true,
"name": "John Doe",
"occupation": "Looking for firebase answer"
},
"-KRyXBWwaK112OWGw5fa": {
"active": false,
"name": "Jane Doe",
"occupation": "Still stuck on combining query and pagination"
},
"-KRyWfOg7Nj59qtoCG30": {
"active": true,
"name": "Johnnie Doe",
"occupation": "There is no greater sorrow than to recall in misery the time when we were stuck"
}
}
If I were to get all the active users, it will be like this: (Code in Swift)
let usersRef = ref.child("users");
let query = usersRef.queryOrderedByChild("active")
.queryEqualToValue(true)
After that filtering, it left me with 10,000 users. Fetching all of those users at the same time is out of question. It must be paginated.
To do the pagination, I have to do the query on the unique sorted value, which is none other than the key itself. This is how it looks now:
let usersRef = ref.child("users");
let query = usersRef.queryOrderedByChild("active")
.queryEqualToValue(true)
let usersPerPage = 10;
query.queryOrderedByKey()
.queryStartingAtValue(lastKey)
.queryLimitedToFirst(usersPerPage)
This wouldn't work because:
You can only use one order-by method at a time. Calling an order-by
method multiple times in the same query throws an error.
After I spent 2 days on thinking how am I supposed to solve this situation, I can only came up with this "anti best practice" solution.
I modified the database schema. I convert the active boolean value to the string and append it after the key to give the order importance control to the key. This is how it looks now:
"users": {
"-KRyXWjI0X6UvffIB_Gc": {
"key_active": "-KRyXWjI0X6UvffIB_Gc true"
"active": true,
"name": "John Doe",
"occupation": "Looking for firebase answer"
}
}
Now I could do both the pagination and the query using the single orderBy:
let usersPerPage = 10;
query.queryOrderedByChild("key_active")
.queryStartingAtValue("\(lastKey) true")
.queryLimitedToFirst(usersPerPage)
Somehow my brain reject the idea of having the key inside the key because it's the worst dirty solution it can be. I want to know the right solution for this particular situation, any solution would be greatly appreciated.
Just add this to your JSON tree:-
active_Users :{
uid1 : {
index : 1,
name : "John Doe"
},
uid2 : {
index : 2,
name : "Johnnie Doe"
}
}
After this just follow this answer :- Retrieving 5 user's at a time, modify it according to your requirements.
Note:- Total no of users/posts in that answers is being retrieved by doing children count. Given your heavy database, you might wanna store totalNoOfUser in a separate node and increment it every time a new user is added.