How to turn repeating elements in MYSQL to array? - mysql

Currently I've got the following query:
SELECT c.Permlink
, c.ParentID
, c.AuthorID
, u.Username AS Author
, c.Title
, c.Body
, c.Metadata
, c.PostTime
, v.VoterID
, v.VoteValue
, voterID.Username AS Voter
FROM comments c
LEFT
JOIN deleted_comments d
ON c.Permlink = d.Permlink
LEFT
JOIN votes v
ON c.Permlink = v.Permlink
JOIN users u
ON u.id = c.AuthorID
LEFT
JOIN users As voterID
ON voterID.ID = v.VoterID
WHERE c.AuthorID = 88077
AND c.ParentID IS NULL
AND d.Permlink IS NULL
And I get the following result:
[
{
"Permlink" : 3,
"ParentID" : null,
"AuthorID" : 88077,
"Author" : "rishi556",
"Title" : "",
"Body" : "foxon",
"Metadata" : "{}",
"PostTime" : 1589475759,
"VoterID" : 88077,
"VoteValue" : 1,
"Voter" : "rishi556"
},
{
"Permlink" : 5,
"ParentID" : null,
"AuthorID" : 88077,
"Author" : "rishi556",
"Title" : "",
"Body" : "foxonier?",
"Metadata" : "{}",
"PostTime" : 1589475774,
"VoterID" : 1278255,
"VoteValue" : 1,
"Voter" : "gamer556"
},
{
"Permlink" : 5,
"ParentID" : null,
"AuthorID" : 88077,
"Author" : "rishi556",
"Title" : "",
"Body" : "foxonier?",
"Metadata" : "{}",
"PostTime" : 1589475774,
"VoterID" : 88077,
"VoteValue" : 1,
"Voter" : "rishi556"
},
{
"Permlink" : 9,
"ParentID" : null,
"AuthorID" : 88077,
"Author" : "rishi556",
"Title" : "",
"Body" : "does this work?",
"Metadata" : "{}",
"PostTime" : 1589475858,
"VoterID" : null,
"VoteValue" : null,
"Voter" : null
}
]
To reduce repetition, I would like the data in VoteID, VoteValue, Voter to be put into an array, so something like this, using the Permlink : 5 as the example:
[
{
"Permlink" : 5,
"ParentID" : null,
"AuthorID" : 88077,
"Author" : "rishi556",
"Title" : "",
"Body" : "foxonier?",
"Metadata" : "{}",
"PostTime" : 1589475774,
"Votes" : [{"VoterID" : 1278255,"VoteValue" : 1,"Voter" : "gamer556"},{"VoterID" : 88077, "VoteValue" : 1,"Voter" : "rishi556"} ]
}
]
And for those with null in their votes(meaning that it hasn't received votes yet), it should just be an empty array.
What would I need to add to the query to do that? Or would it not be possible with a change to the query and I'd need to manipulate in my code?

Ok, so I see the "Array Table" is a separate table. This means that you have 2 choices in order to solve this problem.
1. In Your Code (Untested!!!!)
Ideally, you can just separate it into 2 queries (one which gets the comment, and one which gets the votes cast on it)
SELECT c.Permlink
, c.ParentID
, c.AuthorID
, u.Username AS Author
, c.Title
, c.Body
, c.Metadata
, c.PostTime
FROM comments c
LEFT
JOIN deleted_comments d
ON c.Permlink = d.Permlink
JOIN users u
ON u.id = c.AuthorID
AND c.ParentID IS NULL
AND d.Permlink IS NULL
And then within that query, loop through the votes table in order to get the votes
SELECT votes.VoterID, votes.VoteValue,(SELECT Users.username FROM users WHERE users.ID = votes.VoterID) FROM votes WHERE comments.Permlink = votes.Permlink
2. Purely in MySQL
If you have to do it in MySQL or have good reason to do so, this is a query which gives you exactly what you want (A JSON string of all the Votes)
SELECT
comments.Permlink,
comments.ParentID,
comments.AuthorID,
users.Username AS Author,
comments.Title,
comments.Body,
comments.Metadata,
comments.PostTime,
CONCAT("[",
(SELECT GROUP_CONCAT(CONCAT('{"voter_id":',votes.VoterID,', "voteValue": ',votes.VoteValue,',"voter_name":"',(SELECT Users.username FROM users WHERE users.ID = votes.VoterID), '"}'))
FROM votes WHERE
comments.Permlink = votes.Permlink),"]") as vote_data
FROM
comments
LEFT JOIN
deleted_comments
ON
comments.Permlink = deleted_comments.Permlink
JOIN
users
ON
users.id = comments.AuthorID
WHERE
comments.AuthorID = 0 AND comments.ParentID IS NULL AND deleted_comments.Permlink IS NULL

Related

best practice to get nested result json

I have cases with nested results in JSON. here's the sample result,
{
"total_result" : 25,
"questions" : [
{
"id" : 1,
"text" : "the question of user 1 here",
"user" : {
"id" : 5,
"name" : "user 5",
},
"answers" : [
{
"id" : 5,
"text" : "first answer to user 1 question",
"user" : {
"id" : 10,
"name" : "user 10",
}
},
{
"id" : 6,
"text" : "second answer to user 1 question",
"user" : {
"id" : 11,
"name" : "user 11",
}
},
{
"id" : 10,
"text" : "third answer to user 1 question",
"user" : {
"id" : 12,
"name" : "user 12",
}
}
]
},
{
"id" : 2,
"text" : "the question by user 2 here",
"user" : {
"id" : 6,
"name" : "user 6",
},
"answers" : [
{
"id" : 5,
"text" : "first answer to user 2 question",
"user" : {
"id" : 30,
"name" : "user 30",
}
},
{
"id" : 6,
"text" : "second answer to user 2 question",
"user" : {
"id" : 20,
"name" : "user 20",
}
},
{
"id" : 10,
"text" : "third answer to user 2 question",
"user" : {
"id" : 1,
"name" : "user 1",
}
}
]
},
]
}
my struct goes here,
type Question struct {
Id int
Text string ...
User User ...
Answer []Answer ...
}
type User struct {
Id int ...
Name string ...
}
type Answer struct {
Id int ...
Text string ...
User User ...
}
here's the query to get questions and user.Detail
query := "select text, user_id from questions limit 10 offset 10"
rows, err, := db.QueryCtx(ctx, query)
//handel error
var questions []Question
var userIds []string
for rows.Next() {
var q Question
var userId string
//scan to `question` and `userId`
questions = append(questions, q)
userIds = append(questions, userId)
}
here's the query to get the user to the question
query = "select name from users where id = any($1)"
userRows, err := db.QueryCtx(ctx, query, pq.Array(userIds))
//handle error
var users []User
//scan and store to users
here's the query to get answers to the question
query = "select answers.id, answers.text, u.id, u.name from answers join questions as q on q.id=answers.question_id join users as u on u.id=answers.user_id where answers.questions_id=$1"
for i := 0; i < len(questions); i++{
rowAnswer, err := db.QueryCtx(ctx, query, questions[i].Id)
//handle error
var answers []Answer
for rowAnswer.Next(){
var answer Answer
//scan to answer
append = (answers, answer)
}
questions[i].User.Id = users[i].Id
questions[i].User.Name = users[i].Name
questions[i].Answer = answers
}
users table
id
name
1
name
questions table
id
text
user_id
1
text
1
answers table
id
text
question_id
user_id
1
text
1
1
the result is good, nothing wrong with the code and the result. but, I'm thinking about the n+query case, because I do looping to get the answers. my question is, is it reasonable to do that, or is any good advice for my code?
In get questions, you need var questionIds and mapQuestionIdx
add select id
questionIds is for get question id, so you can query where in
mapQuestionIdx is for save question id and index in slice. Note question_id is key and index is value.
so it look like this
query := "select id, text, user_id from questions limit 10 offset 10"
rows, err, := db.QueryCtx(ctx, query)
//handel error
var questions []Question
var userIds []string
questionIds := make([]int, 0, 10) // because limit is 10
mapQuestionIdx := make(map[int]int)
idx := 0
for rows.Next() {
var q Question
var userId string
//scan to `question` and `userId`
questions = append(questions, q)
userIds = append(questions, userId)
questionIds = append(questionIds, q.ID)
mapQuestionIdx[q.ID] = idx
idx++
}
On query to get answers to the question
add select question_id
// add select question_id
query = "select q.id question_id, answers.id, answers.text, u.id, u.name from answers join questions as q on q.id=answers.question_id join users as u on u.id=answers.user_id where answers.questions_id in ($1)"
rowAnswer, err := db.QueryCtx(ctx, query, questionIds) // questionIds from above
//handle error
for rowAnswer.Next(){
var answer Answer
var question_id int
//scan to answer and question_id
i := mapQuestionIdx[question_id]
questions[i].User.Id = users[i].Id
questions[i].User.Name = users[i].Name
if questions[i].Answer == nil {
questions[i].Answer = make([]answer, 0)
}
questions[i].Answer = append(questions[i].Answer, answer)
}
You can make use of a combination of struct tag and encoding/json to un-marshal your result properly.
Example for your use case:
Redeclare your type as
type Question struct {
Id int `json:"id"`
Text string `json:"text"`
User User `json:"user"`
Answer []Answer `json:"answer"`
// Fill the rest yourself
...
}
To unmarshal simply:
someDummyQuestionsResult := []byte{
{
// some example
}
}
var questions []Question
err := json.Unmarshal(someDummyQuestionsResult , &questions)
fmt.Println(questions)

Delete value from nested json - postgres

I have the json block modeled below. I want to selectively delete individual blocks from my_items based on the id which is AAA and BBB in my sample. ie if I tried to delete the AAA block under my_items I would want tojust delete the {"id" : "AAA"} but if wanted to delete the BBB block it would delete the larger {"name" : "TestRZ", "id" : "BBB", "description" : ""} block.
I know I can use the #- to remove whole blocks like SELECT '{sample_json}'::jsonb #- '{my_items}' would purge out the whole my_items block. But I dont know how to use this to conditionally delete children under a parent block of json. I have also used code similar to this example to append data inside a nested structure by reading in the node of the nested structure cat-ing new data to it and rewriting it. UPDATE data SET value= jsonb_set(value, '{my_items}', value->'items' || (:'json_to_adds'), true) where id='testnofeed'.
But I dont know how to apply either of these methods to: 1)Delete data in nested structure using #- or 2)Do the same using `jsonb_set. Anyone have any guidance for how to do this using either of these(or another method).
{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
},
],
"name" : "TheName"
}
Data is stored in value jsonb. when I update I will be able to pass in a unique kind so that it only updates this json in one row in db.
-- Table Definition
CREATE TABLE "public"."data" (
"id" varchar(100) NOT NULL,
"kind" varchar(100) NOT NULL,
"revision" int4 NOT NULL,
"value" jsonb
);
This works in PostgreSQL 12 and later with jsonpath support. If you do not have jsonpath, then please leave a comment.
with data as (
select '{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
}
],
"name" : "TheName"
}'::jsonb as stuff
)
select jsonb_set(stuff, '{my_items}',
jsonb_path_query_array(stuff->'my_items', '$ ? (#."id" <> "AAA")'))
from data;
jsonb_set
---------------------------------------------------------------------------------------------------------------------------------------------------
{"name": "TheName", "urlName": "testurl", "my_items": [{"id": "BBB", "name": "TestRZ", "description": ""}], "countryside": "", "description": ""}
(1 row)
To update the table directly, the statement would be:
update data
set value = jsonb_set(value, '{my_items}',
jsonb_path_query_array(value->'my_items',
'$ ? (#."id" <> "AAA")'));
This works for versions before PostgreSQL 12:
with data as (
select 1 as id, '{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
}
],
"name" : "TheName"
}'::jsonb as stuff
), expand as (
select d.id, d.stuff, e.item, e.rn
from data d
cross join lateral jsonb_array_elements(stuff->'my_items') with ordinality as e(item, rn)
)
select id, jsonb_set(stuff, '{my_items}', jsonb_agg(item order by rn)) as new_stuff
from expand
where item->>'id' != 'AAA'
group by id, stuff;
id | new_stuff
----+---------------------------------------------------------------------------------------------------------------------------------------------------
1 | {"name": "TheName", "urlName": "testurl", "my_items": [{"id": "BBB", "name": "TestRZ", "description": ""}], "countryside": "", "description": ""}
(1 row)
The direct update for this is a little more involved:
with expand as (
select d.id, d.value, e.item, e.rn
from data d
cross join lateral jsonb_array_elements(value->'my_items')
with ordinality as e(item, rn)
), agg as (
select id, jsonb_set(value, '{my_items}', jsonb_agg(item order by rn)) as new_value
from expand
where item->>'id' != 'AAA'
group by id, value
)
update data
set value = agg.new_value
from agg
where agg.id = data.id;

Overlapping in NodeJS/mysql2

When i joining 2 tables i have to get result as nested array how can i do it in nodejs with mysql2
if i am executing
SELECT users.id as user_id , users.name as user_name , comments.id as comment_id , comments.comment as comment FROM users LEFT JOIN comments ON comments.user_id = users.id WHERE users.id = 1
I'm getting result as
[{
"user_id" : 1,
"user_name" : "Naaban"
"comment_id" : "2",
"comment" : "Hello"
},
{
"user_id" : 1,
"user_name" : "Naaban"
"comment_id" : "3",
"comment" : "Bye"
}]
My Expectation is
{
"user_id" : 1,
"user_name" : "ramesh",
"comments" : [
{
"comment_id" : 2,
"comment" : "Hello"
},
{
"comment_id" : 3,
"comment" : "Bye"
}]
}
Well you can do that directly from the query however you can normalize your code as follows:
let data = [{
user_id : 1,
user_name : "Naaban",
comment_id : "2",
comment : "Hello"
},
{
user_id : 1,
user_name : "Naaban",
comment_id : "3",
comment : "Bye"
}];
let normalizedData = [];
for(let i = 0; i < data.length; i++){
const comment = {comment_id: data[i].comment_id, comment: data[i].comment};
const { user_id, user_name} = data[i];
normalizedData.push({ user_id, user_name, comment });
}
console.log(normalizedData);

