forked from mirrors/catstodon
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
3 changed files with 98 additions and 60 deletions
|
@ -9,23 +9,90 @@ class SearchQueryTransformer < Parslet::Transform
|
||||||
before
|
before
|
||||||
after
|
after
|
||||||
during
|
during
|
||||||
|
in
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
class Query
|
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)
|
@clauses = clauses
|
||||||
grouped = clauses.compact.chunk(&:operator).to_h
|
@options = options
|
||||||
@must_not_clauses = grouped.fetch(:must_not, [])
|
|
||||||
@must_clauses = grouped.fetch(:must, [])
|
flags_from_clauses!
|
||||||
@filter_clauses = grouped.fetch(:filter, [])
|
|
||||||
end
|
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_clauses.each { |clause| search = search.query.must(clause.to_query) }
|
||||||
must_not_clauses.each { |clause| search = search.query.must_not(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) }
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,6 +175,9 @@ class SearchQueryTransformer < Parslet::Transform
|
||||||
@filter = :created_at
|
@filter = :created_at
|
||||||
@type = :range
|
@type = :range
|
||||||
@term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
@term = { gte: term, lte: term, time_zone: @options[:current_account]&.user_time_zone.presence || 'UTC' }
|
||||||
|
when 'in'
|
||||||
|
@operator = :flag
|
||||||
|
@term = term
|
||||||
else
|
else
|
||||||
raise "Unknown prefix: #{prefix}"
|
raise "Unknown prefix: #{prefix}"
|
||||||
end
|
end
|
||||||
|
@ -176,6 +246,6 @@ class SearchQueryTransformer < Parslet::Transform
|
||||||
end
|
end
|
||||||
|
|
||||||
rule(query: sequence(:clauses)) do
|
rule(query: sequence(:clauses)) do
|
||||||
Query.new(clauses)
|
Query.new(clauses, current_account: current_account)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,20 +14,8 @@ class StatusesSearchService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def status_search_results
|
def status_search_results
|
||||||
definition = parsed_query.apply(
|
request = parsed_query.request
|
||||||
Chewy::Search::Request.new(StatusesIndex, PublicStatusesIndex).filter(
|
results = request.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact
|
||||||
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
|
|
||||||
account_ids = results.map(&:account_id)
|
account_ids = results.map(&:account_id)
|
||||||
account_domains = results.map(&:account_domain)
|
account_domains = results.map(&:account_domain)
|
||||||
preloaded_relations = @account.relations_map(account_ids, account_domains)
|
preloaded_relations = @account.relations_map(account_ids, account_domains)
|
||||||
|
@ -37,27 +25,6 @@ class StatusesSearchService < BaseService
|
||||||
[]
|
[]
|
||||||
end
|
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
|
def parsed_query
|
||||||
SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account)
|
SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query), current_account: @account)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe SearchQueryTransformer do
|
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) }
|
let(:parser) { SearchQueryParser.new.parse(query) }
|
||||||
|
|
||||||
context 'with "hello world"' do
|
context 'with "hello world"' do
|
||||||
let(:query) { 'hello world' }
|
let(:query) { 'hello world' }
|
||||||
|
|
||||||
it 'transforms clauses' do
|
it 'transforms clauses' do
|
||||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello world)
|
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello world)
|
||||||
expect(subject.must_not_clauses).to be_empty
|
expect(subject.send(:must_not_clauses)).to be_empty
|
||||||
expect(subject.filter_clauses).to be_empty
|
expect(subject.send(:filter_clauses)).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,9 +22,9 @@ describe SearchQueryTransformer do
|
||||||
let(:query) { 'hello -world' }
|
let(:query) { 'hello -world' }
|
||||||
|
|
||||||
it 'transforms clauses' do
|
it 'transforms clauses' do
|
||||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello)
|
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello)
|
||||||
expect(subject.must_not_clauses.map(&:term)).to match_array %w(world)
|
expect(subject.send(:must_not_clauses).map(&:term)).to match_array %w(world)
|
||||||
expect(subject.filter_clauses).to be_empty
|
expect(subject.send(:filter_clauses)).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -31,9 +32,9 @@ describe SearchQueryTransformer do
|
||||||
let(:query) { 'hello is:reply' }
|
let(:query) { 'hello is:reply' }
|
||||||
|
|
||||||
it 'transforms clauses' do
|
it 'transforms clauses' do
|
||||||
expect(subject.must_clauses.map(&:term)).to match_array %w(hello)
|
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(hello)
|
||||||
expect(subject.must_not_clauses).to be_empty
|
expect(subject.send(:must_not_clauses)).to be_empty
|
||||||
expect(subject.filter_clauses.map(&:term)).to match_array %w(reply)
|
expect(subject.send(:filter_clauses).map(&:term)).to match_array %w(reply)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,9 +42,9 @@ describe SearchQueryTransformer do
|
||||||
let(:query) { 'foo: bar' }
|
let(:query) { 'foo: bar' }
|
||||||
|
|
||||||
it 'transforms clauses' do
|
it 'transforms clauses' do
|
||||||
expect(subject.must_clauses.map(&:term)).to match_array %w(foo bar)
|
expect(subject.send(:must_clauses).map(&:term)).to match_array %w(foo bar)
|
||||||
expect(subject.must_not_clauses).to be_empty
|
expect(subject.send(:must_not_clauses)).to be_empty
|
||||||
expect(subject.filter_clauses).to be_empty
|
expect(subject.send(:filter_clauses)).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,9 +52,9 @@ describe SearchQueryTransformer do
|
||||||
let(:query) { 'foo:bar' }
|
let(:query) { 'foo:bar' }
|
||||||
|
|
||||||
it 'transforms clauses' do
|
it 'transforms clauses' do
|
||||||
expect(subject.must_clauses.map(&:term)).to contain_exactly('foo bar')
|
expect(subject.send(:must_clauses).map(&:term)).to contain_exactly('foo bar')
|
||||||
expect(subject.must_not_clauses).to be_empty
|
expect(subject.send(:must_not_clauses)).to be_empty
|
||||||
expect(subject.filter_clauses).to be_empty
|
expect(subject.send(:filter_clauses)).to be_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue