在Rails和Elasticsearch中创建搜索功能并尝试各种测试 – Rspec
首先
本文将介绍使用Rspec编写使用Elasticsearch进行测试的方法。文章的前提是使用elasticsearch-rails库。
另外,本文中提到的样例和环境是基于使用Rails和Elasticsearch构建搜索功能进行各种试验的 – 第一部分:基于样例应用的创建。
环境
-
- Ruby 2.5.3
-
- Rails 5.2.2
- Elasticsearch 6.5.4
宝石
-
- rspec-rails 3.8.2
-
- elasticsearch-model 6.0.0
- elasticsearch-rails 6.0.0
有关用于测试的Elasticsearch集群
在调查过程中,我发现了很多关于使用elasticsearch-extensions gem搭建测试集群的文章,但是我觉得只要本地环境已经启动了开发用的集群,就不需要再单独启动了,所以我没有使用它。
测试代码
考虑以下已在关注点(concerns)中实现了具有标题和描述列的漫画模型的搜索处理的测试。
class Manga < ApplicationRecord
include MangaSearchable
end
module MangaSearchable
extend ActiveSupport::Concern
included do
include Elasticsearch::Model
# index名
# 環境名を入れることで開発用とは別にrspec用のindexを作成する
index_name "es_manga_#{Rails.env}"
# マッピング情報
settings do
mappings dynamic: 'false' do
indexes :id, type: 'integer'
indexes :title, type: 'text', analyzer: 'kuromoji'
indexes :description, type: 'text', analyzer: 'kuromoji'
end
end
def as_indexed_json(*)
attributes
.symbolize_keys
.slice(:id, :title, :description)
end
end
class_methods do
def create_index!
client = __elasticsearch__.client
# すでにindexを作成済みの場合は削除する
client.indices.delete index: self.index_name rescue nil
client.indices.create(index: self.index_name,
body: {
settings: self.settings.to_hash,
mappings: self.mappings.to_hash
})
end
# 今回テストする検索処理
def es_search(query)
__elasticsearch__.search({
query: {
multi_match: {
fields: %w(title description),
type: 'cross_fields',
query: query,
operator: 'and'
}
}
})
end
end
end
Rspec Rspec 是用于 Ruby 的一种行为驱动开发(BDD)框架。
关于创建指标的问题
为了对每个测试进行独立操作,我们需要为每个案例创建索引。此外,我认为在与elasticsearch相关的测试中,只需要在meta信息中进行索引创建控制,这将非常好。
以下是一个示例。
RSpec.configure do |config|
config.before :each do |example|
if example.metadata[:elasticsearch]
Manga.create_index!
end
end
测试用例
require 'rails_helper'
# elasticsearch: true を追加しindexをテストケース毎に再作成する
RSpec.describe MangaSearchable, elasticsearch: true do
describe '.es_search' do
describe '検索ワードにマッチする漫画の検索' do
let!(:manga_1) do
create(:manga, title: 'キングダム', description: '時は紀元前―。いまだ一度も統一...')
end
let!(:manga_2) do
create(:manga, title: '僕のヒーローアカデミア', description: '多くの人間が“個性という力を持つ...')
end
let!(:manga_3) do
create(:manga, title: 'はたらく細胞', description: '人間1人あたりの細胞の数、およそ60兆個...')
end
before :each do
# 作成したデータをelasticsearchに登録する
# refresh: true を追加することで登録したデータをすぐに検索できるようにする
Manga.__elasticsearch__.import(refresh: true)
end
def search_manga_ids
Manga.es_search(query).records.pluck(:id)
end
context '検索ワードがタイトルにマッチする場合' do
let(:query) { 'キングダム' }
it '検索ワードにマッチする漫画を取得する' do
expect(search_manga_ids).to eq [manga_1.id]
end
end
context '検索ワードが本文にマッチする場合' do
let(:query) { '60兆個' }
it '検索ワードにマッチする漫画を取得する' do
expect(search_manga_ids).to eq [manga_3.id]
end
end
context '検索ワードが複数ある場合' do
let(:query) { '人間 個性' }
it '両方の検索ワードにマッチする漫画を取得する' do
expect(search_manga_ids).to eq [manga_2.id]
end
end
end
end
end
刷新选项:真
重点是在导入时添加refresh: true选项。
从文件中
Elasticsearch是一种近实时的搜索平台。这意味着从您索引一个文档到它可以被搜索的时间会有一个轻微的延迟(通常为一秒钟)。
一般情况下,从注册文件开始到能够进行搜索通常需要1秒。(虽然似乎是以1秒间隔更新的,但可以更改这个间隔。)因此,如果在导入数据后立即进行搜索,由于更新未被反映,会导致测试失败。通过传递refresh: true选项,在导入时进行刷新,就可以进行搜索。
请提供一个需要翻译的具体句子。
使用Rails库elasticsearch-rails和RSpec时如何编写测试
在编写使用Elasticsearch的测试时,请不要使用sleep 1
加快ElasticSearch的索引速度