Rails - best database design for existing model - ruby-on-rails

So I have inherited some Rails 4 code and database models which I need to add to.
The model, called mpb_item has a table called mpb_items.
Inside this items table there are columns such as:
role1_start_date, role2_start_date, role3_start_date, role4_start_date
Not ideal but this is what it is. They should have been in a separate roles table I guess.
I need to add functionality to Suspend any one of these roles (or all of them).
I guess I can either:
To the existing table, I can add a new boolean column for each existing role column. e.g. role1_suspended, role2_suspended etc
Create table called mpb_suspensions, with 2 columns: mpb_item_id and role_name. Since the roles don't have ids themselves, the role_name column will store 'role1', or 'role2' etc depending on which role was suspended.
In my View, I need to have the ability to "suspend" each job, or all of them. I'm not sure how the model code would look to do this and which approach would be best.

If I had to build this, given the minimal info you provided I'd start by picking option 2 that you described. (a RoleSuspension is its own table) Benefits include:
fewer new columns total, and doesn't make any table indefinitely large
no redundant columns (role1, role2, etc.)
no need to add more columns if the # of possible roles changes (ie. it's more normalized)
no nil values (approach 1)
you can easily query for the overall state of RoleSuspensions (whereas with approach 1 you'd need to count the non-nil values in role1_suspended, role2_suspended, etc. then sum them up), and easier to index
you can attach logic to a RoleSuspension; it's a separate animal from its parent MbpItem. If it's just a bunch of boolean columns, any complex logic would need to be mushed into the MbpItem model, and would likely be much messier to maintain.
Following option 2's logic, suspending a role would involve creating a new record like this:
#mbp_item.role_suspensions.create!(role_number: 2)
and checking for suspension status would involve something like this:
if #mbp_item.role_suspensions.any?
# or....
RoleSuspension.where(role_number: 3).each do |s|
puts "Item #{s.mbp_item} is suspended for role #{s.role_number}."
end
Database performance will be a larger consideration with this approach, depending on the answers to the following questions:
Where and how often do you need to check for role suspensions?
When you check for role suspensions, how do you approach asking the question? In other words is some global task asking "What role suspensions exist, and for what mbp_items?" or do you check for suspensions on a given mbp_item when you render that object?
If you'll need to check for role suspensions very frequently, perhaps you should add a boolean column to mbp_items called has_suspensions, which will be a partial cache in that it will indicate whether any suspensions exist for this MbpItem (and must be maintained in after_create and after_destroy hooks).
On the other hand, if you know that suspension info never will need to have its own logic and never will need to be queried directly, you could add a single column to MbpItem, role_suspensions, containing a serialized array of integers for the suspended roles. This would be a much less invasive approach in terms of database structure, and probably much simpler even than your option 1 in that it would allow you to suspend and de-suspend any role number with less code and less metaprogramming, but if you ever need to add any fancy logic to the suspension or desuspension process (ie. if a RoleSuspension deserves status as its own object), you'd regret this approach.

Related

Rails model.create set id

I wonder if it's possible to to run Model.create() such that instead of taking next free id integer it takes the lowest free integer.
For example, assume we have records for id=10..20 and we don't have records for id=0..9, I want create instance of Model with id starting from 0 (in normal Mode.create() in would create instance staring from 21)
Preferably I want to do it in automatic manner. I don't want to change id by explicitly defining it.
DB
You'll be best doing this at database-level (look at altering the auto-increment number)
Although I think you can do this in Rails, I would highly recommend using the DB functionality to make it happen. You can do something like this in PHPMYAdmin (for MYSQL):
If you set the Auto-Increment to the number you wish to start at, every time you save data into the DB, it will just save with that number. I think using any Rails-based method will just overcomplicate things unnecessarily.
I'd discourage it.
Those ids serve solely as unique identifiers for rows in a table, and it's the database's job to assign one. You can verify that the model doesn't require an id to be saved:
m = Model.new
# populate m with data
m.name = "Name"
# look at what m contains
m
# and save it
m.save
# now inspect it again and see it got its unique id
m
While it might be possible to modify ids, it's not a good practice to give more sense to ids — when each new record gets a unique id at any time it's easier to debug possible DB structure errors that might occur during development. Like, say, some associated objects suddenly show up in a new user's account. Weird enough, right? That can happen and, worst case, can show up in production resulting in a severe security breach.
Keeping ids unique at all times eliminates this bug's effect. That seems much more important if the associated objects store confidential information and you care about keeping them safe. Encryption concerns aside.
So, to be sure in every situation, developers have adopted a practice of not giving id any other role other than uniquely identifying a row in a table. If you want it to do something else, consider making another field for that purpose.

How to create an Order Model with a type field that dictates other fields

I'm building a Ruby on Rails App for a business and will be utilizing an ActiveRecord database. My question really has to do with Database Architecture and really the best way I should organize all the different tables and models within my app. So the App I'm building is going to have a database of orders for an ECommerce Business that sells products through 2 different channels, a subscription service where they pick the products and sell it for a fixed monthly fee and a traditional ECommerce channel, where customers pay for their products directly. So essentially while all of these would be classified as the Order model, there are two types of Orders: Subscription Order and Regular Order.
So initially I thought I would classify all this activity in my Orders Table and include a field 'Type' that would indicate whether it is a subscription order or a regular order. My issue is that there are a bunch of fields that I would need that would be specific to each type. For instance, transaction_id, batch_id and sub_id are all fields that would only be present if that order type was a subscription, and conversely would be absent if the order type was regular.
My question is, would it be in my best interest to just create two separate tables, one for subscription orders and one for regular orders? Or is there a way that fields could only appear conditional on what the Type field is? I would hate to see so many Nil values, for instance, if the order type was a regular order.
Sorry this question isn't as technical as it is just pertaining to best practice and organization.
Thanks,
Sunny
What you've described is a pattern called Single Table Inheritance — aka, having one table store data for different types of objects with different behavior.
Generally, people will tell you not to do it, since it leads to a lot of empty fields in your database which will hurt performance long term. It also just looks gross.
You should probably instead store the data in separate tables. If you want to get fancy, you can try to implement Class Table Inheritance, in which there are actually separate but connected table for each of the child classes. This isn't supported natively by ActiveRecord. This gem and this gem might be able to help you, but I've never used either, so I can't give you a firm recommendation.
I would keep all of my orders in one table. You could create a second table for "subscription order information" that would only contain the columns transaction_id, batch_id and sub_id as well as a primary key to link it back to the main orders table. You would still want to include an order type column in the main database though to make it a little easier when debugging.
Assuming you're using Postgres, I might lean towards an Hstore for that.
Some reading:
http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination
https://github.com/devmynd/hstore_accessor
Make an integer column called order_type.
In the model do:
SUBSCRIPTION = 0
ONLINE = 1
...
It'll query better than strings and whenever you want to call one you do Order:SUBSCRIPTION.
Make two+ other tables with a foreign key equal to whatever the ID of the corresponding row in orders.
Now you can keep all shared data in the orders table, for easy querying, and all unique data in the other tables so you don't have bloated models.

Ruby on Rails Active Record Validation for "Draft" state best practices

I'm developing a form that I would like the user to have the option to return to. Ultimately all the fields need to be completed and I would like to incorporate the proper model level field validation before labeling the record as "complete". I can think of a few ways to do this:
Create two tables, one for records in "draft" state, with looser validation rules (ie fields don't necessarily need to be complete for the record to be saved), and a second table to store the records that have been submitted as "complete", obviously with the more stringent validation rules.
Create only one table to store the records, with a field that is labeled "isComplete", and based on this value determine which validation rules to apply.
I'm leaning towards option #2 because it would involve less working parts (in option #1 I would have to ensure that when I change a records state from "Draft" to "Complete" it gets deleted from one table and added to the other). The issue is I don't know how how do this elegantly in Rails.
Ultimately, as I'm sure this problem has been solved before, my question is:
What is the best practice in this situation?
Definitely option number two. Have an isComplete or draft option that is a boolean. Then in your models you can control what validations to run based on the state of the isComplete field. This can be done in a number of ways, for instance Rails has the concept of conditional validation which allows you to specify :if options on the validations so you can restrict what runs based on the complete state. You can also add a before_save or before_update hook to run methods based on if the record is a draft or not. With these two tools you should be able to have everything in a single table in an intuitive way.

Generic flags for a model in RoR

I am making a Ruby on Rails app and am realizing that my User class could potentially end up with a lot of generic boolean / integer attributes. For example, suppose I have a promotion each quarter, and I only want a person to be able to use the promotion once. Then I'd have to make a new column each quarter has_used_promotion_N to track that promotion.
Alternatively, I'm thinking of creating a new column called "Generic Flags" which is just a comma separated value of flags set on the account. For example:
"has_used_promotion_1, has_used_promotion_2, limit_on_feature_a=20" etc. could be set for some particular user
(or maybe I'll store it as JSON)
In any case, I'm thinking of giving myself some sort of NoSQL-like functionality in my DB.
Is this really bad design for some reason? Has anyone else done this before? Anything I'm completely missing about RoR?
In my opinion Promotion should be a separate model with a many to many relationship with User. When you have a promotion you would create a Promotion instance and when a person uses that promotion you add that person to promotion.users relationship.
This is much better than your idea because you can now query those relationship. Want a list of all users that used the first quarter promotion? No problem. You can do that with your solution, but you have to resort to some hackiness (is that a word?) to do it, and you'd have to parse the generic flag string for EVERY user on EVERY query. Not ideal to say the least.
If there's an arbitrarily-sized collection of associations then it should be a real relation, modeled using the existing DB and facilities. Promotions sounds like that, and it seems like it would be something you'd be modeling in your DB already; no real reason to keep a duplicate value hierarchy.
For actually-generic flags, you could have a named-flag table and again use a real association.
You could also just serialize a flag object to a text column. Doing so impedes your ability to do trivial searches on a flag/flag value, however. This may not matter for a wad of flags associated with a single user that you don't care about unless they're logged in, but tread lightly--it depends on your usecase.

How we design Dynamo db with keep relation of two entity

Hi iam new in dynamo db and, with my knowledge its a non relational db ie we cant join the tables. My doubt is how we design the table structure. Please clarify with following example.
I have a following tables
1) users - user_id, username, password, email, phone number, role
2) roles - id, name [ie admin, supervisor, ect..]
a) My first doubt is we have any provision to set auto increment for user_id fields ?
b) Is this correct way of setting primary key as user_id?
c) Is this is the correct method to store user role in dynamo db? ie a roles table contains id and title and store role id in user table?
e) Is this possible to retrieve two tables data along with each user? Am using rails 3 and aws-sdk gem
If anybody reply it will be very helpful for me like a new dynamodb user
Typically with nosql style databases you would provide the unique identifier, rather than having an auto increment PK field do that for you. This usually would mean that you would have a GUID be the key for each User record.
As far as the user roles, there are many ways to accomplish this and each has benefits and problems:
One simple way would be to add a "Role" attribute to the Users table and have one entry per role for that user. Then you could grab the User and you would have all the roles in one query. DynamoDB allows attributes to have multiple values, so one attribute can have one value per role.
If you need to be able to query users in a particular role (ie. "Give me all the Users who are Supervisors") then you will be doing a table scan in DynamoDB, which can be an expensive operation. But, if your number of users is reasonably small, and if the need to do this kind of lookup is infrequent, this still may be acceptable for your application.
If you really need to do this expensive type of lookup often, then you will need to create a new table something like "RolesWithUsers" having one record per Role, with the userIds of the users in the role record. For most applications I'd advise against doing something like this, because now you have two tables representing one fact: what role does a particular user have. So, delete or update needs to be done in two places each time. Not impossible to do, but it takes more vigilance and testing to be sure your application doesn't get wrong data. The other disadvantage of this approach is that you need two queries to get the information, which may be more expensive than the table scan, again, depending on the quantity of records.
Another option that makes sense for this specific use case would be to use SimpleDb. It has better querying capability (all attributes are indexed by default) and the single table with roles as multi-valued attribute is going to be a much better solution than DynamoDB in this case.
Hope this helps!
We have a similar situation and we simply use two DBs, a relational and a NoSQL (Dynamo). For a "User" object, everything that is tied to other things, such as roles, projects, skills, etc, that goes in relational, and everything about the user (attributes, etc) goes in Dynamo. If we need to add new attributes to the user, that is fine, since NoSQL doesn't care about those attributes. The rule of thumb is if we only need something on that object page (that is, we don't need to associate with other objects), then we put in Dynamo. Otherwise, it goes in relational.
Using a table scan on the NoSQL DB is not really an option after you cross even a small threshold (up to that point, you can just use an in memory DB anyway).

Resources