Add my reacted statuses search
This commit is contained in:
parent
7b88422644
commit
5fb6bce744
9 changed files with 124 additions and 16 deletions
|
@ -157,6 +157,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 :stemmed, type: 'text', analyzer: 'natural' }
|
||||
field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(account) { account.searchable_text })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -149,7 +149,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(:stemmed, type: 'text', analyzer: 'sudachi_analyzer') }
|
||||
field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text })
|
||||
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 || '' })
|
||||
|
|
|
@ -160,17 +160,26 @@ class StatusesIndex < Chewy::Index
|
|||
if status.searchability == 'direct'
|
||||
status.searchable_by.empty?
|
||||
else
|
||||
status.searchability == 'limited' ? status.account.domain.present? : false
|
||||
status.searchability == 'limited' ? !status.local? : false
|
||||
end
|
||||
}
|
||||
|
||||
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(:stemmed, type: 'text', analyzer: 'sudachi_analyzer') }
|
||||
field(:text, type: 'text', analyzer: 'sudachi_analyzer', value: ->(status) { status.searchable_text })
|
||||
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 })
|
||||
field(:favourited_by, type: 'long', value: ->(status) { status.favourited_by })
|
||||
field(:reblogged_by, type: 'long', value: ->(status) { status.reblogged_by })
|
||||
field(:bookmarked_by, type: 'long', value: ->(status) { status.bookmarked_by })
|
||||
field(:bookmark_categoried_by, type: 'long', value: ->(status) { status.bookmark_categoried_by })
|
||||
field(:emoji_reacted_by, type: 'long', value: ->(status) { status.emoji_reacted_by })
|
||||
field(:referenced_by, type: 'long', value: ->(status) { status.referenced_by })
|
||||
field(:voted_by, type: 'long', value: ->(status) { status.voted_by })
|
||||
field(:searchability, type: 'keyword', value: ->(status) { status.compute_searchability })
|
||||
field(:visibility, type: 'keyword', value: ->(status) { status.searchable_visibility })
|
||||
field(:language, type: 'keyword')
|
||||
field(:domain, type: 'keyword', value: ->(status) { status.account.domain || '' })
|
||||
field(:properties, type: 'keyword', value: ->(status) { status.searchable_properties })
|
||||
|
|
|
@ -59,13 +59,15 @@ class Search extends PureComponent {
|
|||
defaultOptions = [
|
||||
{ label: <><mark>has:</mark> <FormattedList type='disjunction' value={['media', 'poll', 'embed']} /></>, action: e => { e.preventDefault(); this._insertText('has:') } },
|
||||
{ label: <><mark>is:</mark> <FormattedList type='disjunction' value={['reply', 'sensitive']} /></>, action: e => { e.preventDefault(); this._insertText('is:') } },
|
||||
{ label: <><mark>my:</mark> <FormattedList type='disjunction' value={['favourited', 'bookmarked', 'boosted']} /></>, action: e => { e.preventDefault(); this._insertText('my:') } },
|
||||
{ label: <><mark>language:</mark> <FormattedMessage id='search_popout.language_code' defaultMessage='ISO language code' /></>, action: e => { e.preventDefault(); this._insertText('language:') } },
|
||||
{ label: <><mark>from:</mark> <FormattedMessage id='search_popout.user' defaultMessage='user' /></>, action: e => { e.preventDefault(); this._insertText('from:') } },
|
||||
{ label: <><mark>domain:</mark> <FormattedMessage id='search_popout.domain' defaultMessage='domain' /></>, action: e => { e.preventDefault(); this._insertText('domain:') } },
|
||||
{ label: <><mark>before:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('before:') } },
|
||||
{ label: <><mark>during:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('during:') } },
|
||||
{ label: <><mark>after:</mark> <FormattedMessage id='search_popout.specific_date' defaultMessage='specific date' /></>, action: e => { e.preventDefault(); this._insertText('after:') } },
|
||||
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library']} /></>, action: e => { e.preventDefault(); this._insertText('in:') } }
|
||||
{ label: <><mark>in:</mark> <FormattedList type='disjunction' value={['all', 'library']} /></>, action: e => { e.preventDefault(); this._insertText('in:') } },
|
||||
{ label: <><mark>order:</mark> <FormattedList type='disjunction' value={['desc', 'asc']} /></>, action: e => { e.preventDefault(); this._insertText('order:') } },
|
||||
];
|
||||
|
||||
setRef = c => {
|
||||
|
|
|
@ -4,6 +4,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
SUPPORTED_PREFIXES = %w(
|
||||
has
|
||||
is
|
||||
my
|
||||
language
|
||||
from
|
||||
before
|
||||
|
@ -11,6 +12,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
during
|
||||
in
|
||||
domain
|
||||
order
|
||||
).freeze
|
||||
|
||||
class Query
|
||||
|
@ -34,6 +36,18 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
search
|
||||
end
|
||||
|
||||
def order_by
|
||||
return @order_by if @order_by
|
||||
|
||||
@order_by = 'desc'
|
||||
order_clauses.each { |clause| @order_by = clause.term }
|
||||
@order_by
|
||||
end
|
||||
|
||||
def valid
|
||||
must_clauses.any? || must_not_clauses.any? || filter_clauses.any?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clauses_by_operator
|
||||
|
@ -56,6 +70,10 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
clauses_by_operator.fetch(:filter, [])
|
||||
end
|
||||
|
||||
def order_clauses
|
||||
clauses_by_operator.fetch(:order, [])
|
||||
end
|
||||
|
||||
def indexes
|
||||
case @flags['in']
|
||||
when 'library'
|
||||
|
@ -214,6 +232,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
if @term.start_with?('#')
|
||||
{ match: { tags: { query: @term, operator: 'and' } } }
|
||||
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 } } }
|
||||
end
|
||||
|
@ -236,7 +255,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
class PrefixClause
|
||||
attr_reader :operator, :prefix, :term
|
||||
|
||||
def initialize(prefix, operator, term, options = {})
|
||||
def initialize(prefix, operator, term, options = {}) # rubocop:disable Metrics/CyclomaticComplexity
|
||||
@prefix = prefix
|
||||
@negated = operator == '-'
|
||||
@options = options
|
||||
|
@ -274,6 +293,39 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
when 'in'
|
||||
@operator = :flag
|
||||
@term = term
|
||||
when 'my'
|
||||
@type = :term
|
||||
@term = @options[:current_account]&.id
|
||||
case term
|
||||
when 'favourited', 'favorited', 'fav'
|
||||
@filter = :favourited_by
|
||||
when 'boosted', 'bt'
|
||||
@filter = :reblogged_by
|
||||
when 'replied', 'mentioned', 're'
|
||||
@filter = :mentioned_by
|
||||
when 'referenced', 'ref'
|
||||
@filter = :referenced_by
|
||||
when 'emoji_reacted', 'stamped', 'stamp'
|
||||
@filter = :emoji_reacted_by
|
||||
when 'bookmarked', 'bm'
|
||||
@filter = :bookmarked_by
|
||||
when 'categoried', 'bmc'
|
||||
@filter = :bookmark_categoried_by
|
||||
when 'voted', 'vote'
|
||||
@filter = :voted_by
|
||||
when 'interacted', 'act'
|
||||
@filter = :searchable_by
|
||||
else
|
||||
raise "Unknown prefix: my:#{term}"
|
||||
end
|
||||
when 'order'
|
||||
@operator = :order
|
||||
@term = case term
|
||||
when 'asc'
|
||||
term
|
||||
else
|
||||
'desc'
|
||||
end
|
||||
else
|
||||
raise "Unknown prefix: #{prefix}"
|
||||
end
|
||||
|
@ -302,7 +354,7 @@ class SearchQueryTransformer < Parslet::Transform
|
|||
end
|
||||
|
||||
def domain_from_term(term)
|
||||
return '' if %w(local me).include?(term)
|
||||
return '' if ['local', 'me', Rails.configuration.x.local_domain].include?(term)
|
||||
|
||||
term
|
||||
end
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
#
|
||||
|
||||
class BookmarkCategoryStatus < ApplicationRecord
|
||||
include Paginable
|
||||
|
||||
update_index('statuses', :status) if Chewy.enabled?
|
||||
|
||||
belongs_to :bookmark_category
|
||||
belongs_to :status
|
||||
belongs_to :bookmark
|
||||
|
|
|
@ -13,18 +13,48 @@ module StatusSearchConcern
|
|||
|
||||
ids << account_id if local?
|
||||
|
||||
ids += local_mentioned.pluck(:id)
|
||||
ids += local_favorited.pluck(:id)
|
||||
ids += local_reblogged.pluck(:id)
|
||||
ids += local_bookmarked.pluck(:id)
|
||||
ids += local_emoji_reacted.pluck(:id)
|
||||
ids += local_referenced.pluck(:id)
|
||||
ids += preloadable_poll.local_voters.pluck(:id) if preloadable_poll.present?
|
||||
ids += mentioned_by
|
||||
ids += favourited_by
|
||||
ids += reblogged_by
|
||||
ids += bookmarked_by
|
||||
ids += emoji_reacted_by
|
||||
ids += referenced_by
|
||||
ids += voted_by if preloadable_poll.present?
|
||||
|
||||
ids.uniq
|
||||
end
|
||||
end
|
||||
|
||||
def mentioned_by
|
||||
@mentioned_by ||= local_mentioned.pluck(:id)
|
||||
end
|
||||
|
||||
def favourited_by
|
||||
@favourited_by ||= local_favorited.pluck(:id)
|
||||
end
|
||||
|
||||
def reblogged_by
|
||||
@reblogged_by ||= local_reblogged.pluck(:id)
|
||||
end
|
||||
|
||||
def bookmarked_by
|
||||
@bookmarked_by ||= local_bookmarked.pluck(:id)
|
||||
end
|
||||
|
||||
def emoji_reacted_by
|
||||
@emoji_reacted_by ||= local_emoji_reacted.pluck(:id)
|
||||
end
|
||||
|
||||
def referenced_by
|
||||
@referenced_by ||= local_referenced.pluck(:id)
|
||||
end
|
||||
|
||||
def voted_by
|
||||
return [] if preloadable_poll.blank?
|
||||
|
||||
@voted_by ||= preloadable_poll.local_voters.pluck(:id)
|
||||
end
|
||||
|
||||
def searchable_text
|
||||
[
|
||||
spoiler_text,
|
||||
|
|
|
@ -86,6 +86,7 @@ class Status < ApplicationRecord
|
|||
has_many :referenced_by_statuses, through: :referenced_by_status_objects, class_name: 'Status', source: :status
|
||||
has_many :capability_tokens, class_name: 'StatusCapabilityToken', inverse_of: :status, dependent: :destroy
|
||||
has_many :bookmark_category_relationships, class_name: 'BookmarkCategoryStatus', inverse_of: :status, dependent: :destroy
|
||||
has_many :bookmark_categories, class_name: 'BookmarkCategory', through: :bookmark_category_relationships, source: :bookmark_category
|
||||
has_many :joined_bookmark_categories, class_name: 'BookmarkCategory', through: :bookmark_category_relationships, source: :bookmark_category
|
||||
|
||||
# Those associations are used for the private search index
|
||||
|
@ -93,6 +94,7 @@ class Status < ApplicationRecord
|
|||
has_many :local_favorited, -> { merge(Account.local) }, through: :favourites, source: :account
|
||||
has_many :local_reblogged, -> { merge(Account.local) }, through: :reblogs, source: :account
|
||||
has_many :local_bookmarked, -> { merge(Account.local) }, through: :bookmarks, source: :account
|
||||
has_many :local_bookmark_categoried, -> { merge(Account.local) }, through: :bookmark_categories, source: :account
|
||||
has_many :local_emoji_reacted, -> { merge(Account.local) }, through: :emoji_reactions, source: :account
|
||||
has_many :local_referenced, -> { merge(Account.local) }, through: :referenced_by_statuses, source: :account
|
||||
|
||||
|
@ -435,6 +437,12 @@ class Status < ApplicationRecord
|
|||
compute_searchability
|
||||
end
|
||||
|
||||
def searchable_visibility
|
||||
return limited_scope if limited_visibility? && !none_limited?
|
||||
|
||||
visibility
|
||||
end
|
||||
|
||||
class << self
|
||||
def selectable_visibilities
|
||||
visibilities.keys - %w(direct limited)
|
||||
|
|
|
@ -16,8 +16,11 @@ class StatusesSearchService < BaseService
|
|||
private
|
||||
|
||||
def status_search_results
|
||||
request = parsed_query.request
|
||||
results = request.collapse(field: :id).order(id: { order: :desc }).limit(@limit).offset(@offset).objects.compact
|
||||
query = parsed_query
|
||||
request = query.request
|
||||
return [] unless query.valid
|
||||
|
||||
results = request.collapse(field: :id).order(id: { order: query.order_by }).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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue