I have a model House
that has many boolean attributes, like has_fireplace
, has_basement
, has_garage
, and so on. House
has around 30 such boolean attributes. What is the best way to structure this model for efficient database storage and search?
I would like to eventually search for all Houses
that have a fireplace and a garage, for example.
The naive way, I suppose, would be to simply add 30 boolean attributes in the model that each corresponds to a column in the database, but I'm curious if there's a Rails best practice I'm unaware of.
Your 'naive' assumption is correct - the most efficient way from a query speed and productivity perspective is to add a column for each flag.
You could get fancy as some others have described, but unless you're solving some very specific performance problems, it's not worth the effort. You'd end with a system that's harder to maintain, less flexible and that takes longer to develop.
For that many booleans in a single model you might consider using a single integer and bitwise operations to represent, store and retrieve values. For example:
class Model < ActveRecord::Base
HAS_FIREPLACE = (1 << 0)
HAS_BASEMENT = (1 << 1)
HAS_GARAGE = (1 << 2)
...
end
Then some model attribute called flags
would be set like this:
flags |= HAS_FIREPLACE
flags |= (HAS_BASEMENT | HAS_GARAGE)
And tested like this:
flags & HAS_FIREPLACE
flags & (HAS_BASEMENT | HAS_GARAGE)
which you could abstract into methods. Should be pretty efficient in time and space as an implementation
I suggest the flag_shih_tzu gem. It helps you store many boolean attributes in one integer column. It gives you named scopes for each attribute and a way to chain them together as active record relations.
Here's another solution.
You could make a HouseAttributes
model and set up a two way has_and_belongs_to_many association
# house.rb
class House
has_and_belongs_to_many :house_attributes
end
# house_attribute.rb
class HouseAttribute
has_and_belongs_to_many :houses
end
Then each attribute for a house would be a database entry.
Don't forget to set up your join table on your database.
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