From bf34294fcb4e184a5af9fb6f18d663d647d8557a Mon Sep 17 00:00:00 2001 From: Vyr Cossont Date: Fri, 30 Dec 2022 15:11:27 -0800 Subject: [PATCH] Add is:, has:, domain:, lang: search filters --- app/chewy/statuses_index.rb | 4 ++++ app/lib/search_query_parser.rb | 2 +- app/lib/search_query_transformer.rb | 21 ++++++++++++++++++--- app/models/status.rb | 20 ++++++++++++++++++++ app/services/fetch_link_card_service.rb | 8 ++++++++ 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index e6f9a04c30..17df16b612 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -67,6 +67,10 @@ class StatusesIndex < Chewy::Index field :account_id, type: 'long' field :created_at, type: 'date' field :visibility, type: 'keyword' + field :domain, type: 'keyword', value: ->(status) { status.account.domain or Rails.configuration.x.local_domain } + field :lang, type: 'keyword', value: ->(status) { status.language } + field :is, type: 'keyword', value: ->(status) { status.searchable_is } + field :has, type: 'keyword', value: ->(status) { status.searchable_has } field :text, type: 'text', value: ->(status) { status.searchable_text } do field :stemmed, type: 'text', analyzer: 'content' diff --git a/app/lib/search_query_parser.rb b/app/lib/search_query_parser.rb index 15956d4cfd..5d6ffbf29d 100644 --- a/app/lib/search_query_parser.rb +++ b/app/lib/search_query_parser.rb @@ -9,7 +9,7 @@ class SearchQueryParser < Parslet::Parser rule(:prefix) { (term >> colon).as(:prefix) } rule(:shortcode) { (colon >> term >> colon.maybe).as(:shortcode) } rule(:phrase) { (quote >> (term >> space.maybe).repeat >> quote).as(:phrase) } - rule(:clause) { (prefix.maybe >> operator.maybe >> (phrase | term | shortcode)).as(:clause) } + rule(:clause) { (operator.maybe >> prefix.maybe >> (phrase | term | shortcode)).as(:clause) } rule(:query) { (clause >> space.maybe).repeat.as(:query) } root(:query) end diff --git a/app/lib/search_query_transformer.rb b/app/lib/search_query_transformer.rb index d1671459c1..4cba60aaea 100644 --- a/app/lib/search_query_transformer.rb +++ b/app/lib/search_query_transformer.rb @@ -35,6 +35,8 @@ class SearchQueryTransformer < Parslet::Transform { multi_match: { type: 'most_fields', query: clause.term, fields: ['text', 'text.stemmed'] } } when PhraseClause { match_phrase: { text: { query: clause.phrase } } } + when PrefixClause + { term: { clause.filter => clause.term } } else raise "Unexpected clause type: #{clause}" end @@ -99,10 +101,21 @@ class SearchQueryTransformer < Parslet::Transform class PrefixClause attr_reader :filter, :operator, :term, :order - def initialize(prefix, term) + def initialize(prefix, operator, term) + case operator + when '+', nil + @operator = :filter + when '-' + @operator = :must_not + else + raise "Unknown operator: #{str}" + end + case prefix + when 'domain', 'is', 'has', 'lang' + @filter = prefix.to_s + @term = term when 'from' - @operator = :filter @filter = :account_id username, domain = term.gsub(/\A@/, '').split('@') @@ -111,6 +124,8 @@ class SearchQueryTransformer < Parslet::Transform @term = account.id when 'sort' + raise Mastodon::SyntaxError unless operator.nil? + @operator = :order @term = :created_at @@ -133,7 +148,7 @@ class SearchQueryTransformer < Parslet::Transform operator = clause[:operator]&.to_s if clause[:prefix] - PrefixClause.new(prefix, clause[:term].to_s) + PrefixClause.new(prefix, operator, clause[:term].to_s) elsif clause[:term] TermClause.new(prefix, operator, clause[:term].to_s) elsif clause[:shortcode] diff --git a/app/models/status.rb b/app/models/status.rb index fa1c95166f..4b5dcc5111 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -173,6 +173,26 @@ class Status < ApplicationRecord ].compact.join("\n\n") end + def searchable_is + keywords = [] + keywords << :bot if account.bot? + keywords << :local if local? + keywords << :local_only if local_only + keywords << :reply if reply? + keywords + end + + def searchable_has + keywords = [] + keywords << :cw if spoiler_text? + keywords << :link if FetchLinkCardService.new.link?(self) + keywords << :media if media_attachments.present? + keywords << :mention if mentions.present? + keywords << :poll if preloadable_poll.present? + keywords << :tag if tags.present? + keywords + end + def to_log_human_identifier account.acct end diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index e5b5b730ec..86fe4d96b9 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -34,6 +34,14 @@ class FetchLinkCardService < BaseService nil end + ## + # Borrow most of this machinery to detect whether the status has at least one link. + def link?(status) + @status = status + @original_url = parse_urls + !@original_url.nil? + end + private def process_url