Separate single record into two rows by column names

I want to separate a single record into 2 records by their column names.
Consider only a single record for now.
Currently what I get using simple select query:
{ "id" : "1", "route_name" : "6", "start_up" : "Mumbai", "destination_up" : "Delhi", "start_down" : "Delhi", "destination_down" : "Mumbai" }
What I actually need:
{ "id" : "1", "route_name" : "6", "start_up" : "Mumbai", "destination_up" : "Delhi" }, { "id" : "1", "route_name" : "6", "start_down" : "Delhi", "destination_down" : "Mumbai" }
How can I achieve this using a single query?
you can use an union
select id, route_name, start_up, destination_up
from my_table
where id ='1'
union
select id, route_name, start_down, destination_down
from my_table
where id ='1'

Postgres nested JSON array using row_to_json

I am trying to create nested json array using 2 tables.
I have 2 tables journal and journaldetail.
Schema is -
journal : journalid, totalamount
journaldetail : journaldetailid, journalidfk, account, amount
Relation between journal and journaldetail is one-to-many.
I want the output in following format :
{ journalid : 1,
totalamount : 1000,
journaldetails : [
{
journaldetailid : j1,
account : "abc",
amount : 500
},
{
journaldetailid : j2,
account : "def",
amount : 500
}
]}
However, by writing this query as per this post the query is:
select j.*, row_to_json(jd) as journal from journal j
inner join (
select * from journaldetail
) jd on jd.sjournalidfk = j.sjournalid
and the output is like this :
{ journalid : 1,
totalamount : 1000,
journaldetails :
{
journaldetailid : j1,
account : "abc",
amount : 500
}
}
{ journalid : 1,
totalamount : 1000,
journaldetails :
{
journaldetailid : j2,
account : "def",
amount : 500
}
}
I want the child table data as nested array in the parent.
I found the answer from here:
Here is the query :
select row_to_json(t)
from (
select sjournalid,
(
select array_to_json(array_agg(row_to_json(jd)))
from (
select sjournaldetailid, saccountidfk
from btjournaldetail
where j.sjournalid = sjournalidfk
) jd
) as journaldetail
from btjournal j
) as t
This gives output in array format.