Merge branch 'main' into glitch-soc/merge-upstream
Conflicts: - `spec/models/status_spec.rb`: Upstream added tests too close to glitch-soc-specific tests. Kept both tests.
This commit is contained in:
		@@ -429,6 +429,9 @@ class Account < ApplicationRecord
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  class << self
 | 
			
		||||
    DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/.freeze
 | 
			
		||||
    TEXTSEARCH = "(setweight(to_tsvector('simple', accounts.display_name), 'A') || setweight(to_tsvector('simple', accounts.username), 'B') || setweight(to_tsvector('simple', coalesce(accounts.domain, '')), 'C'))"
 | 
			
		||||
 | 
			
		||||
    def readonly_attributes
 | 
			
		||||
      super - %w(statuses_count following_count followers_count)
 | 
			
		||||
    end
 | 
			
		||||
@@ -439,70 +442,29 @@ class Account < ApplicationRecord
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def search_for(terms, limit = 10, offset = 0)
 | 
			
		||||
      textsearch, query = generate_query_for_search(terms)
 | 
			
		||||
      tsquery = generate_query_for_search(terms)
 | 
			
		||||
 | 
			
		||||
      sql = <<-SQL.squish
 | 
			
		||||
        SELECT
 | 
			
		||||
          accounts.*,
 | 
			
		||||
          ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
 | 
			
		||||
          ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank
 | 
			
		||||
        FROM accounts
 | 
			
		||||
        WHERE #{query} @@ #{textsearch}
 | 
			
		||||
        WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH}
 | 
			
		||||
          AND accounts.suspended_at IS NULL
 | 
			
		||||
          AND accounts.moved_to_account_id IS NULL
 | 
			
		||||
        ORDER BY rank DESC
 | 
			
		||||
        LIMIT ? OFFSET ?
 | 
			
		||||
        LIMIT :limit OFFSET :offset
 | 
			
		||||
      SQL
 | 
			
		||||
 | 
			
		||||
      records = find_by_sql([sql, limit, offset])
 | 
			
		||||
      records = find_by_sql([sql, limit: limit, offset: offset, tsquery: tsquery])
 | 
			
		||||
      ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
 | 
			
		||||
      records
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def advanced_search_for(terms, account, limit = 10, following = false, offset = 0)
 | 
			
		||||
      textsearch, query = generate_query_for_search(terms)
 | 
			
		||||
 | 
			
		||||
      if following
 | 
			
		||||
        sql = <<-SQL.squish
 | 
			
		||||
          WITH first_degree AS (
 | 
			
		||||
            SELECT target_account_id
 | 
			
		||||
            FROM follows
 | 
			
		||||
            WHERE account_id = ?
 | 
			
		||||
            UNION ALL
 | 
			
		||||
            SELECT ?
 | 
			
		||||
          )
 | 
			
		||||
          SELECT
 | 
			
		||||
            accounts.*,
 | 
			
		||||
            (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
 | 
			
		||||
          FROM accounts
 | 
			
		||||
          LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?)
 | 
			
		||||
          WHERE accounts.id IN (SELECT * FROM first_degree)
 | 
			
		||||
            AND #{query} @@ #{textsearch}
 | 
			
		||||
            AND accounts.suspended_at IS NULL
 | 
			
		||||
            AND accounts.moved_to_account_id IS NULL
 | 
			
		||||
          GROUP BY accounts.id
 | 
			
		||||
          ORDER BY rank DESC
 | 
			
		||||
          LIMIT ? OFFSET ?
 | 
			
		||||
        SQL
 | 
			
		||||
 | 
			
		||||
        records = find_by_sql([sql, account.id, account.id, account.id, limit, offset])
 | 
			
		||||
      else
 | 
			
		||||
        sql = <<-SQL.squish
 | 
			
		||||
          SELECT
 | 
			
		||||
            accounts.*,
 | 
			
		||||
            (count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
 | 
			
		||||
          FROM accounts
 | 
			
		||||
          LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
 | 
			
		||||
          WHERE #{query} @@ #{textsearch}
 | 
			
		||||
            AND accounts.suspended_at IS NULL
 | 
			
		||||
            AND accounts.moved_to_account_id IS NULL
 | 
			
		||||
          GROUP BY accounts.id
 | 
			
		||||
          ORDER BY rank DESC
 | 
			
		||||
          LIMIT ? OFFSET ?
 | 
			
		||||
        SQL
 | 
			
		||||
 | 
			
		||||
        records = find_by_sql([sql, account.id, account.id, limit, offset])
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      tsquery = generate_query_for_search(terms)
 | 
			
		||||
      sql = advanced_search_for_sql_template(following)
 | 
			
		||||
      records = find_by_sql([sql, id: account.id, limit: limit, offset: offset, tsquery: tsquery])
 | 
			
		||||
      ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
 | 
			
		||||
      records
 | 
			
		||||
    end
 | 
			
		||||
@@ -524,12 +486,55 @@ class Account < ApplicationRecord
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def generate_query_for_search(terms)
 | 
			
		||||
      terms      = Arel.sql(connection.quote(terms.gsub(/['?\\:]/, ' ')))
 | 
			
		||||
      textsearch = "(setweight(to_tsvector('simple', accounts.display_name), 'A') || setweight(to_tsvector('simple', accounts.username), 'B') || setweight(to_tsvector('simple', coalesce(accounts.domain, '')), 'C'))"
 | 
			
		||||
      query      = "to_tsquery('simple', ''' ' || #{terms} || ' ''' || ':*')"
 | 
			
		||||
    def generate_query_for_search(unsanitized_terms)
 | 
			
		||||
      terms = unsanitized_terms.gsub(DISALLOWED_TSQUERY_CHARACTERS, ' ')
 | 
			
		||||
 | 
			
		||||
      [textsearch, query]
 | 
			
		||||
      # The final ":*" is for prefix search.
 | 
			
		||||
      # The trailing space does not seem to fit any purpose, but `to_tsquery`
 | 
			
		||||
      # behaves differently with and without a leading space if the terms start
 | 
			
		||||
      # with `./`, `../`, or `.. `. I don't understand why, so, in doubt, keep
 | 
			
		||||
      # the same query.
 | 
			
		||||
      "' #{terms} ':*"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def advanced_search_for_sql_template(following)
 | 
			
		||||
      if following
 | 
			
		||||
        <<-SQL.squish
 | 
			
		||||
          WITH first_degree AS (
 | 
			
		||||
            SELECT target_account_id
 | 
			
		||||
            FROM follows
 | 
			
		||||
            WHERE account_id = :id
 | 
			
		||||
            UNION ALL
 | 
			
		||||
            SELECT :id
 | 
			
		||||
          )
 | 
			
		||||
          SELECT
 | 
			
		||||
            accounts.*,
 | 
			
		||||
            (count(f.id) + 1) * ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank
 | 
			
		||||
          FROM accounts
 | 
			
		||||
          LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = :id)
 | 
			
		||||
          WHERE accounts.id IN (SELECT * FROM first_degree)
 | 
			
		||||
            AND to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH}
 | 
			
		||||
            AND accounts.suspended_at IS NULL
 | 
			
		||||
            AND accounts.moved_to_account_id IS NULL
 | 
			
		||||
          GROUP BY accounts.id
 | 
			
		||||
          ORDER BY rank DESC
 | 
			
		||||
          LIMIT :limit OFFSET :offset
 | 
			
		||||
        SQL
 | 
			
		||||
      else
 | 
			
		||||
        <<-SQL.squish
 | 
			
		||||
          SELECT
 | 
			
		||||
            accounts.*,
 | 
			
		||||
            (count(f.id) + 1) * ts_rank_cd(#{TEXTSEARCH}, to_tsquery('simple', :tsquery), 32) AS rank
 | 
			
		||||
          FROM accounts
 | 
			
		||||
          LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = :id) OR (accounts.id = f.target_account_id AND f.account_id = :id)
 | 
			
		||||
          WHERE to_tsquery('simple', :tsquery) @@ #{TEXTSEARCH}
 | 
			
		||||
            AND accounts.suspended_at IS NULL
 | 
			
		||||
            AND accounts.moved_to_account_id IS NULL
 | 
			
		||||
          GROUP BY accounts.id
 | 
			
		||||
          ORDER BY rank DESC
 | 
			
		||||
          LIMIT :limit OFFSET :offset
 | 
			
		||||
        SQL
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user