I wondering what will be the right approach to build EAV on jsonb.
I have Attribute
-> Values
tables as like in standard EAV.
CREATE TABLE attribute_values
(
id INTEGER,
attribute_id INTEGER,
value VARCHAR(255)
);
CREATE TABLE attributes
(
id INTEGER,
name VARCHAR(255)
);
Values will saved in attributes
filed of Entity
CREATE TABLE entity
(
id INTEGER,
title TEXT,
attributes JSONB
);
Tables Attribute
created to control duplicate attributes their types and better determine what it's a attribute is. For example to avoid: {weight: 100}
and {Weight: 100}
or {weigh: 100}
. Values
for work with unique values and contain avaliable list of values like color (green, red, white etc.) Values can be preloaded and using for faseted search.
I see several options:
1. Store format like
[{"attribute_id":1, "value":5},{"attribute_id":1, value:"text"}]
where value_id
will be custom value
like text or id
from Values
table. But I can't understand how to build indexing on this format, for example if Attribute 10
will integer
2. leave only Attribute
table (for controlling attribute name
) and store data like:
{"price": 105, "weight": 100, "color": "white"}
. This approach much better for indexing
CREATE INDEX entity_index ON entity (((attributes ->> 'price')::int));
but I will have problem with translation of text property and controlling of unique values. Also I can't add additional key like in option 1
: {"attribute_id":1, "value":5, "values": []}
What will be the best approach to store extra field with unique control (for unique attributes) and with the opportunity to indexing.
The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed.
JSONB stands for “JSON Binary” or “JSON better” depending on whom you ask. It is a decomposed binary format to store JSON. JSONB supports indexing the JSON data, and is very efficient at parsing and querying the JSON data. In most cases, when you work with JSON in PostgreSQL, you should be using JSONB.
The term "EAV database" refers to a database design where a significant proportion of the data is modeled as EAV. However, even in a database described as "EAV-based", some tables in the system are traditional relational tables.
I do not recommend a separate table for attribute values like we might have done in years gone by. Put a jsonb
field right on the appropriate table and call it Attributes
. Add a GIN
index to it so you can query the values quickly. Or use the other techniques described within.
Read this: https://dba.stackexchange.com/a/174421/7762
The biggest question here is if you intend to pre-define attribute values. If you do, there is an extremely efficient way to store them. If not, then I recommend a standard JSON object.
This gives you the most control, speed, and still provides flexibility.
Create a table Attribute
which has these fields:
AttributeID int4 unsigned not null primary key
ParentAttributeID int4 unsigned null
Name varchar(64) not null
Deleted
bool not null default falseParentAttributeID
AttributeID
from changingThen in any table you want to attribute, add this field:
AttributeSet" int[] not null default
intarray
extension from https://www.postgresql.org/docs/current/static/intarray.html
What has this accomplished?
You've create a tree of attributes. It might look like this:
ID Parent Name
----------------------------
100 NULL Color
101 100 Blue
102 100 Red
103 100 Green
110 NULL Size
111 110 Large
112 110 Medium
113 110 Small
Say you have a table called Items
and on it you've added AttributeSet
:
ItemID: 1234
Name: Tee Shirt
AttributeSet: [100, 103, 110, 112]
When translated, this means that it has the Color=Green
attribute, and the Size=Medium
attribute. 103
and 112
were enough to store that, but sometimes it's nice to be able to say "Show me all items that have any Size defined", that's why 110 was included.
You can make this lightning fast and ultra flexible.
SELECT
"ItemID", "Name"
FROM
"Items"
WHERE "AttributeMap" @> ARRAY[103,112]
Will return all items that have Size=Medium
and Color=Green
Or you can use the other operators on https://www.postgresql.org/docs/10/static/functions-array.html to come up with some awesome queries.
This gives you the most speed, control, and is even more flexible. You can flag new attributes for review if needed.
You can use the above technique and just dynamically add values to the Attribute
table if they don't exist.
This gives you the most flexibility, but at the expense of control.
In this case just add this to any table:
AttributeMap jsonb not null default '{}'::jsonb
Write code to validate the values against your Attribute
table. Have an indicator there if it is a single or multi-value...
Store like this in the AttributeMap
field:
{
"Color": "Green",
"Size": "Medium",
"Categories": ["Sports", "Leisure"]
}
Notice that Categories is a multi-attribute. In your Attribute
table you should have a field that is IsMulti bool not null
which will allow you to know how to query for it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With