Introduction One of the major requirements for any online business is to have a backend that either provides or can be extended to provide an API response. Building websites with static HTML and simple jquery ajax is coming to an end. In this era, Javascript frameworks rules the market. Hence, it is a good decision for the database to support JSON, as JSON is becoming the glue that connects the frontend and backend. Rails have an inbuilt support for generating JSON, as it’s our swiss army knife of web development, and encourages the REST URL structure . And its a good choice for building API. It is good enough to a particular point of growth. Very soon you will reach bottlenecks, where you have more requests than you can handle and you have to either spawn up more servers or use some concurrent languages like elixir, go, etc. Before we go to that scale and burn down the existing codebase, we can use database to generate JSON responses for us, which is 10 times faster in generating JSON than Rails (though more verbose). Since PostgreSQL 9.2, the database has taken a major leap in supporting JSON. The support that PostgreSQL provides can be divided into two
- Storing data in JSON and JSONB format
- Generating JSON results from the query itself
Getting Started
One of the advantages of using a database to generate JSON is that I have found it fast while generating smaller JSON but much more faster in generating complex JSON. (Note: The speed is in comparison with rails not with respect to the database itself)How to generate JSON
select row_to_json(users) from users where id = 1;Result:
{"id":1,"email":"[email protected]","encrypted_password":"iwillbecrazytodisplaythat", "reset_password_token":null,"reset_password_sent_at":null, "remember_created_at":"2016-11-06T08:39:47.983222", "sign_in_count":11,"current_sign_in_at":"2016-11-18T11:47:01.946542", "last_sign_in_at":"2016-11-16T20:46:31.110257", "current_sign_in_ip":"::1","last_sign_in_ip":"::1", "created_at":"2016-11-06T08:38:46.193417", "updated_at":"2016-11-18T11:47:01.956152", "first_name":"Super","last_name":"Admin","role":3}if you want to send only some specific fields
select row_to_json(results) from ( select id, email from users ) as resultsResult
{"id":1,"email":"[email protected]"}Now let’s see how to generate more complex JSON with sub JSON, and arrays.
select row_to_json(result) from ( select id, email, ( select array_to_json(array_agg(row_to_json(user_projects))) from ( select id, name from projects where user_id=users.id order by created_at asc ) user_projects ) as projects from users where id = 1 ) resultThis would return the JSON response
{"id":1,"email":"[email protected]", "project":["id": 3, "name": "CSnipp"]}The issue with the above code is that it is more verbose (has more text) when compared to a ruby code. We need to make sure that while we do a bit of sacrifice there, is worthwhile. So while working with API’s use it only where you see a delay in JSON generation. Similarly ,to the ‘array_agg’ method that we used above to aggregate values to an array then to JSON, we aggregate them as JSON using
json_agg
.
array_to_json(array_agg(row_to_json(user_projects)))can be shortened to
json_agg(user_projects)Since the above method of array generation can be tedious, in PostgreSQL 9.4, they have introduced a new method called
json_build_object
. Simple usage of the function can be as below
json_build_object('foo',1,'bar',2)which will output
{"foo": 1, "bar": 2}Also, we can use it to build complex JSON tree by creating functions within the PostgreSQL database. Of course, as we do that, we are moving more and more logic of the code into the DB and we would need to run migrations every time when we want to update a function. So as I said before, we are sacrificing our convenience here .So we should only use this, as the complexity of our JSON generation increases. I will be covering how to write PostgreSQL functions to help generate more complex JSON structure easier in the second part of this particle.
Interesting. Any idea of the performance increase?
I did this in production. I saw 4x improvement in request per seconds. Though it’s not a proper benchmark. As it was just a use case for us. I feel that as the JSON data gets more complicated PostgreSQL is a faster alternative.
As seen in 2013 (with performance benchmarks): https://hashrocket.com/blog/posts/faster-json-generation-with-postgresql
SELECT array_to_json(array_agg(Result))
FROM
(
column 1 ,column 2 ,
json_agg(
row_to_json(
(
SELECT t FROM (
column 3 ,
column 4
—————> here I need order by (column 4 )
)
)
)
) as detils
from table_name
)as result
The problem I ran into was memory consumption on the database server skyrocketed. It slowed down all the other queries. So make sure and profile speed and memory usage for your workload
Can you tell me the type of query that you ran and the number of records in the table. I would like to help you debug and reduce the memory usage.
It was several months ago and we already moved away from using it.
Any idea regarding it. We are using Postgres 9.5 with JSON generation in a table with presently half a million record and growing per day. The query runs in a small subset of the table after joining with other tables, but we have found no spike in memory compare to the spike we see while doing it in rails.
Also what did you do instead. Did you find another suitable/efficient solution for your problem.
I cant remember what version of postgres we were using. It might have been an older one. We just moved the json generation back to the app servers. It works there. The database was probably about the same order of magnitude number of records. I would just recommend monitoring the database memory usage. It is possible there was some bug that we ran into that has since been fixed or there was something pathological about our queries. I did not really investigate too deeply since we were just testing it out. It was easy for us to switch back and we had a lot of higher priority stuff we needed to do.
–>Here I need order by of CLAIM_COUNTER .
If i put inside json_agg it was not working.
please tell me any other alternative for this one.
SELECT array_to_json(array_agg(Result))
FROM (
SELECT
C.eCPClaim_carrier_id,
json_agg(
row_to_json(
(
SELECT t FROM
(
SELECT C.eCPClaim_Key AS CLAIM_KEY,
(
SELECT eCPClaim_Custom_Value1
FROM tbl_eCPClaim_Custom
WHERE eCPClaim_Key = C.eCPClaim_Key
AND eCPClaim_Custom_Field_Config_ID = ‘text3’
LIMIT 1
) AS CLAIM_COUNTER
ORDER BY CLAIM_COUNTER
) AS t
)
)
) AS available_codes
FROM tbl_ecpclaim C
GROUP BY C.eCPClaim_carrier_id
LIMIT 10
) AS result;
You randomly hit to a tutorial, find `hsps` in a code block, then you look again who is the author and you find it is written by your senior. Nice vibe!