+11
app/models/searchable.rb
+11
app/models/searchable.rb
···
24
24
}
25
25
26
26
scope :matching_terms, ->(terms) {
27
+
return self if terms.empty?
28
+
27
29
where(
28
30
(["(posts.text ~* ?)"] * (terms.length)).join(" AND "),
31
+
*(terms.map { |x| "\\y" + x.gsub(/\s+/, "\\s+") + "\\y" })
32
+
)
33
+
}
34
+
35
+
scope :excluding_terms, ->(terms) {
36
+
return self if terms.empty?
37
+
38
+
where(
39
+
(["(posts.text !~* ?)"] * (terms.length)).join(" AND "),
29
40
*(terms.map { |x| "\\y" + x.gsub(/\s+/, "\\s+") + "\\y" })
30
41
)
31
42
}
+20
-6
app/query_parser.rb
+20
-6
app/query_parser.rb
···
1
1
class QueryParser
2
-
attr_reader :terms
2
+
attr_reader :terms, :exclusions
3
3
4
4
def initialize(query)
5
5
@terms = []
6
-
query = query.strip
6
+
@exclusions = []
7
+
8
+
query = query.clone
7
9
8
-
while match = query.match(/".+?"/)
10
+
while match = query.match(/\-?".+?"/)
9
11
range = match.begin(0)...match.end(0)
10
-
phrase = query[range][1..-2].gsub(/[\W_]+/, ' ').strip
12
+
phrase = query[range]
11
13
query[range] = ' '
12
-
@terms << phrase
14
+
15
+
negative = phrase.start_with?('-')
16
+
phrase = phrase.gsub(/^\-/, '').gsub(/^"|"$/, '').gsub(/[^\w\-]+/, ' ').strip
17
+
18
+
if negative
19
+
@exclusions << phrase
20
+
else
21
+
@terms << phrase
22
+
end
13
23
end
14
24
15
-
@terms += query.gsub(/[\W_]+/, ' ').strip.split(/ +/)
25
+
terms = query.gsub(/[^\w\-]+/, ' ').strip.split(/ +/)
26
+
negative, positive = terms.partition { |x| x.start_with?('-') }
27
+
28
+
@terms += positive
29
+
@exclusions += negative.map { |x| x[1..-1] }.reject(&:empty?)
16
30
end
17
31
end
+8
-3
app/server.rb
+8
-3
app/server.rb
···
55
55
return json_error('MissingParameter', 'Missing "collection" parameter')
56
56
end
57
57
58
-
terms = QueryParser.new(params[:query]).terms
58
+
query = QueryParser.new(params[:query])
59
59
60
60
collection = case params[:collection]
61
61
when 'likes' then user.likes
···
63
63
when 'quotes' then user.quotes
64
64
when 'reposts' then user.reposts
65
65
else return json_error('InvalidParameter', 'Invalid search collection')
66
+
end
67
+
68
+
if query.terms.empty?
69
+
return json_response(terms: [], posts: [], cursor: nil)
66
70
end
67
71
68
72
items = collection
69
73
.joins(:post)
70
74
.includes(:post => :user)
71
-
.matching_terms(terms)
75
+
.matching_terms(query.terms)
76
+
.excluding_terms(query.exclusions)
72
77
.reverse_chronologically
73
78
.after_cursor(params[:cursor])
74
79
.limit(PAGE_LIMIT)
···
80
85
items.map(&:post).map(&:at_uri)
81
86
end
82
87
83
-
json_response(terms: terms, posts: post_uris, cursor: items.last&.cursor)
88
+
json_response(terms: query.terms, posts: post_uris, cursor: items.last&.cursor)
84
89
end
85
90
86
91
get '/.well-known/did.json' do