diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index 888a638c9c..a7a0df40da 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -92,6 +92,6 @@ class AccountsIndex < Chewy::Index field(:domain, type: 'keyword', value: ->(account) { account.domain || '' }) field(:display_name, type: 'text', analyzer: 'verbatim') { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } field(:username, type: 'text', analyzer: 'verbatim', value: ->(account) { [account.username, account.domain].compact.join('@') }) { field :edge_ngram, type: 'text', analyzer: 'edge_ngram', search_analyzer: 'verbatim' } - field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(account) { account.searchable_text }) + field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(account) { account.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'natural') } end end diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 806fd42ecb..6ae13418f4 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -87,7 +87,7 @@ class PublicStatusesIndex < Chewy::Index root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text }) + field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) }) field(:language, type: 'keyword') field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' }) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index fbf59d6404..ff6e0e7672 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -107,7 +107,7 @@ class StatusesIndex < Chewy::Index root date_detection: false do field(:id, type: 'long') field(:account_id, type: 'long') - field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text }) + field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text }) { field(:stemmed, type: 'text', analyzer: 'content') } field(:tags, type: 'text', analyzer: 'hashtag', value: ->(status) { status.tags.map(&:display_name) }) field(:searchable_by, type: 'long', value: ->(status) { status.searchable_by }) field(:mentioned_by, type: 'long', value: ->(status) { status.mentioned_by }) diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index ed73751cb5..89b16c1d2e 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -237,7 +237,7 @@ class SearchQueryTransformer < Parslet::Transform else # Memo for checking when manually merge # { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } - { match_phrase: { text: { query: @term } } } + { multi_match: { type: 'most_fields', query: @term, fields: ['text', 'text.stemmed'], operator: 'and' } } end end end @@ -251,6 +251,8 @@ class SearchQueryTransformer < Parslet::Transform end def to_query + # Memo for checking when manually merge + # { match_phrase: { text: { query: @phrase } } } { match_phrase: { text: { query: @phrase } } } end end diff --git a/spec/search/services/statuses_search_service_spec.rb b/spec/search/services/statuses_search_service_spec.rb index 51245f7354..8bc236e4a6 100644 --- a/spec/search/services/statuses_search_service_spec.rb +++ b/spec/search/services/statuses_search_service_spec.rb @@ -213,4 +213,54 @@ describe StatusesSearchService do end end end + + describe 'a local user posts with search keyword' do + subject do + described_class.new.call(search_keyword, account, limit: 10).map(&:id) + end + + let(:search_keyword) { 'ohagi' } + let(:status_text) { 'りんごを食べました' } + + let(:alice) { Fabricate(:user).account } + let(:account) { alice } + let!(:status) { Fabricate(:status, text: status_text, account: alice, searchability: :public) } + + before do + alice.update!(username: 'alice') + StatusesIndex.import! + end + + shared_examples 'hit status' do |name, keyword| + context name do + let(:search_keyword) { keyword } + + it 'a status hits' do + expect(subject.count).to eq 1 + expect(subject).to include status.id + end + end + end + + shared_examples 'does not hit status' do |name, keyword| + context name do + let(:search_keyword) { keyword } + + it 'no statuses hit' do + expect(subject.count).to eq 0 + end + end + end + + it_behaves_like 'hit status', 'when search with word', 'りんご' + it_behaves_like 'hit status', 'when search with multiple words', 'りんご 食べる' + it_behaves_like 'does not hit status', 'when search with multiple words but does not hit half', 'りんご 茹でる' + it_behaves_like 'hit status', 'when search with letter in word', 'ご' + it_behaves_like 'does not hit status', 'when double quote search with letter in word', '"ご"' + it_behaves_like 'hit status', 'when search with fixed word', '"りんご"' + it_behaves_like 'hit status', 'when double quote search with multiple letter in word', 'り ご' + it_behaves_like 'does not hit status', 'when double quote search with multiple letter in word but does not contain half', 'ず ご' + it_behaves_like 'hit status', 'when specify user name', 'りんご from:alice' + it_behaves_like 'does not hit status', 'when specify not existing user name', 'りんご from:ohagi' + end end