lsolr 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/lsolr.rb +213 -0
  3. metadata +45 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d2af9d86ec14e9bf006d10b5af744e1c87a52f4d
4
+ data.tar.gz: 3891da477cf63a26da48b66f728687ffa99743a6
5
+ SHA512:
6
+ metadata.gz: 46592b63cc533c78c7c66d23c1dc82b087e9ab0d14a44cc4b17947a454ee8ff6f5f0a722bd4bad4916a1478fa90a2b6f2f3db2d186c4ea173535402db0532578
7
+ data.tar.gz: 194b24916051035de0c74a92d7b19db852630416e865b821ca3f607a633652938b4a39e7d5abb752e2ae11ab40bdaa4470617046dfd973a91570cb1b111c6d04
@@ -0,0 +1,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @example How to use.
4
+ # monoclinic = LSolr.new(:crystal_system).match(:monoclinic)
5
+ # cubic = LSolr.new(:crystal_system).match(:cubic)
6
+ # soft = LSolr.new(:mohs_scale).greater_than_or_equal_to('*').less_than(5.0)
7
+ # hard = LSolr.new(:mohs_scale).greater_than_or_equal_to(5.0).less_than_or_equal_to(10.0)
8
+ #
9
+ # phosphophyllite = monoclinic.and(soft).wrap
10
+ # diamond = cubic.and(hard).wrap
11
+ #
12
+ # phosphophyllite.or(diamond).to_s
13
+ # #=> '(crystal_system:monoclinic AND mohs_scale:[* TO 5.0}) OR (crystal_system:cubic AND mohs_scale:[5.0 TO 10.0])'
14
+ class LSolr
15
+ NOT = 'NOT'
16
+ AND = 'AND'
17
+ OR = 'OR'
18
+ TO = 'TO'
19
+
20
+ GREATER_THAN = '{'
21
+ LESS_THAN = '}'
22
+ GREATER_THAN_OR_EQUAL_TO = '['
23
+ LESS_THAN_OR_EQUAL_TO = ']'
24
+
25
+ PROXIMITY = '~'
26
+ BOOST = '^'
27
+ FUZZY_MATCH_DISTANCE_RANGE = (0.0..1.0).freeze
28
+
29
+ PARENTHESIS_LEFT = '('
30
+ PARENTHESIS_RIGHT = ')'
31
+
32
+ RESERVED_SYMBOLS = %w(- + & | ! ( ) { } [ ] ^ " ~ * ? : \\\\ /).freeze
33
+ RESERVED_WORDS = /(AND|OR|NOT)/
34
+ REPLACEMENT_CHAR = ' '
35
+
36
+ attr_accessor :prev, :operator, :left_parentheses, :right_parentheses
37
+
38
+ # @param field [String] field name
39
+ # @return [LSolr] a instance
40
+ def initialize(field)
41
+ raise ArgumentError, 'Please specify a field name.' if field.nil? || field.empty?
42
+
43
+ @not = ''
44
+ @field = field
45
+ @value = ''
46
+ @range_first = ''
47
+ @range_last = ''
48
+ @boost = ''
49
+ @left_parentheses = []
50
+ @right_parentheses = []
51
+ end
52
+
53
+ # @return [String] a stringigied query
54
+ def to_s
55
+ @value = "#{@range_first} #{TO} #{@range_last}" if range_search?
56
+ raise 'Please specify a search condition.' if blank?
57
+
58
+ expr = "#{left_parentheses.join}#{@not}#{@field}:#{@value}#{right_parentheses.join}"
59
+ expr = "#{prev} #{operator} #{expr}" if prev&.present?
60
+ "#{expr}#{@boost}"
61
+ end
62
+
63
+ # @return [true] unless search condition specified
64
+ # @return [false] if search condition specified
65
+ def blank?
66
+ @value.empty? && (@range_first.empty? || @range_last.empty?)
67
+ end
68
+
69
+ # @return [true] if search condition specified
70
+ # @return [false] unless search condition specified
71
+ def present?
72
+ !blank?
73
+ end
74
+
75
+ # @return [LSolr] copied self instance
76
+ def wrap
77
+ this = dup
78
+ take_head(this).left_parentheses << PARENTHESIS_LEFT
79
+ this.right_parentheses << PARENTHESIS_RIGHT
80
+ this
81
+ end
82
+
83
+ # @return [LSolr] self instance
84
+ def not
85
+ @not = "#{NOT} "
86
+ self
87
+ end
88
+
89
+ # @param weight [Float] boost weight
90
+ # @return [LSolr] self instance
91
+ def boost(weight)
92
+ @boost = "#{BOOST}#{weight}"
93
+ self
94
+ end
95
+
96
+ # @param value [String, Integer, true, false] search word or filter value
97
+ # @return [LSolr] self instance
98
+ def match(value)
99
+ values = clean(value).split
100
+ if values.size > 1
101
+ phrase_match(values)
102
+ else
103
+ @value = values.join('')
104
+ self
105
+ end
106
+ end
107
+
108
+ # @param value [String] filter value
109
+ # @return [LSolr] self instance
110
+ def date_time_match(value)
111
+ @value = clean(value, symbols: RESERVED_SYMBOLS - %w[- : . / +])
112
+ self
113
+ end
114
+
115
+ # @param value [String] search word
116
+ # @return [LSolr] self instance
117
+ def prefix_match(value)
118
+ @value = clean(value, symbols: RESERVED_SYMBOLS - %w[* ?])
119
+ self
120
+ end
121
+
122
+ # @param values [Array<String>] search word
123
+ # @param distance [Integer] proximity distance
124
+ # @return [LSolr] self instance
125
+ def phrase_match(values, distance: 0)
126
+ value = values.map { |v| clean(v) }.join(REPLACEMENT_CHAR)
127
+ proximity_match = distance.positive? ? "#{PROXIMITY}#{distance}" : ''
128
+ @value = %("#{value}"#{proximity_match})
129
+ self
130
+ end
131
+
132
+ # @param value [String] search word
133
+ # @param distance [Float] proximity distance
134
+ # @return [LSolr] self instance
135
+ def fuzzy_match(value, distance: 0.0)
136
+ raise RangeError, "Out of #{FUZZY_MATCH_DISTANCE_RANGE}. #{distance} given." unless (FUZZY_MATCH_DISTANCE_RANGE).member?(distance)
137
+ @value = "#{clean(value)}#{PROXIMITY}#{distance}"
138
+ self
139
+ end
140
+
141
+ # @param value [String, Integer] filter value
142
+ # @return [LSolr] self instance
143
+ def greater_than(value)
144
+ @range_first = "#{GREATER_THAN}#{value}"
145
+ self
146
+ end
147
+
148
+ # @param value [String, Integer] filter value
149
+ # @return [LSolr] self instance
150
+ def less_than(value)
151
+ @range_last = "#{value}#{LESS_THAN}"
152
+ self
153
+ end
154
+
155
+ # @param value [String, Integer] filter value
156
+ # @return [LSolr] self instance
157
+ def greater_than_or_equal_to(value)
158
+ @range_first = "#{GREATER_THAN_OR_EQUAL_TO}#{value}"
159
+ self
160
+ end
161
+
162
+ # @param value [String, Integer] filter value
163
+ # @return [LSolr] self instance
164
+ def less_than_or_equal_to(value)
165
+ @range_last = "#{value}#{LESS_THAN_OR_EQUAL_TO}"
166
+ self
167
+ end
168
+
169
+ # @param another [LSolr] another instance
170
+ # @return [LSolr] copied another instance
171
+ def and(another)
172
+ link(another, AND)
173
+ end
174
+
175
+ # @param another [LSolr] another instance
176
+ # @return [LSolr] copied another instance
177
+ def or(another)
178
+ link(another, OR)
179
+ end
180
+
181
+ private
182
+
183
+ def initialize_copy(obj)
184
+ obj.prev = obj.prev.dup if obj.prev&.present?
185
+ obj.left_parentheses = obj.left_parentheses.dup
186
+ obj.right_parentheses = obj.right_parentheses.dup
187
+ end
188
+
189
+ def range_search?
190
+ @value.empty? && !@range_first.empty? && !@range_last.empty?
191
+ end
192
+
193
+ def clean(value, symbols: RESERVED_SYMBOLS)
194
+ value.to_s
195
+ .tr(symbols.join(''), REPLACEMENT_CHAR)
196
+ .gsub(RESERVED_WORDS) { |match| "\\#{match}" }
197
+ end
198
+
199
+ def link(another, operator)
200
+ return self if another.nil? || another.blank?
201
+
202
+ another = another.dup
203
+ head = take_head(another)
204
+ head.prev = dup
205
+ head.operator = operator
206
+ another
207
+ end
208
+
209
+ def take_head(element)
210
+ while element.prev&.present? do element = element.prev end
211
+ element
212
+ end
213
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lsolr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Taishi Kasuga
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: LSolr is a query builder of Apache Solr standard Lucene type query for
14
+ Ruby.
15
+ email: supercaracal@yahoo.co.jp
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/lsolr.rb
21
+ homepage: https://github.com/supercaracal/lsolr
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.2.2
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 2.6.14
42
+ signing_key:
43
+ specification_version: 4
44
+ summary: A query builder of Apache Solr for Ruby
45
+ test_files: []