2010-09-09 44 views
15

我有一个模型House有许多布尔属性,如has_fireplace,has_basementhas_garage,等等。 House有大约30个这样的布尔属性。构建此模型以实现高效的数据库存储和搜索的最佳方法是什么?在Rails中,在模型中存储多个布尔属性的最佳方式是什么?

例如,我想最终搜索所有有壁炉和车库的Houses。我想,简单的方法是在模型中简单地添加30个布尔属性,每个布尔属性对应于数据库中的一列,但我很好奇,如果有一个Rails最佳实践,我不知道。

回答

15

您的'天真'假设是正确的 - 从查询速度和生产率角度来看,最有效的方法是为每个标志添加一列。

你可能会像其他人所描述的那样幻想,但除非你解决了一些非常具体的性能问题,否则这是不值得的。你最终会遇到一个难以维护的系统,不够灵活,需要花费更长的时间来开发。

+2

同意。如果你愿意,可以将所有的布尔值都放在属于房屋的单独模型中,比如has_one:feature_set(或其他),并且该特性集包含所有布尔值。那么你可以使用虚拟属性来制作一些糖,比如House模型中的def has_fireplace? self.feature_set.fireplace;结束 – 2010-09-09 23:34:30

+0

谢谢约书亚和约翰。 John,在创建一个单独的:feature_set模型来存储所有布尔值方面有很大的胜利吗?你会强烈建议单独的模型设计吗?因为如果不是的话,我想我会跟着约书亚的“向众议院自己添加专栏”的方法。 – Sanjay 2010-09-10 00:14:07

+0

不,除了组织方面的原因外,没有真正的理由来分开东西,如果你发现你的“房子”模型变得太乱,那么只是一个选择。 – 2010-09-10 19:04:49

0

您可以随时拥有一个TEXT列,您可以持有JSON(例如,data),然后您的查询可以使用SQL的LIKE。

如:house.data#=> '{ “has_fireplace”:真实的, “has_basement”:假的, “has_garage”:真正}'

因此,做一个发现使用LIKE '%"has_fireplace":true%'将一个带壁炉任何回报。

在这种情况下,使用模型关系(例如,除了House之外的壁炉,地下室和车库模型)将会非常麻烦,因为您拥有如此多的模型。

+2

当然,缺点是LIKE以通配符开头的查询不能使用索引,所以如果您在该表中有大量的数据,您最终将得到每个查询的全表扫描。 – 2010-09-09 23:15:57

6

对于单个模型中的许多布尔值,您可以考虑使用单个整数和位运算来表示,存储和检索值。例如:

class Model < ActveRecord::Base 
    HAS_FIREPLACE = (1 << 0) 
    HAS_BASEMENT = (1 << 1) 
    HAS_GARAGE = (1 << 2) 

    ... 
end 

那么一些所谓的flags模型属性将设置这样的:

flags |= HAS_FIREPLACE 
flags |= (HAS_BASEMENT | HAS_GARAGE) 

而且测试是这样的:

flags & HAS_FIREPLACE 
flags & (HAS_BASEMENT | HAS_GARAGE) 

它你可以抽象成方法。作为实施应该在时间和空间上非常高效

3

如果您想查询这些属性,那么不幸的是,如果性能是一个考虑因素,您可能会遇到第一类字段。位域和标志字符串是解决问题的简单方法,但它们不能很好地对比生产数据集。

如果你不会担心性能,那么我会使用一个实现,其中每个属性由一个字符(“a”=“garage”,“b”=“fireplace”等)表示,而且你只是建立一个代表记录所有标记的字符串。这对于一个位域来说的主要优点是:a)人类更容易调试,并且b)您不必担心数据类型的大小。

如果表现是一个问题,那么您可能需要将他们提升到一流的领域。

2

通常我会同意你的天真假设是正确的。

如果布尔字段的数量在不断扩张(has_fusion_reactor?),你也可以考虑serializing标志

# house.rb 
class House 
    serialize :flags 
    … 
end 

# Setting flags 
@house.flags = [:fireplace, :pool, :doghouse] 
# Appending 
@house.flags << :sauna 
#Querying 
@house.flags.has_key? :porch 
#Searching 
House.where "flags LIKE ?", "pool" 
4

的阵列这里的另一个解决方案。

你可以做一个HouseAttributes模型,并建立了双向has_and_belongs_to_many协会

# house.rb 
class House 
    has_and_belongs_to_many :house_attributes 
end 

# house_attribute.rb 
class HouseAttribute 
    has_and_belongs_to_many :houses 
end 

然后换房子每个属性将是一个数据库条目。

不要忘记在数据库上设置连接表。

+2

+1在过去,我选择了这种方法,因为始终有要求(在当时或稍后)添加更多功能 - 他们要求has_solar_panels和has_futuretech之前只是时间问题。 – edralph 2012-07-10 08:16:40

1

我在想这样的事情

你有房子表(用于房屋的详细信息)

你有一种叫功能(具有的功能,如“壁炉”主表“地下室”等)

和你有一个像Houses_Features 的连接表,它已经house_id和FEATURE_ID

通过这种方式可以分配功能给定的房子。不知道这是否符合您的需求,但只是想想而已:d

感谢和问候

sameera

5

我建议the flag_shih_tzu gem。它可以帮助您将许多布尔属性存储在一个整数列中。它为您提供每个属性的命名范围以及将它们作为活动记录关系链接在一起的方法。

相关问题