Unstructured Datatypes in Postgres – Hstore vs. JSON vs. JSONB
citusdata.com
citusdata.com
So far the only thing I've figured out is that updating pieces of a JSONB structure seems like a bad idea at the moment since it will not always succeed (notably you can't add entries to an array once it has 999 elements already).
In my problem I've given each analysis tool I run and API I query it's own JSONB column so that they only ever need to be written once, or if they are changed, they would be overwritten entirely and keep a few normal columns that contain some metadata, but I'm not sure if this is a good approach.
The thing you want to avoid in that type of system is having data in both locations that you need to update to keep in sync, this is a bad sign and you probably need to move some data out of the JSONB column.
Also, I would almost never choose anything but normal row data unless I absolutely had to and produce some json at the end of the pipeline. However, your situation has a special type of work you can save and it would be silly to redo it all over again on each request.
CREATE TABLE tbl(eventtime datetime);
INSERT INTO tbl {"eventtime":"2016-07-20 10:37:12","foo":"bar","whatever":"xx"};
INSERT INTO tbl {"eventtime":"2016-07-20 10:38:22","foo":"bar2","intfield":42};
SELECT foo, intfield FROM tbl;
--
{"foo":"bar","intfield":null}
{"foo":"bar2","intfield":42}
It won't complain about the missing 'columns'.Anyway, glad to see that Postgres is steadily making progress in this area.
Having the DB automatically insert null fields also makes it harder to use Javascript's `Object.assign` to populate undefined fields with default values:
Object.assign({a: "default-a", b: "default-b"}, {a: "db-a"})
{a: "db-a", b: "default-b"}
VS
Object.assign({a: "default-a", b: "default-b"}, {a: "db-a", b: null})
{a: "db-a", b: null}
null and undefined are two different concepts in JS. It is like the difference between "there is nothing here" and "I don't know if there is anything here".
{"foo":"bar"}
{"foo":"bar2","intfield":42}
Note that when you select the full record with the star it does not return null values: select * from tbl;
--
{"_id":1,"eventtime":"2016-07-20 10:37:12","foo":"bar","whatever":"xx"}
{"_id":2,"eventtime":"2016-07-20 10:38:22","foo":"bar2","intfield":42}
The undef/exists thing can get a bit confusing when you are mapping this into SQL.We've since updated to PostgreSQL 9.5. Does anyone know if we would see a performance advantage switching this to JSONB (either in selects or updates).
My sense is no, but I haven't been able to find any good answers one way or the other. If it's yes, then HStore doesn't really serve any purpose at all anymore.
I don't actually know anything from practical usage, but I think JSONB makes some kinds of membership tests more efficient (at the price of more expensive inserts). See 8.14.3. of https://www.postgresql.org/docs/9.4/static/datatype-json.htm...
The one "gotcha" with the column type right now is comparing hash values to certain data types can be a bit confusing at times, but simple string comparisons in arrays and hashes is relatively straight forward.
My favorite hstore feature is that you can produce a diff simply by using the minus operator. For auditing a table, in an ON UPDATE trigger you can simply do
previous_values = hstore(OLD) - hstore(NEW)
previous_values will only contain columns that have changed, with their old values.
You can then restore the row to its previous state by doing PREVIOUS_ROW = CURRENT_ROW #= previous_values.
The ability to diff might come at some point, but interest for it is probably low because of the purpose of JSONB. Diffing works best for similar structure, and if you're storing things with similar structure in postgres, I doubt you're going to use JSONB. Diffing deep trees is also very costly.
I have a use case where either HStore or JSONB might be neat (The most basic requirement is K/V, so HStore might be good enough. I could use nested structures as well though). Unfortunately some of the values I need to store are datetime values and I'd need to query based on those:
"Give me all documents where the datetime is from this year"
The article claims HStore is basically just strings (might work for bool as well, but not numbers/datetimes). JSON can express strings, numbers, booleans - but not datetime values.
Has anyone done something similar? Is that even possible?
Right now that application stores that in a rather messy way. Exposing this as a table field would mean that configuration changes (New field/a different field) causes a structural change (alter table). Or you have to go down the 'Date1, Date2, Date3, Number1, Number2' ... path.
In short: I see no way to expose these pieces of information in fields without ugly hacks, hence my interest in HStore/JSON(B) - but datetime based filters would be quite nice to have..
If the datetimes are ISO formatted strings, would regex matching not work for basic filtering?
where json_field.datetime similar to '2016-07-.*'
Of course, it'll probably be slow-tastic, and less convenient than native datetime manipulation, but hey, maybe it's enough to get unblocked?With either approach you'll have to do some mapping in and out of the system (though date probably serialises to string more or less out of the box for most json libs).
[1] http://stackoverflow.com/questions/10286204/the-right-json-d...
That said, you and IanCal have a point: I could just drop the idea of DateTimes and use timestamps/integers - converting queries in my application.
In itself it doesn't seem like a big deal, but you'll lose generic things that normally always work. An example would be a trigger that updates an updated_at column, by checking
IF OLD IS DISTINCT FROM NEW
This syntax normally works on everything, but will throw if a column is of type JSON.
You can try to be clever and encode the incoming data to reduce whitespace, but the business requirement is whatever user entered is what user expects to get back. Otherwise, your best bet is save the entire text.
After figuring out exactly what data I need to store, I decided to move back to Postgresql. I may still use Redis as I like the geo-location features that come right out of the box. Recompiling the extensions to Postgresql as not my favorite thing.