Don't forget to lycansubscribe

allow searching in either likes or reposts

Changed files
+67 -22
app
+3
app/models/like.rb
··· 1 1 require 'active_record' 2 2 3 3 require_relative 'post' 4 + require_relative 'searchable' 4 5 require_relative 'user' 5 6 6 7 class Like < ActiveRecord::Base 8 + include Searchable 9 + 7 10 validates_presence_of :time, :rkey 8 11 validates_length_of :rkey, maximum: 13 9 12
+3
app/models/repost.rb
··· 1 1 require 'active_record' 2 2 3 3 require_relative 'post' 4 + require_relative 'searchable' 4 5 require_relative 'user' 5 6 6 7 class Repost < ActiveRecord::Base 8 + include Searchable 9 + 7 10 validates_presence_of :time, :rkey 8 11 validates_length_of :rkey, maximum: 13 9 12
+37
app/models/searchable.rb
··· 1 + require 'active_support/concern' 2 + 3 + module Searchable 4 + extend ActiveSupport::Concern 5 + 6 + included do 7 + scope :reverse_chronologically, -> { order(time: :desc, id: :desc) } 8 + 9 + scope :after_cursor, ->(cursor) { 10 + return self if cursor.nil? 11 + 12 + t = arel_table 13 + 14 + timestamp, id = cursor.split(':') 15 + time = Time.at(timestamp.to_f) 16 + 17 + where( 18 + t[:time].lt(time) 19 + .or( 20 + t[:time].eq(time) 21 + .and(t[:id].lt(id)) 22 + ) 23 + ) 24 + } 25 + 26 + scope :matching_terms, ->(terms) { 27 + where( 28 + (["(posts.text ~* ?)"] * (terms.length)).join(" AND "), 29 + *(terms.map { |x| "\\y#{x}\\y" }) 30 + ) 31 + } 32 + 33 + def cursor 34 + "#{self.time.to_f}:#{self.id}" 35 + end 36 + end 37 + end
+24 -22
app/server.rb
··· 100 100 return json_error('UserNotFound', 'Missing authentication header') if user.nil? 101 101 end 102 102 103 - if params[:query] 104 - query = params[:query].gsub('%', "\\%") 105 - words = query.strip.split(/ +/) 103 + if params[:query].to_s.strip.empty? 104 + return json_error('MissingParameter', 'Missing "query" parameter') 105 + end 106 106 107 - likes = user.likes 108 - .joins(:post) 109 - .includes(:post => :user) 110 - .where( 111 - (["(text ~* ?)"] * (words.length)).join(" AND "), 112 - *(words.map { |x| "\\y#{x}\\y" }) 113 - ) 114 - .order('likes.time DESC, likes.id DESC') 115 - .limit(PAGE_LIMIT) 116 - 117 - if params[:cursor] 118 - timestamp, id = params[:cursor].split(':') 119 - time = Time.at(timestamp.to_f) 120 - likes = likes.where("likes.time < ? OR (likes.time = ? AND likes.id < ?)", time, time, id) 121 - end 107 + if params[:collection].to_s.strip.empty? 108 + return json_error('MissingParameter', 'Missing "collection" parameter') 109 + end 122 110 123 - post_uris = likes.map(&:post).map(&:at_uri) 111 + query = params[:query].gsub('%', "\\%") 112 + words = query.strip.split(/ +/) 124 113 125 - json_response(posts: post_uris, cursor: likes.last && "#{likes.last.time.to_f}:#{likes.last.id}") 126 - else 127 - json_error('MissingParameter', 'Missing "query" parameter') 114 + collection = case params[:collection] 115 + when 'likes' then user.likes 116 + when 'reposts' then user.reposts 117 + else return json_error('InvalidParameter', 'Invalid search collection') 128 118 end 119 + 120 + items = collection 121 + .joins(:post) 122 + .includes(:post => :user) 123 + .matching_terms(words) 124 + .reverse_chronologically 125 + .after_cursor(params[:cursor]) 126 + .limit(PAGE_LIMIT) 127 + 128 + post_uris = items.map(&:post).map(&:at_uri) 129 + 130 + json_response(posts: post_uris, cursor: items.last&.cursor) 129 131 end 130 132 131 133 get '/.well-known/did.json' do