In this post I’ll be writing about the database model and also show some examples of the types of operations we’ll be doing on our JSON data. This post is the second part of a series, so you will probably want to read the previous post first.
The database schema
Here’s an overview of how our database schema looks like:
The schema has been simplified in order to make for simpler examples. In our application we have users, which in turn have 0 or more clients. Each client will have 0 or more forms that the user will be filling out for them. Here’s a script you can use to generate the tables.
For our purposes, the interesting part is the data
column of the forms
table.
That column will store the entire form. The form JSON document looks like this:
This is of course a fake schema that will probably make no sense as a form or application, but the basic functionality I want to go over is there. Keep in mind, though, that as of Postgres 9.3 the JSON datatype is missing many operators, so you will need to cast your values from text.
The fun stuff
Before moving forward, I’d advise you see the documentation about JSON operators, specifically Table 9-39. It’ll help out
making sense of what the ->
and ->>
operators do and the subtle difference between them. With that in mind, let’s dive into
some cool things we can do with JSON fields:
Lets go over the query. First, we use data->>'Printed'
to retrieve the value of Printed
as text. Since the JSON data type is missing many operators, we can’t just simply do data->'Printed' = true
as it will fail to cast that JSON object to bool. To work around this, we use the ::bool
operator to cast the value of the property as text to a boolean . In Postgres we
do casts by appending ::<type>
to a value. In our case we want to cast the text
value returned by data->>'Printed'
to a bool
. Note that we wrap the entire data->>'Printed'
part in parentheses to avoid Postgres from mistakingly casting the text 'Printed'
to bool
. You will see this pattern repeat itself in all of the following examples.
Other things we can do:
Here we cast our FiledDate
to a timestamp using the pattern in which our dates are stored. This is quite
handy, as different JSON libraries serialize dates differently. For more information on to_timestamp
check the official docs.
Note that in this example we use ->
to get the Company
property and then we retrieve the text value of it’s
FullName
property by using ->>
. The ->
operator returns the JSON object (Company
), which then allows us
to use the ->>
operator to retrieve the text value of its FullName
property. Also note that we’re using the
nullif
function to make sure we treat empty strings
as nulls, making our comparison far easier to write and read. For more information on nullif
check the official docs.
We can also do standard text comparisons. Note that =
, LIKE
, and other text comparisons are case sensitive in Postgres. For a
case-insensitive comparison either use tolower
on both values or use ILIKE
. For example:
We can also do numerical comparisons, as long as we provide the correct casts. In the following example we have to cast
to int
in order to filter (note the parentheses wrapping the expression):
Maybe you want to generate a chart with daily filing activity. Personally, when I realized I could do this, it blew my mind. Yes, we’re grouping by a value inside a JSON object:
I’m using to_date
in this example because our fictional chart only needs the date part without time information. For more
information on to_date
check the official docs.
Maybe you need to join
tables against JSON data. Postgres’ got you covered as well. This also blew my mind the first
time I did it:
Limitations
One thing that you can’t do yet using regular queries is updating values within a
JSON document. In our case, we don’t need to do this because our application will
generate a new JSON document each time, so we update the entire document in one go.
For example, if we wanted to update the Filed
property, we can’t do:
Instead we have to provide the entire JSON document to the query and cast it to JSON this way:
Inserting data is done in the same fasion.
Wrapping up
Hopefully this gives you an idea of how we can use regular ol’ sql queries to retrieve JSON data. There’s some functionality missing from the JSON data type, no doubt. Don’t let this discourage you, though. The JSON data type is evolving and there are changes planned for the 9.4 release to add operators and other great stuff to it. For those of you concerned about performance, you can use expression indexes to index your JSON fields. If I can find the time, I’ll add a post at the end of the series with some numbers on this (I make no promises, though).
In the next part of this series I’ll show you how I implemented the data access layer on a C# application. Stay tuned.
Comments
comments powered by Disqus