redi_search 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +516 -112
- data/lib/redi_search.rb +5 -2
- data/lib/redi_search/add.rb +70 -0
- data/lib/redi_search/alter.rb +30 -0
- data/lib/redi_search/create.rb +53 -0
- data/lib/redi_search/document.rb +71 -16
- data/lib/redi_search/index.rb +31 -26
- data/lib/redi_search/lazily_load.rb +65 -0
- data/lib/redi_search/log_subscriber.rb +4 -0
- data/lib/redi_search/model.rb +41 -18
- data/lib/redi_search/schema.rb +17 -8
- data/lib/redi_search/schema/text_field.rb +0 -2
- data/lib/redi_search/search.rb +22 -44
- data/lib/redi_search/search/clauses.rb +60 -31
- data/lib/redi_search/search/clauses/and.rb +17 -0
- data/lib/redi_search/search/clauses/application_clause.rb +18 -0
- data/lib/redi_search/search/clauses/boolean.rb +72 -0
- data/lib/redi_search/search/clauses/highlight.rb +47 -0
- data/lib/redi_search/search/clauses/in_order.rb +17 -0
- data/lib/redi_search/search/clauses/language.rb +23 -0
- data/lib/redi_search/search/clauses/limit.rb +27 -0
- data/lib/redi_search/search/clauses/no_content.rb +17 -0
- data/lib/redi_search/search/clauses/no_stop_words.rb +17 -0
- data/lib/redi_search/search/clauses/or.rb +23 -0
- data/lib/redi_search/search/clauses/return.rb +23 -0
- data/lib/redi_search/search/clauses/slop.rb +23 -0
- data/lib/redi_search/search/clauses/sort_by.rb +25 -0
- data/lib/redi_search/search/clauses/verbatim.rb +17 -0
- data/lib/redi_search/search/clauses/where.rb +66 -0
- data/lib/redi_search/search/clauses/with_scores.rb +17 -0
- data/lib/redi_search/search/result.rb +46 -0
- data/lib/redi_search/search/term.rb +4 -4
- data/lib/redi_search/spellcheck.rb +30 -29
- data/lib/redi_search/spellcheck/result.rb +44 -0
- data/lib/redi_search/version.rb +1 -1
- metadata +101 -31
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -1757
- data/.travis.yml +0 -23
- data/Gemfile +0 -17
- data/Rakefile +0 -12
- data/bin/console +0 -8
- data/bin/publish +0 -58
- data/bin/setup +0 -8
- data/bin/test +0 -7
- data/lib/redi_search/document/converter.rb +0 -26
- data/lib/redi_search/error.rb +0 -6
- data/lib/redi_search/result/collection.rb +0 -22
- data/lib/redi_search/search/and_clause.rb +0 -15
- data/lib/redi_search/search/boolean_clause.rb +0 -72
- data/lib/redi_search/search/highlight_clause.rb +0 -43
- data/lib/redi_search/search/or_clause.rb +0 -21
- data/lib/redi_search/search/where_clause.rb +0 -66
- data/redi_search.gemspec +0 -48
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RediSearch
|
4
|
+
class Search
|
5
|
+
module Clauses
|
6
|
+
class Boolean
|
7
|
+
def initialize(search, term, prior_clause = nil, **term_options)
|
8
|
+
@search = search
|
9
|
+
@prior_clause = prior_clause
|
10
|
+
@not = false
|
11
|
+
|
12
|
+
initialize_term(term, **term_options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
raise ArgumentError, "missing query terms" if term.blank?
|
17
|
+
|
18
|
+
[
|
19
|
+
prior_clause.presence,
|
20
|
+
queryify_term.dup.prepend(not_operator)
|
21
|
+
].compact.join(operand)
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :inspect, to: :to_s
|
25
|
+
|
26
|
+
def not(term, **term_options)
|
27
|
+
@not = true
|
28
|
+
|
29
|
+
initialize_term(term, **term_options)
|
30
|
+
|
31
|
+
search
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :prior_clause, :term, :search
|
37
|
+
|
38
|
+
def operand
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
def not_operator
|
43
|
+
return "" unless @not
|
44
|
+
|
45
|
+
"-"
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize_term(term, **term_options)
|
49
|
+
return if term.blank?
|
50
|
+
|
51
|
+
@term =
|
52
|
+
if term.is_a? RediSearch::Search
|
53
|
+
term
|
54
|
+
else
|
55
|
+
Term.new(term, term_options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def queryify_term
|
60
|
+
if term.is_a?(RediSearch::Search) &&
|
61
|
+
!term.term_clause.is_a?(RediSearch::Search::Clauses::Where)
|
62
|
+
"(#{term.term_clause})"
|
63
|
+
elsif term.is_a?(RediSearch::Search)
|
64
|
+
term.term_clause
|
65
|
+
else
|
66
|
+
term
|
67
|
+
end.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RediSearch
|
4
|
+
class Search
|
5
|
+
module Clauses
|
6
|
+
class Highlight
|
7
|
+
def initialize(fields: [], opening_tag: "<b>", closing_tag: "</b>")
|
8
|
+
@fields = fields
|
9
|
+
@opening_tag = opening_tag
|
10
|
+
@closing_tag = closing_tag
|
11
|
+
end
|
12
|
+
|
13
|
+
def clause
|
14
|
+
[
|
15
|
+
"HIGHLIGHT",
|
16
|
+
fields_clause,
|
17
|
+
tags_clause,
|
18
|
+
].compact.flatten(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :fields, :opening_tag, :closing_tag
|
24
|
+
|
25
|
+
def tags_clause
|
26
|
+
return if opening_tag.blank? && closing_tag.blank?
|
27
|
+
|
28
|
+
if opening_tag.present? && closing_tag.present?
|
29
|
+
["TAGS", opening_tag, closing_tag]
|
30
|
+
else
|
31
|
+
arg_error("Missing opening or closing tag")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def fields_clause
|
36
|
+
return if fields.empty?
|
37
|
+
|
38
|
+
["FIELDS", fields.size, fields]
|
39
|
+
end
|
40
|
+
|
41
|
+
def arg_error(msg)
|
42
|
+
raise ArgumentError, "Highlight: #{msg}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class InOrder < ApplicationClause
|
9
|
+
def clause
|
10
|
+
validate!
|
11
|
+
|
12
|
+
["INORDER"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Language < ApplicationClause
|
9
|
+
clause_term :language, presence: true
|
10
|
+
|
11
|
+
def initialize(language:)
|
12
|
+
@language = language
|
13
|
+
end
|
14
|
+
|
15
|
+
def clause
|
16
|
+
validate!
|
17
|
+
|
18
|
+
["LANGUAGE", language]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Limit < ApplicationClause
|
9
|
+
clause_term :total, presence: true,
|
10
|
+
numericality: { greater_than_or_equal_to: 0 }
|
11
|
+
clause_term :offset, presence: true,
|
12
|
+
numericality: { greater_than_or_equal_to: 0 }
|
13
|
+
|
14
|
+
def initialize(total:, offset: 0)
|
15
|
+
@total = total
|
16
|
+
@offset = offset
|
17
|
+
end
|
18
|
+
|
19
|
+
def clause
|
20
|
+
validate!
|
21
|
+
|
22
|
+
["LIMIT", [offset, total]]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class NoContent < ApplicationClause
|
9
|
+
def clause
|
10
|
+
validate!
|
11
|
+
|
12
|
+
["NOCONTENT"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class NoStopWords < ApplicationClause
|
9
|
+
def clause
|
10
|
+
validate!
|
11
|
+
|
12
|
+
["NOSTOPWORDS"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/boolean"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Or < Boolean
|
9
|
+
def where(**condition)
|
10
|
+
@term = search.dup.where(condition)
|
11
|
+
|
12
|
+
search
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def operand
|
18
|
+
"|"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Return < ApplicationClause
|
9
|
+
clause_term :fields, presence: true
|
10
|
+
|
11
|
+
def initialize(fields:)
|
12
|
+
@fields = fields
|
13
|
+
end
|
14
|
+
|
15
|
+
def clause
|
16
|
+
validate!
|
17
|
+
|
18
|
+
["RETURN", fields.size, fields]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Slop < ApplicationClause
|
9
|
+
clause_term :slop, numericality: { greater_than_or_equal_to: 0 }
|
10
|
+
|
11
|
+
def initialize(slop:)
|
12
|
+
@slop = slop
|
13
|
+
end
|
14
|
+
|
15
|
+
def clause
|
16
|
+
validate!
|
17
|
+
|
18
|
+
["SLOP", slop]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class SortBy < ApplicationClause
|
9
|
+
clause_term :field, presence: true
|
10
|
+
clause_term :order, presence: true, inclusion: { in: %i(asc desc) }
|
11
|
+
|
12
|
+
def initialize(field:, order: :asc)
|
13
|
+
@field = field
|
14
|
+
@order = order.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def clause
|
18
|
+
validate!
|
19
|
+
|
20
|
+
["SORTBY", field, order]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class Verbatim < ApplicationClause
|
9
|
+
def clause
|
10
|
+
validate!
|
11
|
+
|
12
|
+
["VERBATIM"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RediSearch
|
4
|
+
class Search
|
5
|
+
module Clauses
|
6
|
+
class Where
|
7
|
+
def initialize(search, condition, prior_clause = nil)
|
8
|
+
@search = search
|
9
|
+
@prior_clause = prior_clause
|
10
|
+
@not = false
|
11
|
+
|
12
|
+
initialize_term(condition)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
[
|
17
|
+
prior_clause.presence,
|
18
|
+
"(#{not_operator}@#{field}:#{queryify_term})"
|
19
|
+
].compact.join(" ")
|
20
|
+
end
|
21
|
+
|
22
|
+
delegate :inspect, to: :to_s
|
23
|
+
|
24
|
+
def not(condition)
|
25
|
+
@not = true
|
26
|
+
|
27
|
+
initialize_term(condition)
|
28
|
+
|
29
|
+
search
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :prior_clause, :term, :field, :search
|
35
|
+
|
36
|
+
def queryify_term
|
37
|
+
if term.is_a? RediSearch::Search
|
38
|
+
"(#{term.term_clause})"
|
39
|
+
else
|
40
|
+
term.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def not_operator
|
45
|
+
return "" unless @not
|
46
|
+
|
47
|
+
"-"
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize_term(condition)
|
51
|
+
return if condition.blank?
|
52
|
+
|
53
|
+
condition, *options = condition.to_a
|
54
|
+
|
55
|
+
@field = condition[0]
|
56
|
+
@term =
|
57
|
+
if condition[1].is_a? RediSearch::Search
|
58
|
+
condition[1]
|
59
|
+
else
|
60
|
+
Term.new(condition[1], **options.to_h)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "redi_search/search/clauses/application_clause"
|
4
|
+
|
5
|
+
module RediSearch
|
6
|
+
class Search
|
7
|
+
module Clauses
|
8
|
+
class WithScores < ApplicationClause
|
9
|
+
def clause
|
10
|
+
validate!
|
11
|
+
|
12
|
+
["WITHSCORES"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RediSearch
|
4
|
+
class Search
|
5
|
+
class Result < Array
|
6
|
+
def initialize(index, used_clauses, count, documents)
|
7
|
+
@count = count
|
8
|
+
@used_clauses = used_clauses
|
9
|
+
|
10
|
+
super(documents.each_slice(response_slice).map do |slice|
|
11
|
+
document_id = slice[0]
|
12
|
+
fields = slice.last unless no_content?
|
13
|
+
score = slice[1].to_f if with_scores?
|
14
|
+
|
15
|
+
Document.new(index, document_id, Hash[*fields.to_a], score)
|
16
|
+
end)
|
17
|
+
end
|
18
|
+
|
19
|
+
def count
|
20
|
+
@count || super
|
21
|
+
end
|
22
|
+
|
23
|
+
def size
|
24
|
+
@count || super
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def response_slice
|
30
|
+
slice_length = 2
|
31
|
+
slice_length -= 1 if no_content?
|
32
|
+
slice_length += 1 if with_scores?
|
33
|
+
|
34
|
+
slice_length
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_scores?
|
38
|
+
@used_clauses.include? "with_scores"
|
39
|
+
end
|
40
|
+
|
41
|
+
def no_content?
|
42
|
+
@used_clauses.include? "no_content"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|