I have table 'elements' that is related with table 'element_parameters'. element_parameters table have key/value structure. Keys and values are dynamicaly, they can be created be user or by some software, so I can't predefine them in 'elements' table.
For example I have elements 'flower' and 'car':
elements
--------
flower
car
element_parameters
------------------
element | key | value
flower | smell | soft
car | manufacturer | fiat
flower | color | red
car | wheels | 4
As you can see, there could be any keys and any values.
But often I need to find elements by their parameters. This is the problem. One element could have 10000+ parameters. So if I have 100 elements, I have to search through 1000000 parameters each time i want to find some element. And i can't do this in one query, be cause first I have to find all parameters thats fits my condition, group them by parameter ID and then search elements table for those ID's.
Is there some search engines, where I could merge and index elements with their parameters to 'one row'? Elasticsearch would be good decision for this situation? Maybe there is other ways to solve it?
Thank you.
I think you should modify your db like this:
elements
-----------
id | name
1 | flower
2 | car
element_parameters
-------------------------------------------------
element_id | parameters
1 | {"color":"red","smell":"soft"}
2 | {"manufacturer": "fiat", "wheels":3}
You can find easily any element's parameter:
$id = 1;
$element = Element::find($id)->load('ElementParameters')->get();
$paremeters = json_decode($element->ElementParameters);
Related
I've been reading similar questions, but I think my case is a bit more complicated.
I have a form that register items. These items may have options with sub-options (checkboxes and radio buttons):
The number of checkboxes and radio buttons may decrease/increase but the real pain to design a good structure is for the checkboxes, as these must have (at least I think so) a fixed name column for each one.
The case for radio buttons is easier as I just assign an id to each one (and save the names in a different table).
My current DB structure is simple (between parenthesis is the table/column name):
The items table (item) have columns of type integer (to save the id of the radio buttons).
Another table for the checkboxes (item_option), with columns of type integer (1 if checked, 0 if unchecked). And 1 PK column (item_id) that points to the PK column (id) of the items table.
And tables (again item_option) for the names of the radio buttons with a PK column (id) that points to the option column (is this understandable? Sorry for my bad english).
I think a different table containing the sub-options is better than put all the columns in the main table, right?
So, the radio buttons are stored in the main table (1 column per option) and the checkboxes in a separeted table (1 table per option):
Items table:
+-----+----------+----------+
| id | Option_1 | Option_2 |
+-----+----------+----------+
| 123 | 3 | 1 |
+-----+----------+----------+
| 456 | 2 | 3 |
+-----+----------+----------+
| 789 | 1 | 2 |
+-----+----------+----------+
item_option_3 table (this would be needed to know which ones are checked):
+--------------+--------------+--------------+---------+
| Sub_Option_1 | Sub_Option_2 | Sub_Option_3 | item_id |
+--------------+--------------+--------------+---------+
| 1 | 0 | 1 | 123 |
+--------------+--------------+--------------+---------+
| 1 | 1 | 0 | 456 |
+--------------+--------------+--------------+---------+
| 0 | 1 | 1 | 789 |
+--------------+--------------+--------------+---------+
item_option_1-2 table (this would be used to print the names):
+-----------+--------------+--------------+
| option_id | name | name_es |
+-----------+--------------+--------------+
| 1 | Sub_Option_1 | Sub_Opción_1 |
+-----------+--------------+--------------+
| 2 | Sub_Option_2 | Sub_Opción_2 |
+-----------+--------------+--------------+
| 3 | Sub_Option_3 | Sub_Opción_3 |
+-----------+--------------+--------------+
What kind of structure do I need to spawn these sub options (checkboxes) dynamically?
What about something like this?
Your model has option keys as columns and values as rows. Why have both keys and values be rows? If you don't need complex type-based validation, it should suffice to have a single options table with a one to optionally many relationship to itself to account for suboptions. To enumerate all options and values, just retrieve all rows from the table. If ParentOptionId is null, then it is a base-level option; otherwise it is a suboption.
UML & ER version below.
EDIT: After reading through your question and comments again, I've come up with a more complicated but more robust design for you to consider:
It works like this:
Every user input is an Option. Every option consists of a display text (OptionText), tooltip/subtext/etc (Description), a default and then user supplied value (Value), a value type (ValueType boolean,text, date, etc). It also has a DisplayOrder so you know where to situate it in relation to other Options in its group. Options can also have a parent/child relationship with other Options. You can do the same for the other entities if you want but I did not model that.
Every Option is contained within an OptionGroup with 0 or more sibling Options. OptionGroups are just a collection of one or more related Options. The GroupType field dictates how your form builder needs to treat that group. The most obvious example would be for your radio button groups; each of those would be an OptionGroup and each radio button would be a boolean Option within the OptionGroup. An OptionGroup could just as easily handle a multiple selection checkbox group or just some related text inputs that need a common header text (like a street address).
For further dynamic design OptionGroups are contained within GroupSections, even if there is just one default GroupSection in a form.
Finally, a Form models your final actual UI form and consists of one or more GroupSections.
This should be flexible enough for you to tweak to your liking. What do you think?
Final note: if you are looking into dynamically building your forms in Javascript, check out a few frameworks like X-editable or formly. They take JSON or configuration objects and build out the entire form with validation/etc from there while giving you some hooks for event handling. Chances are you don't need to completely reinvent the wheel unless you want to keep your implementation as simple and specific as possible.
I have a EAV structure in DB and tables: attribute(id,parent_id,code,name) and value(id,entity_id,attribute_id,value).
I'm using parent_id to store attribute's list. For example,
id | parent_id | code | name
1 | null | color_id | Color
2 | 1 | null | Red
3 | 1 | null | Blue
4 | 1 | null | Other
Now I need the ability to store different data on select certain item. This may be one or more inputs. For example:
Color:
(o) Red
(o) Blue
(o) Other (please specify) ______________
How to store it? Create additional table or may be store with parent_id pointed to selected element or something else?
id | parent_id | code | name
4 | 1 | null | Other
5 | 4 | other_one | First text value
6 | 4 | other_two | Second text value
Thought a little bit about it... Here's a try to give you an answer to the question "How to store it":
Make sure, that you get partial structure into your concept. Define kind of types, value packages, things which belong together and use XML. Something like the above could loke like:
<settings>
<parent id="1">
<colors>
<color>Red</color> --here you should think of tags to set the meaning of the color...
<color>Red</color>
<color>Other</color> --here you set whatever color you want
</colors>
</parent>
<parent id="2">
-- Your example with the "First text value" gives me this idea:
<colors>
<color meaning="BackColor">Red</color>
<color meaning="BorderColor">Red</color>
<color meaning="First text value">Blue</color>
<color meaning="Second text value">Green</color>
</colors>
</parent>
</settings>
So my suggestion:
Try to define certain types like "FormColor" and let it be like
<FormColor backcolor="Red" forecolor="blue" first_text="green">
--You may add specialities within the FormColor-element
<SpecialColor meaning="SomeSpecialMeaning">Blue</SpecialColor>
</FormColor>
Place these "types" within a parent element. You can easily pick the parent element via id and read its inner XML. There you find everything you need...
With this approach you are as flexible as with your EAV thing, but much better readable, maintainable and better to evaluate.
I have two related tables, results and userID.
results looks like this:
+----+--------+--------+
| ID | userID | result |
+----+--------+--------+
| 1 | abc | 124 |
| 2 | abc | 792 |
| 3 | def | 534 |
+----+--------+--------+
userID looks like this:
+----+--------+---------+
| id | userID | name |
+----+--------+---------+
| 1 | abc | Angela |
| 2 | def | Gerard |
| 3 | zxy | Enrico |
+----+--------+---------+
In results, the userID field is a lookup field; it stores userID.id but the combo box has userID.userID as its choices.
When I try to enter data into results by setting the userID combo box and entering a value for result, I get this error message:
You cannot add or change a record because a related record
is required in table `userID`.
This is strange, because I'm specifically selecting a value that's provided in the userID combo box.
Oddly, there are about 100 rows of data already in results with the same value for userID.
I thought this might be a database corruption issue, so i created a blank database and imported all the tables into it. But I still got the same error. What's going on here?
Both tables include a text field named LanID. You are using that field in this relationship, which enforces referential integrity:
The problem you're facing is due to the Lookup field properties. This is the Row Source:
SELECT [LanID].ID, [LanID].LanID FROM LanID ORDER BY [LanID];
But the value which gets stored (the Bound Column property) is the first column from that SELECT statement, which is the Long Integer [LanID].ID. So that number will not satisfy the relationship, which requires results.LanID = [LanID].LanID.
You must change the relationship or change the Lookup properties so both reference the same field value.
But if it were me, I would just eliminate the Lookup on the grounds that simple operations (such as this) become unnecessarily confusing when Lookup fields are involved. Make results.LanID a plain numeric or text field. If you want some kind of user-friendly drop-down for data entry, build a form with a combo or list box.
For additional arguments against Lookup fields, see The Evils of Lookup Fields in Tables.
If you are using a parameter query, make sure you have them in the same order as the table you are modifying and the query you have created. You might have one parameter inserting the conflicting data. Parameters are used in the order they are created...not the name of the parameter. I had the same problem and all I had to do was switch the order they were in so they matched the query. This is an old thread, so I hope this helps someone who is just now having this problem.
I have put a lot of effort into my database design, but I think I am
now realizing I made a major mistake.
Background: (Skip to 'Problem' if you don't need background.)
The DB supports a custom CMS layer for a website template. Users of the
template are limited to turning pages on and off, but not creating
their own 'new' pages. Further, many elements are non editable.
Therefore, if a page has a piece of text I want them to be able to edit,
I would have 'manually' assigned a static ID to it:
<h2><%= CMS.getDataItemByID(123456) %></h2>
Note: The scripting language is not relevant to this question, but the design forces
each table to have unique column names. Hence the convention of 'TableNameSingular_id'
for the primary key etc.
The scripting language would do a lookup on these tables to find the string.
mysql> SELECT * FROM CMSData WHERE CMSData_data_id = 123456;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| 1 | 123456 | 1 |
+------------+-----------------+-----------------------------+
mysql> SELECT * FROM CMSDataTypes WHERE CMSDataType_type_id = 1;
+----------------+---------------------+-----------------------+------------------------+
| CMSDataType_id | CMSDataType_type_id | CMSDataType_type_name | CMSDataType_table_name |
+----------------+---------------------+-----------------------+------------------------+
| 1 | 1 | String | CMSStrings |
+----------------+---------------------+-----------------------+------------------------+
mysql> SELECT * FROM CMSStrings WHERE CMSString_CMSData_data_id=123456;
+--------------+---------------------------+----------------------------------+
| CMSString_id | CMSString_CMSData_data_id | CMSString_string |
+--------------+--------------------------------------------------------------+
| 1 | 123456 | The answer to the universe is 42.|
+--------------+---------------------------+----------------------------------+
The rendered text would then be:
<h2>The answer to the universe is 42.</h2>
This works great for 'static' elements, such as the example above. I used the exact same
method for other data types such as file specifications, EMail Addresses, Dates, etc.
However, it fails for when I want to allow the User to dynamically generate content.
For example, there is an 'Events' page and they will be dynamically created by the
User by clicking 'Add Event' or 'Delete Event'.
An Event table will use keys to reference other tables with the following data items:
Data Item: Table:
--------------------------------------------------
Date CMSDates
Title CMSStrings (As show above)
Description CMSTexts (MySQL TEXT data type.)
--------------------------------------------------
Problem:
That means, each time an Event is created, I need to create the
following rows in the CMSData table;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| x | y | 6 | (Event)
| x+1 | y+1 | 5 | (Date)
| x+2 | y+2 | 1 | (Title)
| x+3 | y+3 | 3 | (Description)
+------------+-----------------+-----------------------------+
But, there is the problem. In MySQL, you can have only 1 AUTO INCREMENT field.
If I query for the highest value of CMSData_data_id and just add 1 to it, there
is a chance there is a race condition, and someone else grabs it first.
How is this issue typically resolved - or avoided in the first place?
Thanks,
Eric
The id should be meaningless, except to be unique. Your design should work no matter if the block of 4 ids is contiguous or not.
Redesign your implementation to add the parts separately, not as a block of 4. Doing so should simplify things overall, and improve your scalability.
What about locking the table before writing into it? This way, when you are inserting a row in the CMSData table, you can get the last id.
Other suggestion would be to not have an incremented id, but a unique generated one, like a guid or so.
Lock Tables
I'm developing a website where users will be able to save in their profile a list (array) of items (which they can find also in the same website and have their own id).
This is the way I've used so far in similar situations:
- Create a 'text' (string) field in the users table in the DB and save the items separated by commas
- To read that field, use the explode method to get the array in CakePHP and then work with it
- To save the array to the DB, use the implode method to convert it to a string and be able to store it in the field
What I dont' like about this method is that it can get really complicated to deal with those lists (add items, remove items...) and you can't really access those items directly, there's always some pre-processing or post-processing to make.
For example, to make it easier to look at a specifc item in one user's list and find other users with the same item in their lists.
Is there any better way to deal with arrays in CakePHP+MySQL? I've read about serialize()/unserialize(), but I don't think that would make a big difference compared to the other method...
Thank you very much in advance for any ideas!
Definately use a table so as with Kristian Antonsen's suggestion, create an items table then create a hasManu relationship in you User model like this:
class User extends AppModel{
$hasMany = array(
'Item'
);
}
Or A HABTM relationship:
class User extends AppModel{
$hasAndBelongsToMany = array(
'Item'
);
}
Use the form helper to create inputs for items.
Hope this helps.
A database table is just a huge spreadsheet. But instead of storing one piece of information in each cell, you clump them all together in one cell. You would never do that in spreadsheet.
You should create an item table like this:
+----+--------+--------+
| ID | UserID | ItemID |
+----+--------+--------+
| 1 | 2 | 3 |
| 2 | 2 | 1 |
| 3 | 3 | 3 |
+----+--------+--------+
And possibly another table:
+----+-------------+
| ID | ItemName |
+----+-------------+
| 1 | Mathematics |
| 2 | Jogging |
| 3 | Movies |
+----+-------------+
That'll let you know that User 2 likes Movies and Mathematics, and User 3 likes Movies.