Looking for a right EAV structure based on jsonb

别来无恙 提交于 2019-12-04 12:31:46

问题


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.


回答1:


Objective: You want to store attribute related to a given entity.

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.

If you can pre-define your attributes names AND values:

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 false
  • Add an index on ParentAttributeID
  • Add a trigger to prevent AttributeID from changing
  • Add a rule on delete do instead set Deleted=True

Then in any table you want to attribute, add this field:

  • AttributeSet" int[] not null default
  • Add a GIN index on that array field
  • Also enable the 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.

When you don't know the attribute values but it's a small set:

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.

When you don't know the attribute values and the values are diverse

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
  • Add a GIN index to that field

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 youe Attribute table you should have a field that is IsMulti bool not null which will allow you to know how to query for it.



来源:https://stackoverflow.com/questions/49599011/looking-for-a-right-eav-structure-based-on-jsonb

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!