Postgres jsonb to table - postgresql

Is it possible to return jsonb array of arrays as a table object?
The size of the inner arrays will not always be the same.
Postgres version 9.6.6 is being used.
Example jsonb:
{
"r": [
{
"n": "name",
"rcs": [
[
{
"v": "1",
"vt": 9
},
{
"v": "2",
"vt": 9
},
{
"v": "3",
"vt": 9
}
],
[
{
"v": "4",
"vt": 9
},
{
"v": "5",
"vt": 7
}
]
]
}
]
}
Expected table
+------+------+--------+
| Col1 | Col2 | Col3 |
+------+------+--------+
| 1 | 2 | 3 |
| 4 | 5 | Null |
+------+------+--------+
The width of the returned table will be determined by the max length of all rows.
Excess columns for shorter rows will have null values.
I am completely new to Postgres, and not even sure where to start.
This was as far as I got:
select c from someTable,
jsonb_array_elements(data -> 'r') r,
jsonb_array_elements(r -> 'rcs') c
WHERE r->> 'n' = 'name'

Related

Get all paths of matching jsonpath filter

I'm using PostgreSQL 14.
I need to find all paths that matches the jsonpath filter I give.
Input example
{
"A": [
{
"B": [
[
{
"name": "id",
"conditions": [
{
"validator": "nonnull"
}
]
},
{
"name": "x",
"conditions": [
{
"validator": "required"
}
]
},
{
"name": "y",
"rules": []
}
],
[
{
"name": "z",
"conditions": [
{
"validator": "required"
}
]
}
]
]
}
]
}
JsonPath filter
Every A.B which has a required validator condition:
$.A.B[*].conditions ? (#.validator == "required")
Expected output (Or something close)
{A,0,B,0,1,conditions,0}
{A,0,B,1,0,conditions,0}
This is a relational solution, you may define the query as a view and filter on the validator and you get the array indexes.
select a.index-1 as a,
b.index-1 as b,
b2.index-1 as b2,
c.item ->> 'validator' as validator,
c.index-1 as conditions
from t cross join
jsonb_array_elements(jsn -> 'A') WITH ORDINALITY a(item, index) cross join
jsonb_array_elements(a.item -> 'B') WITH ORDINALITY b(item, index) cross join
jsonb_array_elements(b.item) WITH ORDINALITY b2(item, index) cross join
jsonb_array_elements(b2.item -> 'conditions') WITH ORDINALITY c(item, index);
a|b|b2|validator|conditions|
-+-+--+---------+----------+
0|0| 0|nonnull | 0|
0|0| 1|required | 0|
0|1| 0|required | 0|

MongoError: PlanExecutor error during aggregation

I have tree records in mongodb but there could be many more, I'm getting shops by an ID coming from frontend
I need to get 20 records and group them by itemId and colorId, and get counts for every shop. the count of shops can be 1,2,3,....10etc..
this is output I need:
+--------+----------+-------+-------+-------+
| itemId | colorId | shop1 | shop2 | shop3 |
+========+==========+=======+=======+=======+
| 1 | colorId1 | 5 | 0 | 3 |
+--------+----------+-------+-------+-------+
| 2 | colorId2 | 3 | 0 | 0 |
+--------+----------+-------+-------+-------+
| 3 | colorId2 | 0 | 3 | 0 |
+--------+----------+-------+-------+-------+
| 2 | colorId1 | 0 | 5 | 0 |
+--------+----------+-------+-------+-------+
| 3 | colorId1 | 0 | 0 | 5 |
+--------+----------+-------+-------+-------+
here is my data and query - here shopId is string and it's work good.
but when I use this query on my local mashine, I'm getting this error:
MongoError: PlanExecutor error during aggregation :: caused by :: $arrayToObject requires an object with keys 'k' and 'v', where the value of 'k' must be of type string. Found type: objectId
but when I change shopId to the ObjectId I'm getting error.
ObjectId versoin
Per your request in the comments (if I got it right):
db.collection.aggregate([
{
"$match": {}// <-- Highly recommend you to use match due to the complexity of this query
},
{
$group: {
_id: 0,
data: {
$push: {
shopId: "$shopId",
shopItems: "$shopItems"
}
},
shopIds: {
"$push": {
shopId: "$shopId",
"count": 0
}
}
}
},
{
$unwind: "$data"
},
{
$unwind: "$data.shopItems"
},
{
$group: {
_id: {
itemId: "$data.shopItems.itemId",
colorId: "$data.shopItems.colorId"
},
data: {
$push: {
shopId: "$data.shopId",
count: "$data.shopItems.itemCount"
}
},
existing: {
$push: {
shopId: "$data.shopId",
"count": 0
}
},
shopIds: {
$first: "$shopIds"
}
}
},
{
"$addFields": {
"missing": {
"$setDifference": [
"$shopIds",
"$existing"
]
}
}
},
{
$project: {
data: {
$concatArrays: [
"$data",
"$missing"
]
}
}
},
{
$unwind: "$data"
},
{
$sort: {
"data.shopId": 1
}
},
{
$group: {
_id: "$_id",
counts: { // here you can change this key
$push: "$data"
},
totalCount: {
$sum: "$data.count" // if you want it
}
}
}
])
After the first $match, we $group in order to get all shopIds in each document.
Next we $unwind and $group by the group you wanted: by colorId and itemId. Then we are adding all the shops with count 0 and removing the ones that do have actual count. Last three steps are just for sorting, summing and formating.
You can play with it here.

How to port ARRAY_AGG with mulitple arguments in Postgresql to Snowflake

Snowflake supports ARRAY_AGG but it can take only parameter while Postgresql's version supports multiple.
I need to port the following snippet in Posgresql to Snowflake:
ARRAY_AGG(state, city, zipcode)
where state, city and zipcode are fields in one of my tables.
Any workaround? I know I can create 3 separate fields but that's not desired.
Depending if you are want an Array of Array's or an Array of Objects
WITH r AS (
SELECT column1 AS A, column2 AS B FROM (VALUES (1,'A'),(14,'B'),(35,'C'),(91,'D'),(105,'E'))
)
SELECT ARRAY_AGG(ARRAY_CONSTRUCT(a,b)) FROM r;
gives:
[ [ 1, "A" ], [ 14, "B" ], [ 35, "C" ], [ 91, "D" ], [ 105, "E" ] ]
or
WITH r AS (
SELECT column1 AS A, column2 AS B FROM (values (1,'A'),(14,'B'),(35,'C'),(91,'D'),(105,'E'))
)
SELECT ARRAY_AGG(OBJECT_CONSTRUCT('A',a,'B',b)) FROM r;
gives:
[ { "A": 1, "B": "A" }, { "A": 14, "B": "B" }, { "A": 35, "B": "C" }, { "A": 91, "B": "D" }, { "A": 105, "B": "E" } ]
https://docs.snowflake.net/manuals/sql-reference/functions/array_agg.html
https://docs.snowflake.net/manuals/sql-reference/functions/array_construct.html
https://docs.snowflake.net/manuals/sql-reference/functions/object_construct.html

Aggregate counting logical values

Hello friend I am not friendly with mongodb aggregation I want is that I have array of object that contains subjects with its score for each question and I am using node js so I want is that full calculation with mongo query if possible that include subject name with its total score and count of attempt and not attempt my Json Array is as bellow
{
"examId": ObjectId("597367af7d8d3219d88c4341"),
"questionId": ObjectId("597368207d8d3219d88c4342"),
"questionNo": 1,
"subject": "Reasoning Ability",
"yourChoice": "A",
"correctMark": "1",
"attempt": true,
"notAttempt": false,
}
here in object one field is for correct marks subject are different an I want an output like
|Subject Name | Total attempts | total not attempts | total score |
| A | 5 | 3 | 10 |
| B | 10 | 5 | 25 |
I am trying with aggregation but not done yet I have tried this query
db.examscores.aggregate([
{ $group:{
_id:"$examId",
score: { $sum: '$correctMark' },
count: { $sum: 1 }
}}
])
Any one has idea how to achieve this type of output.
and if another way to achieve this using node than also good.
I have solved this here is my query
[
{ $match: { subject:'Reasoning Ability' } },
{
$group:
{
_id:{id:"$examId",subject:'$subject'},
totalAttempt: { $sum: {$cond : [ "$attempt", 1, 0 ]} },
totalNotAttempt: { $sum: {$cond : [ "$notAttempt", 1, 0 ]} },
markedForReview:{ $sum: {$cond : [ "$markedForReview", 1, 0 ]} },
answerAndMarkedForReview:{ $sum: {$cond : [ "$answerAndMarkedForReview", 1, 0 ]} },
score: { $sum: '$correctMark' },
count: { $sum: 1 }
}
}
]

MongoDB select distinct and sort

Consider i have data like this:
| id | property | score | remark |
-------------------------------------------
| aaaa | alpha | 1 | alpha lowest |
| bbbb | beta | 2 | beta highest |
| cccc | alpha | 2 | alpha highest |
Given data above, i want to select distinct by field property and sort it by score highest value, so the expected results will be
| id | property | score | remark |
-------------------------------------------
| bbbb | beta | 2 | beta highest |
| cccc | alpha | 2 | alpha highest |
How to do this with mongodb?
You can easily do this by running the following aggregation pipeline operation:
db.collection.aggregate([
{ "$sort": { "score": -1 } },
{
"$group": {
"_id": "$property",
"id": { "$first": "$_id" },
"score": { "$first": "$score" },
"remark": { "$first": "$remark" }
}
},
{
"$project": {
"_id": 0,
"property": "$_id",
"score": 1,
"remark": 1,
"id": 1
}
}
])
Sample Output
{ "id" : "bbbb", "score" : 2, "remark" : "beta highest ", "property" : "beta" }
{ "id" : "cccc", "score" : 2, "remark" : "alpha highest ", "property" : "alpha" }
MongoDB has a distinct operator. However, it only returns one "column". You'd need to do multiple queries.
The best option is to use MongoDB Aggregation Framework. It works using a pipeline of operations.
I don't know the data, but it could be someting like:
db.collection.aggregate([
{ '$group' : { _id : '$property' , score: { '$max': '$score' } } }
])
You can use Robomongo client to experiment with this method.