Add `in:library` syntax to search (#26760)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
ac3f310f4b
commit
ece1ff77d6
|
@ -9,23 +9,90 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
before
|
||||
after
|
||||
during
|
||||
in
|
||||
).freeze
|
||||
|
||||
class Query
|
||||
attr_reader :must_not_clauses, :must_clauses, :filter_clauses
|
||||
def initialize(clauses, options = {})
|
||||
raise ArgumentError if options[:current_account].nil?
|
||||
|
||||
def initialize(clauses)
|
||||
grouped = clauses.compact.chunk(&:operator).to_h
|
||||
@must_not_clauses = grouped.fetch(:must_not, [])
|
||||
@must_clauses = grouped.fetch(:must, [])
|
||||
@filter_clauses = grouped.fetch(:filter, [])
|
||||
@clauses = clauses
|
||||
@options = options
|
||||
|
||||
flags_from_clauses!
|
||||
end
|
||||
|
||||
def apply(search)
|
||||
def request
|
||||
search = Chewy::Search::Request.new(*indexes).filter(default_filter)
|
||||
|
||||
must_clauses.each { |clause| search = search.query.must(clause.to_query) }
|
||||
must_not_clauses.each { |clause| search = search.query.must_not(clause.to_query) }
|
||||
filter_clauses.each { |clause| search = search.filter(**clause.to_query) }
|
||||
search.query.minimum_should_match(1)
|
||||
|
||||
search
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clauses_by_operator
|
||||
@clauses_by_operator ||= @clauses.compact.chunk(&:operator).to_h
|
||||
end
|
||||
|
||||
def flags_from_clauses!
|
||||
@flags = clauses_by_operator.fetch(:flag, []).to_h { |clause| [clause.prefix, clause.term] }
|
||||
end
|
||||
|
||||
def must_clauses
|
||||
clauses_by_operator.fetch(:must, [])
|
||||
end
|
||||
|
||||
def must_not_clauses
|
||||
clauses_by_operator.fetch(:must_not, [])
|
||||
end
|
||||
|
||||
def filter_clauses
|
||||
clauses_by_operator.fetch(:filter, [])
|
||||
end
|
||||
|
||||
def indexes
|
||||
case @flags['in']
|
||||
when 'library'
|
||||
[StatusesIndex]
|
||||
else
|
||||
[PublicStatusesIndex, StatusesIndex]
|
||||
end
|
||||
end
|
||||
|
||||
def default_filter
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {
|
||||
_index: PublicStatusesIndex.index_name,
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
_index: StatusesIndex.index_name,
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
searchable_by: @options[:current_account].id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,6 +175,9 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
@filter = :created_at
|
||||
@type = :range
|
||||
@term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||
when 'in'
|
||||
@operator = :flag
|
||||
@term = term
|
||||
else
|
||||
raise "Unknown prefix: #{prefix}"
|
||||
end
|
||||
|
@ -176,6 +246,6 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
end
|
||||
|
||||
rule(query: sequence(:clauses)) do
|
||||
Query.new(clauses)
|
||||
Query.new(clauses, current_account: current_account)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,20 +14,8 @@ class StatusesSearchService < BaseService
|
|||
private
|
||||
|
||||
def status_search_results
|
||||
definition = parsed_query.apply(
|
||||
Chewy::Search::Request.new(StatusesIndex, PublicStatusesIndex).filter(
|
||||
bool: {
|
||||
should: [
|
||||
publicly_searchable,
|
||||
non_publicly_searchable,
|
||||
],
|
||||
|
||||
minimum_should_match: 1,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
results = definition.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact
|
||||
request = parsed_query.request
|
||||
results = request.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact
|
||||
account_ids = results.map(&:account_id)
|
||||
account_domains = results.map(&:account_domain)
|
||||
preloaded_relations = @account.relations_map(account_ids, account_domains)
|
||||
|
@ -37,27 +25,6 @@ class StatusesSearchService < BaseService
|
|||
[]
|
||||
end
|
||||
|
||||
def publicly_searchable
|
||||
{
|
||||
term: { _index: PublicStatusesIndex.index_name },
|
||||
}
|
||||
end
|
||||
|
||||
def non_publicly_searchable
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: { _index: StatusesIndex.index_name },
|
||||
},
|
||||
{
|
||||
term: { searchable_by: @account.id },
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def parsed_query
|
||||
SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account)
|
||||
end
|
||||
|
|
|
@ -3,17 +3,18 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe SearchQueryTransformer do
|
||||
subject { described_class.new.apply(parser, current_account: nil) }
|
||||
subject { described_class.new.apply(parser, current_account: account) }
|
||||
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:parser) { SearchQueryParser.new.parse(query) }
|
||||
|
||||
context 'with "hello world"' do
|
||||
let(:query) { 'hello world' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello world)
|
||||
expect(subject.must_not_clauses).to be_empty
|
||||
expect(subject.filter_clauses).to be_empty
|
||||
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello world)
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -21,9 +22,9 @@ describe SearchQueryTransformer do
|
|||
let(:query) { 'hello -world' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello)
|
||||
expect(subject.must_not_clauses.map(&:term)).to match_array %w(world)
|
||||
expect(subject.filter_clauses).to be_empty
|
||||
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello)
|
||||
expect(subject.send(:must_not_clauses).map(&:term)).to match_array %w(world)
|
||||
expect(subject.send(:filter_clauses)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,9 +32,9 @@ describe SearchQueryTransformer do
|
|||
let(:query) { 'hello is:reply' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello)
|
||||
expect(subject.must_not_clauses).to be_empty
|
||||
expect(subject.filter_clauses.map(&:term)).to match_array %w(reply)
|
||||
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello)
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses).map(&:term)).to match_array %w(reply)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,9 +42,9 @@ describe SearchQueryTransformer do
|
|||
let(:query) { 'foo: bar' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.must_clauses.map(&:term)).to match_array %w(foo bar)
|
||||
expect(subject.must_not_clauses).to be_empty
|
||||
expect(subject.filter_clauses).to be_empty
|
||||
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(foo bar)
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,9 +52,9 @@ describe SearchQueryTransformer do
|
|||
let(:query) { 'foo:bar' }
|
||||
|
||||
it 'transforms clauses' do
|
||||
expect(subject.must_clauses.map(&:term)).to contain_exactly('foo bar')
|
||||
expect(subject.must_not_clauses).to be_empty
|
||||
expect(subject.filter_clauses).to be_empty
|
||||
expect(subject.send(:must_clauses).map(&:term)).to contain_exactly('foo bar')
|
||||
expect(subject.send(:must_not_clauses)).to be_empty
|
||||
expect(subject.send(:filter_clauses)).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Reference in New Issue