Rails 模型中不区分大小写的搜索
我的产品模型包含一些项目,
Product.first
=> #<Product id: 10, name: "Blue jeans" >
我现在正在从另一个数据集中导入一些产品参数,但名称拼写不一致。例如,在另一个数据集中,Blue jeans
可以拼写为 Blue Jeans
。
我想要 Product.find_or_create_by_name("Blue Jeans")
,但这将创建一个新产品,几乎与第一个产品相同。如果我想查找并比较小写名称,我有什么选择?
性能问题在这里并不重要:只有 100-200 个产品,我想将其作为导入数据的迁移来运行。
有什么想法吗?
My product model contains some items
Product.first
=> #<Product id: 10, name: "Blue jeans" >
I'm now importing some product parameters from another dataset, but there are inconsistencies in the spelling of the names. For instance, in the other dataset, Blue jeans
could be spelled Blue Jeans
.
I wanted to Product.find_or_create_by_name("Blue Jeans")
, but this will create a new product, almost identical to the first. What are my options if I want to find and compare the lowercased name.
Performance issues is not really important here: There are only 100-200 products, and I want to run this as a migration that imports the data.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(21)
你可能需要在这里更详细
You'll probably have to be more verbose here
这是 Rails 中的完整设置,供我自己参考。如果它也对你有帮助我很高兴。
查询:
验证器:
索引(答案来自 Rails/ActiveRecord 中不区分大小写的唯一索引? ):
我希望有一种更漂亮的方法来完成第一个和最后一个,但话又说回来,Rails 和 ActiveRecord 是开源的,我们不应该抱怨 - 我们可以自己实现它并发送拉取请求。
This is a complete setup in Rails, for my own reference. I'm happy if it helps you too.
the query:
the validator:
the index (answer from Case-insensitive unique index in Rails/ActiveRecord?):
I wish there was a more beautiful way to do the first and the last, but then again, Rails and ActiveRecord is open source, we shouldn't complain - we can implement it ourselves and send pull request.
如果您使用的是 Postegres 和 Rails 4+,那么您可以选择使用列类型 CITEXT,这将允许不区分大小写的查询,而无需编写查询逻辑。
迁移:
为了测试它,您应该期待以下内容:
If you are using Postegres and Rails 4+, then you have the option of using column type CITEXT, which will allow case insensitive queries without having to write out the query logic.
The migration:
And to test it out you should expect the following:
有几条评论提到了 Arel,但没有提供示例。
下面是一个不区分大小写的搜索的 Arel 示例:
这种类型的解决方案的优点是它与数据库无关 - 它将为您当前的适配器使用正确的 SQL 命令(
matches
将使用 <对于 Postgres,则为 code>ILIKE;对于其他所有内容,则为LIKE
)。Several comments refer to Arel, without providing an example.
Here is an Arel example of a case-insensitive search:
The advantage of this type of solution is that it is database-agnostic - it will use the correct SQL commands for your current adapter (
matches
will useILIKE
for Postgres, andLIKE
for everything else).您可能需要使用以下内容:
请注意,默认情况下设置为 :case_sensitive => false,所以如果你没有改变其他方式,你甚至不需要写这个选项。
欲了解更多信息,请访问:
http://api.rubyonrails.org/classes/ActiveRecord /Validations/ClassMethods.html#method-i-validates_uniqueness_of
You might want to use the following:
Please note that by default the setting is :case_sensitive => false, so you don't even need to write this option if you haven't changed other ways.
Find more at:
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of
在 postgres 中:
In postgres:
与安德鲁斯类似,#1:
对我有用的是:
这消除了在同一查询中执行
#where
和#first
的需要。希望这有帮助!Similar to Andrews which is #1:
Something that worked for me is:
This eliminates the need to do a
#where
and#first
in the same query. Hope this helps!引用 SQLite 文档:
...我不知道。但它有效:
所以你可以这样做:
不是
#find_or_create
,我知道,而且它可能不是非常跨数据库友好,但值得一看?Quoting from the SQLite documentation:
...which I didn't know.But it works:
So you could do something like this:
Not
#find_or_create
, I know, and it may not be very cross-database friendly, but worth looking at?另一种没有人提到的方法是将不区分大小写的查找器添加到 ActiveRecord::Base 中。详细信息可以在此处。这种方法的优点是您不必修改每个模型,也不必将
lower()
子句添加到所有不区分大小写的查询中,您只需使用不同的查找器方法代替。Another approach that no one has mentioned is to add case insensitive finders into ActiveRecord::Base. Details can be found here. The advantage of this approach is that you don't have to modify every model, and you don't have to add the
lower()
clause to all your case insensitive queries, you just use a different finder method instead.另一种选择可以是
An alternative can be
大小写字母仅相差一位。搜索它们的最有效方法是忽略此位,而不是转换下限或上限等。对于 MSSQL,请参阅关键字
COLLATION
,如果使用 Oracle,请参阅NLS_SORT=BINARY_CI
, ETC。Upper and lower case letters differ only by a single bit. The most efficient way to search them is to ignore this bit, not to convert lower or upper, etc. See keywords
COLLATION
for MSSQL, seeNLS_SORT=BINARY_CI
if using Oracle, etc.Find_or_create 现已弃用,您应该使用 AR 关系加上first_or_create,如下所示:
这将返回第一个匹配的对象,如果不存在,则为您创建一个。
Find_or_create is now deprecated, you should use an AR Relation instead plus first_or_create, like so:
This will return the first matched object, or create one for you if none exists.
Rails 内置了不区分大小写的搜索。它解释了数据库实现的差异。使用内置 Arel 库,或者像 Squeel 这样的宝石。
Case-insensitive searching comes built-in with Rails. It accounts for differences in database implementations. Use either the built-in Arel library, or a gem like Squeel.
这里有很多很好的答案,特别是@oma的。但您可以尝试的另一件事是使用自定义列序列化。如果您不介意数据库中的所有内容都以小写形式存储,那么您可以创建:
然后在您的模型中:
这种方法的好处是您仍然可以使用所有常规查找器(包括
find_or_create_by
),而无需在查询中使用自定义范围、函数或使用lower(name) = ?
。缺点是您会丢失数据库中的大小写信息。
There are lots of great answers here, particularly @oma's. But one other thing you could try is to use custom column serialization. If you don't mind everything being stored lowercase in your db then you could create:
Then in your model:
The benefit of this approach is that you can still use all the regular finders (including
find_or_create_by
) without using custom scopes, functions, or havinglower(name) = ?
in your queries.The downside is that you lose casing information in the database.
如果您使用 postgres(可能是其他),我喜欢这个解决方案。
我更喜欢这个有几个原因。
%
,这很简单。If you're using postgres (probably others), I like this solution.
I like this better for a couple reasons.
%
, it's straightforward.您还可以使用如下所示的范围,并将它们放在关注点中,并将其包含在您可能需要它们的模型中:
scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }
然后像这样使用:
Model.ci_find('列', '值')
You can also use scopes like this below and put them in a concern and include in models you may need them:
scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }
Then use like this:
Model.ci_find('column', 'value')
假设您使用 mysql,您可以使用不区分大小写的字段: http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
Assuming that you use mysql, you could use fields that are not case sensitive: http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
有些人使用 LIKE 或 ILIKE 进行显示,但这些允许正则表达式搜索。而且在 Ruby 中你不需要小写。您可以让数据库为您做这件事。我想可能会更快。
first_or_create
也可以用在where
之后。Some people show using LIKE or ILIKE, but those allow regex searches. Also you don't need to downcase in Ruby. You can let the database do it for you. I think it may be faster. Also
first_or_create
can be used afterwhere
.您可以在模型中像这样使用
,并在任何您喜欢的地方使用
您可以传递多列进行搜索,
您可以像这样使用单列搜索
You can use like this in model
and use wherever you like this
You can pass multiple column for search
for single column search you can use like this
到目前为止,我使用 Ruby 制定了一个解决方案。将其放入产品模型中:
这将为我提供第一个名称匹配的产品。或者为零。
So far, I made a solution using Ruby. Place this inside the Product model:
This will give me the first product where names match. Or nil.