More details:
CREATE TABLE superobject__object (
superobject_id integer NOT NULL,
path smallint NOT NULL,
set smallint NOT NULL,
object_id integer NOT NULL,
color_id integer NOT NULL,
CONSTRAINT superobject_object__pk PRIMARY KEY (superobject_id,path,object_id,color_id)
);
ALTER TABLE superobject__object ADD CONSTRAINT superobject_object__superobject_id_fk FOREIGN KEY (superobject_id) REFERENCES superobject (id);
ALTER TABLE superobject__object ADD CONSTRAINT superobject_object__object_id_fk FOREIGN KEY (object_id) REFERENCES object (id);
ALTER TABLE superobject__object ADD CONSTRAINT superobject_object__color_id_fk FOREIGN KEY (color_id) REFERENCES color (id);
It's combinatorial data. It's quite complicated to make it simple.
Basically, a superobject is made of objects which 1/ can have dependencies 2/ can be grouped together as set of options.
Think of a car you can buy green, yellow or red (set of options). If you buy the red paint, you can choose bonus hubcaps for a price.
Now, if you search for cars with green paint and hubcaps, you can't find that superobject because hubcaps have a dependency which is the red paint.
I solved this problem with the aforementioned table.
The dependencies (hierarchy) being not deep at all, I can afford to cut the superobjects into "paths".
For my car example, I have:
superobject_1 path_1 object_green_paint
superobject_1 path_1 object_yellow_paint
superobject_1 path_1 object_common_object
superobject_1 path_2 object_red_paint
superobject_1 path_2 object_hubcaps
superobject_1 path_2 object_common_object
As for the set of options, I give every object of a path a different integer and group the options together with the same integer:
superobject_1 path_1 set_1 object_green_paint
superobject_1 path_1 set_1 object_yellow_paint
superobject_1 path_1 set_2 object_common_object
superobject_1 path_2 set_1 object_red_paint
superobject_1 path_2 set_2 object_hubcaps
superobject_1 path_2 set_3 object_common_object
Why am I doing that instead of giving every combination its own path? Because the combinations grow exponentially for every set of options added.
Then, I search with:
SELECT s1.superobject_id as id, s1.set, s2.set, s3.set, s4.set, s5.set, s6.set, s7.set, s8.set FROM superobject__object s1
JOIN superobject__object s2 ON (s1.superobject_id, s1.path) = (s2.superobject_id, s2.path)
JOIN superobject__object s3 ON (s1.superobject_id, s1.path) = (s3.superobject_id, s3.path)
...
JOIN superobject__object s8 ON (s1.superobject_id, s1.path) = (s8.superobject_id, s8.path)
WHERE s1.object_id IN (SELECT id FROM object WHERE <filter_1>)
AND s2.object_id IN (SELECT id FROM object WHERE <filter_2>)
AND s3.object_id IN (SELECT id FROM object WHERE <filter_3>)
...
AND s8.object_id IN (SELECT id FROM object WHERE <filter_8>)
which gives me something like the first table.
I then proceed to filter out the rows where you have objects from the same set (of options) as you can't find cars with green AND yellow paint at the same time as one option exclude the others.