文档 • 社区论坛 • Stack Overflow • 报告错误 • 常见问题解答 • 支持
这个 gem 可让您轻松地将 Algolia Search API 集成到您最喜欢的 ORM 中。它基于 algoliasearch-client-ruby gem。支持 Rails 6.x 和 7.x。
您可能对提供基于autocomplete.js
的自动完成和基于InstantSearch.js
的即时搜索结果页面的示例 Ruby on Rails 应用程序感兴趣:algoliasearch-rails-example。
您可以在 Algolia 网站上找到完整的参考资料。
设置
用法
选项
objectID
指数
测试
故障排除
gem install algoliasearch-rails
将 gem 添加到您的Gemfile
中:
gem "algoliasearch-rails"
并运行:
bundle install
创建一个新文件config/initializers/algoliasearch.rb
来设置您的APPLICATION_ID
和API_KEY
。
AlgoliaSearch . configuration = { application_id : 'YourApplicationID' , api_key : 'YourAPIKey' }
该 gem 与 ActiveRecord、Mongoid 和 Sequel 兼容。
您可以通过在初始化时设置以下选项来配置各种超时阈值:
AlgoliaSearch . configuration = {
application_id : 'YourApplicationID' ,
api_key : 'YourAPIKey' ,
}
这个 gem 广泛使用 Rails 的回调来触发索引任务。如果您使用绕过after_validation
、 before_save
或after_commit
回调的方法,它不会索引您的更改。例如: update_attribute
不执行验证检查,要在更新时执行验证,请使用update_attributes
。
AlgoliaSearch
模块注入的所有方法都以algolia_
为前缀,并为关联的短名称(如果尚未定义)添加别名。
Contact . algolia_reindex! # <=> Contact.reindex!
Contact . algolia_search ( "jon doe" ) # <=> Contact.search("jon doe")
以下代码将创建一个Contact
索引并向您的Contact
模型添加搜索功能:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
attributes :first_name , :last_name , :email
end
end
您可以指定要发送的属性(这里我们限制为:first_name, :last_name, :email
)或不指定(在这种情况下,将发送所有属性)。
class Product < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
# all attributes will be sent
end
end
您还可以使用add_attribute
方法来发送所有模型属性 + 额外的属性:
class Product < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
# all attributes + extra_attr will be sent
add_attribute :extra_attr
end
def extra_attr
"extra_val"
end
end
我们提供了多种配置索引的方法,允许您调整整体索引相关性。最重要的是可搜索属性和反映唱片流行度的属性。
class Product < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
# list of attribute used to build an Algolia record
attributes :title , :subtitle , :description , :likes_count , :seller_name
# the `searchableAttributes` (formerly known as attributesToIndex) setting defines the attributes
# you want to search in: here `title`, `subtitle` & `description`.
# You need to list them by order of importance. `description` is tagged as
# `unordered` to avoid taking the position of a match into account in that attribute.
searchableAttributes [ 'title' , 'subtitle' , 'unordered(description)' ]
# the `customRanking` setting defines the ranking criteria use to compare two matching
# records in case their text-relevance is equal. It should reflect your record popularity.
customRanking [ 'desc(likes_count)' ]
end
end
要为模型建立索引,只需在类上调用reindex
即可:
Product . reindex
要索引所有模型,您可以执行以下操作:
Rails . application . eager_load! # Ensure all models are loaded (required in development).
algolia_models = ActiveRecord :: Base . descendants . select { | model | model . respond_to? ( :reindex ) }
algolia_models . each ( & :reindex )
传统的搜索实现往往在后端具有搜索逻辑和功能。当搜索体验包括用户输入搜索查询、执行该搜索,然后重定向到搜索结果页面时,这是有意义的。
不再需要在后端实现搜索。事实上,在大多数情况下,由于增加了网络和处理延迟,这对性能有害。我们强烈建议使用我们的 JavaScript API 客户端直接从最终用户的浏览器、移动设备或客户端发出所有搜索请求。它将减少整体搜索延迟,同时减轻服务器的负担。
JS API 客户端是 gem 的一部分,只需在 JavaScript 清单中的某个位置需要algolia/v3/algoliasearch.min
,例如,如果您使用的是 Rails 3.1+,则在application.js
中:
//= require algolia/v3/algoliasearch.min
然后在你的 JavaScript 代码中你可以这样做:
var client = algoliasearch ( ApplicationID , Search - Only - API - Key ) ;
var index = client . initIndex ( 'YourIndexName' ) ;
index . search ( 'something' , { hitsPerPage : 10 , page : 0 } )
. then ( function searchDone ( content ) {
console . log ( content )
} )
. catch ( function searchFailure ( err ) {
console . error ( err ) ;
} ) ;
我们最近(2015 年 3 月)发布了 JavaScript 客户端的新版本 (V3),如果您使用的是我们之前的版本 (V2),请阅读迁移指南
注意:我们建议使用 JavaScript API 客户端直接从最终用户浏览器执行查询,而无需通过您的服务器。
搜索会返回符合 ORM 的对象,并从数据库中重新加载它们。我们建议使用我们的 JavaScript API 客户端来执行查询,以减少整体延迟并减轻服务器负担。
hits = Contact . search ( "jon doe" )
p hits
p hits . raw_answer # to get the original JSON raw answer
每个 ORM 对象都会添加一个highlight_result
属性:
hits [ 0 ] . highlight_result [ 'first_name' ] [ 'value' ]
如果您想从 API 检索原始 JSON 答案,而不从数据库重新加载对象,您可以使用:
json_answer = Contact . raw_search ( "jon doe" )
p json_answer
p json_answer [ 'hits' ]
p json_answer [ 'facets' ]
搜索参数可以通过模型中静态的索引设置来指定,也可以在搜索时动态指定搜索参数作为search
方法的第二个参数:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
attribute :first_name , :last_name , :email
# default search parameters stored in the index settings
minWordSizefor1Typo 4
minWordSizefor2Typos 8
hitsPerPage 42
end
end
# dynamical search parameters
p Contact . raw_search ( 'jon doe' , { hitsPerPage : 5 , page : 2 } )
即使我们强烈建议使用 JavaScript 从前端执行所有搜索(因此分页)操作,我们也支持 will_paginate 和 kaminari 作为分页后端。
要使用:will_paginate
,请指定:pagination_backend
如下:
AlgoliaSearch . configuration = { application_id : 'YourApplicationID' , api_key : 'YourAPIKey' , pagination_backend : :will_paginate }
然后,一旦使用search
方法,返回的结果将是一个分页集:
# in your controller
@results = MyModel . search ( 'foo' , hitsPerPage : 10 )
# in your views
# if using will_paginate
<%= will_paginate @results %>
# if using kaminari
<%= paginate @results %>
使用tags
方法将标签添加到您的记录中:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
tags [ 'trusted' ]
end
end
或使用动态值:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
tags do
[ first_name . blank? || last_name . blank? ? 'partial' : 'full' , has_valid_email? ? 'valid_email' : 'invalid_email' ]
end
end
end
在查询时,指定{ tagFilters: 'tagvalue' }
或{ tagFilters: ['tagvalue1', 'tagvalue2'] }
作为搜索参数,以将结果集限制为特定标签。
可以调用搜索答案的facets
方法来检索 Facets。
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
# [...]
# specify the list of attributes available for faceting
attributesForFaceting [ :company , :zip_code ]
end
end
hits = Contact . search ( 'jon doe' , { facets : '*' } )
p hits # ORM-compliant array of objects
p hits . facets # extra method added to retrieve facets
p hits . facets [ 'company' ] # facet values+count of facet 'company'
p hits . facets [ 'zip_code' ] # facet values+count of facet 'zip_code'
raw_json = Contact . raw_search ( 'jon doe' , { facets : '*' } )
p raw_json [ 'facets' ]
您还可以搜索构面值。
Product . search_for_facet_values ( 'category' , 'Headphones' ) # Array of {value, highlighted, count}
此方法还可以采用查询可以采用的任何参数。这会将搜索调整为仅匹配查询的命中。
# Only sends back the categories containing red Apple products (and only counts those)
Product . search_for_facet_values ( 'category' , 'phone' , {
query : 'red' ,
filters : 'brand:Apple'
} ) # Array of phone categories linked to red Apple products
有关分组的不同信息的更多信息可以在此处找到。
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
# [...]
# specify the attribute to be used for distinguishing the records
# in this case the records will be grouped by company
attributeForDistinct "company"
end
end
使用geoloc
方法本地化您的记录:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
geoloc :lat_attr , :lng_attr
end
end
在查询时,指定{ aroundLatLng: "37.33, -121.89", aroundRadius: 50000 }
作为搜索参数,以将结果集限制为圣何塞周围 50KM。
每次保存一条记录时,都会对其进行异步索引。另一方面,每次销毁记录时,它都会从索引中异步删除。这意味着带有 ADD/DELETE 操作的网络调用会同步发送到 Algolia API,但随后引擎将异步处理该操作(因此,如果您在之后进行搜索,结果可能还没有反映出来)。
您可以禁用自动索引和自动删除设置以下选项:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch auto_index : false , auto_remove : false do
attribute :first_name , :last_name , :email
end
end
您可以使用without_auto_index
范围临时禁用自动索引。这通常出于性能原因而使用。
Contact . delete_all
Contact . without_auto_index do
1 . upto ( 10000 ) { Contact . create! attributes } # inside this block, auto indexing task will not run.
end
Contact . reindex! # will use batch operations
您可以配置自动索引和自动删除过程以使用队列在后台执行这些操作。默认情况下使用 ActiveJob (Rails >=4.2) 队列,但您可以定义自己的排队机制:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch enqueue : true do # ActiveJob will be triggered using a `algoliasearch` queue
attribute :first_name , :last_name , :email
end
end
如果您在后台执行更新和删除,则可以在作业实际执行之前将记录删除提交到您的数据库。因此,如果您要加载记录以将其从数据库中删除,那么您的 ActiveRecord#find 将失败并显示 RecordNotFound。
在这种情况下,您可以绕过从 ActiveRecord 加载记录,而直接与索引通信:
class MySidekiqWorker
def perform ( id , remove )
if remove
# the record has likely already been removed from your database so we cannot
# use ActiveRecord#find to load it
index = AlgoliaSearch . client . init_index ( "index_name" )
index . delete_object ( id )
else
# the record should be present
c = Contact . find ( id )
c . index!
end
end
end
如果您使用的是 Sidekiq:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch enqueue : :trigger_sidekiq_worker do
attribute :first_name , :last_name , :email
end
def self . trigger_sidekiq_worker ( record , remove )
MySidekiqWorker . perform_async ( record . id , remove )
end
end
class MySidekiqWorker
def perform ( id , remove )
if remove
# the record has likely already been removed from your database so we cannot
# use ActiveRecord#find to load it
index = AlgoliaSearch . client . init_index ( "index_name" )
index . delete_object ( id )
else
# the record should be present
c = Contact . find ( id )
c . index!
end
end
end
如果您使用的是delayed_job:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch enqueue : :trigger_delayed_job do
attribute :first_name , :last_name , :email
end
def self . trigger_delayed_job ( record , remove )
if remove
record . delay . remove_from_index!
else
record . delay . index!
end
end
end
您可以通过设置以下选项强制索引和删除同步(在这种情况下,gem 将调用wait_task
方法以确保该方法返回后已考虑该操作):(不建议这样做,除非用于测试目的)
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch synchronous : true do
attribute :first_name , :last_name , :email
end
end
默认情况下,索引名称将是类名称,例如“Contact”。您可以使用index_name
选项自定义索引名称:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch index_name : "MyCustomName" do
attribute :first_name , :last_name , :email
end
end
您可以使用以下选项在索引名称后添加当前 Rails 环境的后缀:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch per_environment : true do # index name will be "Contact_#{Rails.env}"
attribute :first_name , :last_name , :email
end
end
您可以使用块来指定复杂的属性值
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
attribute :email
attribute :full_name do
" #{ first_name } #{ last_name } "
end
add_attribute :full_name2
end
def full_name2
" #{ first_name } #{ last_name } "
end
end
注意:一旦您使用此类代码定义额外的属性,gem 就不再能够检测属性是否已更改(代码使用 Rails 的#{attribute}_changed?
方法来检测)。因此,即使其属性没有更改,您的记录也会被推送到 API。您可以通过创建_changed?
来解决此行为。方法:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch do
attribute :email
attribute :full_name do
" #{ first_name } #{ last_name } "
end
end
def full_name_changed?
first_name_changed? || last_name_changed?
end
end
您可以轻松嵌入定义额外属性的嵌套对象,返回任何符合 JSON 的对象(数组或哈希或两者的组合)。
class Profile < ActiveRecord :: Base
include AlgoliaSearch
belongs_to :user
has_many :specializations
algoliasearch do
attribute :user do
# restrict the nested "user" object to its `name` + `email`
{ name : user . name , email : user . email }
end
attribute :public_specializations do
# build an array of public specialization (include only `title` and `another_attr`)
specializations . select { | s | s . public? } . map do | s |
{ title : s . title , another_attr : s . another_attr }
end
end
end
end
对于 ActiveRecord,我们将使用touch
和after_touch
来实现这一点。
# app/models/app.rb
class App < ApplicationRecord
include AlgoliaSearch
belongs_to :author , class_name : :User
after_touch :index!
algoliasearch do
attribute :title
attribute :author do
author . as_json
end
end
end
# app/models/user.rb
class User < ApplicationRecord
# If your association uses belongs_to
# - use `touch: true`
# - do not define an `after_save` hook
has_many :apps , foreign_key : :author_id
after_save { apps . each ( & :touch ) }
end
使用 Sequel,您可以使用touch
插件来传播更改:
# app/models/app.rb
class App < Sequel :: Model
include AlgoliaSearch
many_to_one :author , class : :User
plugin :timestamps
plugin :touch
algoliasearch do
attribute :title
attribute :author do
author . to_hash
end
end
end
# app/models/user.rb
class User < Sequel :: Model
one_to_many :apps , key : :author_id
plugin :timestamps
# Can't use the associations since it won't trigger the after_save
plugin :touch
# Define the associations that need to be touched here
# Less performant, but allows for the after_save hook to trigger
def touch_associations
apps . map ( & :touch )
end
def touch
super
touch_associations
end
end
objectID
默认情况下, objectID
基于您的记录的id
。您可以通过指定:id
选项来更改此行为(请确保使用 uniq 字段)。
class UniqUser < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch id : :uniq_name do
end
end
您可以添加约束来控制是否必须使用:if
或:unless
选项对记录建立索引。
它允许您对每个文档进行条件索引。
class Post < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch if : :published? , unless : :deleted? do
end
def published?
# [...]
end
def deleted?
# [...]
end
end
注意:一旦您使用这些约束,就会执行addObjects
和deleteObjects
调用,以保持索引与数据库同步(无状态 gem 不知道该对象是否不再与您的约束匹配或从未匹配) ,因此我们强制发送 ADD/DELETE 操作)。您可以通过创建_changed?
来解决此行为。方法:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch if : :published do
end
def published
# true or false
end
def published_changed?
# return true only if you know that the 'published' state changed
end
end
您可以使用以下任一方法对记录的子集建立索引:
# will generate batch API calls (recommended)
MyModel . where ( 'updated_at > ?' , 10 . minutes . ago ) . reindex!
或者
MyModel . index_objects MyModel . limit ( 5 )
您可以使用sanitize
选项清理所有属性。它将从您的属性中删除所有 HTML 标签。
class User < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch per_environment : true , sanitize : true do
attributes :name , :email , :company
end
end
如果您使用Rails 4.2+,您还需要依赖rails-html-sanitizer
:
gem 'rails-html-sanitizer'
您可以使用force_utf8_encoding
选项强制对所有属性进行UTF-8编码:
class User < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch force_utf8_encoding : true do
attributes :name , :email , :company
end
end
注意:此选项与 Ruby 1.8 不兼容
您可以使用raise_on_failure
选项禁用尝试访问 Algolia 的 API 时可能引发的异常:
class Contact < ActiveRecord :: Base
include AlgoliaSearch
# only raise exceptions in development env
algoliasearch raise_on_failure : Rails . env . development? do
attribute :first_name , :last_name , :email
end
end
这是一个真实的配置示例(来自 HN Search):
class Item < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch per_environment : true do
# the list of attributes sent to Algolia's API
attribute :created_at , :title , :url , :author , :points , :story_text , :comment_text , :author , :num_comments , :story_id , :story_title
# integer version of the created_at datetime field, to use numerical filtering
attribute :created_at_i do
created_at . to_i
end
# `title` is more important than `{story,comment}_text`, `{story,comment}_text` more than `url`, `url` more than `author`
# btw, do not take into account position in most fields to avoid first word match boost
searchableAttributes [ 'unordered(title)' , 'unordered(story_text)' , 'unordered(comment_text)' , 'unordered(url)' , 'author' ]
# tags used for filtering
tags do
[ item_type , "author_ #{ author } " , "story_ #{ story_id } " ]
end
# use associated number of HN points to sort results (last sort criteria)
customRanking [ 'desc(points)' , 'desc(num_comments)' ]
# google+, $1.5M raises, C#: we love you
separatorsToIndex '+#$'
end
def story_text
item_type_cd != Item . comment ? text : nil
end
def story_title
comment? && story ? story . title : nil
end
def story_url
comment? && story ? story . url : nil
end
def comment_text
comment? ? text : nil
end
def comment?
item_type_cd == Item . comment
end
# [...]
end
您可以使用index!
实例方法。
c = Contact . create! ( params [ :contact ] )
c . index!
并使用remove_from_index!
实例方法。
c . remove_from_index!
c . destroy
gem 提供了 2 种方法来重新索引所有对象:
要重新索引所有记录(考虑到已删除的对象), reindex
类方法会将所有对象索引到名为<INDEX_NAME>.tmp
临时索引,并在所有内容都被索引(原子地)后将临时索引移动到最后一个索引。这是重新索引所有内容的最安全方法。
Contact . reindex
注意:如果您使用特定于索引的 API 密钥,请确保您允许<INDEX_NAME>
和<INDEX_NAME>.tmp
。
警告:在确定/过滤模型范围时不应使用此类原子重新索引操作,因为此操作会替换整个索引,仅保留过滤后的对象。即:不要执行MyModel.where(...).reindex
,而是执行MyModel.where(...).reindex!
(带有尾随!
)!!!
要重新索引所有对象(没有临时索引,因此不会删除已删除的对象),请使用reindex!
类方法:
Contact . reindex!
要清除索引,请使用clear_index!
类方法:
Contact . clear_index!
您可以通过调用index
类方法来访问底层index
对象:
index = Contact . index
# index.get_settings, index.partial_update_object, ...
您可以使用add_replica
方法定义副本索引。如果您希望副本块继承主设置,请在副本块上使用inherit: true
。
class Book < ActiveRecord :: Base
attr_protected
include AlgoliaSearch
algoliasearch per_environment : true do
searchableAttributes [ :name , :author , :editor ]
# define a replica index to search by `author` only
add_replica 'Book_by_author' , per_environment : true do
searchableAttributes [ :author ]
end
# define a replica index with custom ordering but same settings than the main block
add_replica 'Book_custom_order' , inherit : true , per_environment : true do
customRanking [ 'asc(rank)' ]
end
end
end
要使用副本进行搜索,请使用以下代码:
Book . raw_search 'foo bar' , replica : 'Book_by_editor'
# or
Book . search 'foo bar' , replica : 'Book_by_editor'
在多个模型之间共享索引是有意义的。为了实现这一点,您需要确保与底层模型的objectID
没有任何冲突。
class Student < ActiveRecord :: Base
attr_protected
include AlgoliaSearch
algoliasearch index_name : 'people' , id : :algolia_id do
# [...]
end
private
def algolia_id
"student_ #{ id } " # ensure the teacher & student IDs are not conflicting
end
end
class Teacher < ActiveRecord :: Base
attr_protected
include AlgoliaSearch
algoliasearch index_name : 'people' , id : :algolia_id do
# [...]
end
private
def algolia_id
"teacher_ #{ id } " # ensure the teacher & student IDs are not conflicting
end
end
注意:如果您针对多个模型中的单个索引,则绝对不能使用MyModel.reindex
,而只能使用MyModel.reindex!
。 reindex
方法使用临时索引来执行原子重新索引:如果使用它,生成的索引将仅包含当前模型的记录,因为它不会重新索引其他模型。
您可以使用add_index
方法在多个索引中索引记录:
class Book < ActiveRecord :: Base
attr_protected
include AlgoliaSearch
PUBLIC_INDEX_NAME = "Book_ #{ Rails . env } "
SECURED_INDEX_NAME = "SecuredBook_ #{ Rails . env } "
# store all books in index 'SECURED_INDEX_NAME'
algoliasearch index_name : SECURED_INDEX_NAME do
searchableAttributes [ :name , :author ]
# convert security to tags
tags do
[ released ? 'public' : 'private' , premium ? 'premium' : 'standard' ]
end
# store all 'public' (released and not premium) books in index 'PUBLIC_INDEX_NAME'
add_index PUBLIC_INDEX_NAME , if : :public? do
searchableAttributes [ :name , :author ]
end
end
private
def public?
released && ! premium
end
end
要使用额外索引进行搜索,请使用以下代码:
Book . raw_search 'foo bar' , index : 'Book_by_editor'
# or
Book . search 'foo bar' , index : 'Book_by_editor'
要运行规范,请设置ALGOLIA_APPLICATION_ID
和ALGOLIA_API_KEY
环境变量。由于测试正在创建和删除索引,因此请勿使用您的生产帐户。
您可能想禁用所有索引(添加、更新和删除操作)API 调用,您可以设置disable_indexing
选项:
class User < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch per_environment : true , disable_indexing : Rails . env . test? do
end
end
class User < ActiveRecord :: Base
include AlgoliaSearch
algoliasearch per_environment : true , disable_indexing : Proc . new { Rails . env . test? || more_complex_condition } do
end
end
遇到问题?在寻求支持之前,我们建议您先查看我们的常见问题解答,您可以在其中找到最常见问题和客户遇到的问题的答案。
如果您想为该项目做出贡献而不安装其所有依赖项,则可以使用我们的 Docker 映像。请查看我们的专用指南以了解更多信息。