command_search 0.8.1 → 0.8.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: da69ccede9f11407bf8cb7891a5171df25006638
4
- data.tar.gz: 578158cfcb0d5341704df96b665c9b0df5549f16
2
+ SHA256:
3
+ metadata.gz: 15ffe763615f2dbf119a8960fb91405376f2d2e30b3f43f594c6b3c7730ce96c
4
+ data.tar.gz: afe8f45ba3199a987b5ee2abbb7429fc13d89658d8dd0c48af74c19d3f4634ac
5
5
  SHA512:
6
- metadata.gz: 13894e8550b893d07a122b28c9e01df1b2bae2eaad69dd13202f9761e5fcc4f735265c86d03211981096f6cd1af313dc2150ca29bd54fb1df1158305e8136127
7
- data.tar.gz: eb74c17244825021a4b641e29ff97564bc7138f67b25c5fee7ef3ad0c117303b2c716d53595dea08fc11d80d328f848318439ffe34b74f6c4c5fdbaea0986ee7
6
+ metadata.gz: 3b10c60d4c3cca7daea2f1d14b3fc87afbdaf79922f1fc2f6fb4b33b97af8983c61ce0140e5ec1234749bd1b738fe6f34b9f6932db7f7f5ec7b95492ad863e3b
7
+ data.tar.gz: ad4acd1ceeafa32f0982a9c076724b3b933177653e997f2d8a5c9635f61d3f17980e47285c32b375a1ec0a28765dd5eaf00f01c5a160d8cd7e9cc1f1c3cc04f2
@@ -15,9 +15,13 @@ module CommandSearch
15
15
 
16
16
  def unnest_unaliased(node, aliases)
17
17
  type = node[:nest_type]
18
- values = node[:value].map { |x| x[:value].to_sym }
19
- return node if type == :colon && aliases[values.first]
20
- return node if type == :compare && (values & aliases.keys).any?
18
+ if type == :colon
19
+ val = node[:value][0][:value].to_sym
20
+ return node if aliases[val]
21
+ elsif type == :compare
22
+ return node if node[:value].any? { |child| aliases[child[:value].to_sym] }
23
+ end
24
+ values = node[:value].map { |x| x[:value] }
21
25
  str_values = values.join(node[:nest_op])
22
26
  { type: :str, value: str_values }
23
27
  end
@@ -5,28 +5,27 @@ module CommandSearch
5
5
  module_function
6
6
 
7
7
  def numeric_field?(field, command_types)
8
- raw_type = command_types[field.to_sym]
9
- if raw_type.is_a?(Array)
10
- type = (raw_type - [:allow_existence_boolean]).first
11
- else
12
- type = raw_type
8
+ type = command_types[field.to_sym]
9
+ if type.is_a?(Array)
10
+ type = (type - [:allow_existence_boolean]).first
13
11
  end
14
12
  [Numeric, Integer].include?(type)
15
13
  end
16
14
 
15
+ def build_str_regex(raw, type)
16
+ str = Regexp.escape(raw)
17
+ return /#{str}/i unless type == :quoted_str
18
+ return '' if raw == ''
19
+ return /\b#{str}\b/ unless raw[/(^\W)|(\W$)/]
20
+ border_a = '(^|\s|[^:+\w])'
21
+ border_b = '($|\s|[^:+\w])'
22
+ Regexp.new(border_a + str + border_b)
23
+ end
24
+
17
25
  def build_search(ast_node, fields, command_types)
18
26
  str = ast_node[:value] || ''
19
27
  fields = [fields] unless fields.is_a?(Array)
20
- if ast_node[:type] == :quoted_str
21
- regex = /\b#{Regexp.escape(str)}\b/
22
- if str[/(^\W)|(\W$)/]
23
- head_border = '(?<=^|[^:+\w])'
24
- tail_border = '(?=$|[^:+\w])'
25
- regex = Regexp.new(head_border + Regexp.escape(str) + tail_border)
26
- end
27
- else
28
- regex = /#{Regexp.escape(str)}/i
29
- end
28
+ regex = build_str_regex(str, ast_node[:type])
30
29
 
31
30
  forms = fields.map do |field|
32
31
  if numeric_field?(field, command_types)
@@ -39,12 +38,29 @@ module CommandSearch
39
38
  { '$or' => forms }
40
39
  end
41
40
 
42
- def is_bool_str?(str)
43
- str[/\Atrue\Z|\Afalse\Z/i]
41
+ def is_bool_str?(str, search_type)
42
+ search_type != :quoted_str && str[/\Atrue\Z|\Afalse\Z/i]
44
43
  end
45
44
 
46
45
  def make_boolean(str)
47
- str[/\Atrue\Z/i]
46
+ str[0] == 't'
47
+ end
48
+
49
+ def build_time_command(key, val)
50
+ time_str = val.tr('_.-', ' ')
51
+ if time_str == time_str.to_i.to_s
52
+ date_a = Time.new(time_str)
53
+ date_b = Time.new(time_str.to_i + 1).yesterday
54
+ else
55
+ date = Chronic.parse(time_str, guess: nil) || Chronic.parse(val, guess: nil)
56
+ return [{ CommandSeachDummyDate: true }, { CommandSeachDummyDate: false }] unless date
57
+ date_a = date.begin
58
+ date_b = date.end
59
+ end
60
+ [
61
+ { key => { '$gte' => date_a } },
62
+ { key => { '$lte' => date_b } }
63
+ ]
48
64
  end
49
65
 
50
66
  def build_command(ast_node, command_types)
@@ -57,11 +73,11 @@ module CommandSearch
57
73
  search_type = search_node[:type]
58
74
 
59
75
  if raw_type.is_a?(Array)
60
- is_bool = raw_type.include?(:allow_existence_boolean) && is_bool_str?(raw_val) && search_type != :quoted_str
61
76
  type = (raw_type - [:allow_existence_boolean]).first
77
+ is_bool = raw_type.include?(:allow_existence_boolean) && is_bool_str?(raw_val, search_type)
62
78
  else
63
- is_bool = false
64
79
  type = raw_type
80
+ is_bool = false
65
81
  end
66
82
 
67
83
  if type == Boolean
@@ -84,40 +100,11 @@ module CommandSearch
84
100
  val = { '$exists' => false }
85
101
  end
86
102
  elsif type == String
87
- if search_type == :quoted_str
88
- val = /\b#{Regexp.escape(raw_val)}\b/
89
- val = '' if raw_val == ''
90
- if raw_val[/(^\W)|(\W$)/]
91
- head_border = '(?<=^|[^:+\w])'
92
- tail_border = '(?=$|[^:+\w])'
93
- val = Regexp.new(head_border + Regexp.escape(raw_val) + tail_border)
94
- end
95
- else
96
- val = /#{Regexp.escape(raw_val)}/i
97
- end
103
+ val = build_str_regex(raw_val, search_type)
98
104
  elsif [Numeric, Integer].include?(type)
99
- if raw_val == raw_val.to_i.to_s
100
- val = raw_val.to_i
101
- elsif raw_val.to_f != 0 || raw_val[/\A[\.0]*0\Z/]
102
- val = raw_val.to_f
103
- else
104
- val = raw_val
105
- end
105
+ val = raw_val
106
106
  elsif [Date, Time, DateTime].include?(type)
107
- time_str = raw_val.tr('_.-', ' ')
108
- if time_str == time_str.to_i.to_s
109
- date_begin = Time.new(time_str)
110
- date_end = Time.new(time_str.to_i + 1).yesterday
111
- else
112
- date = Chronic.parse(time_str, guess: nil) || Chronic.parse(raw_val, guess: nil)
113
- date_begin = date.begin
114
- date_end = date.end
115
- end
116
- val = [
117
- { key => { '$gte' => date_begin } },
118
- { key => { '$lte' => date_end } }
119
- ]
120
- key = '$and'
107
+ return build_time_command(key, raw_val)
121
108
  end
122
109
  { key => val }
123
110
  end
@@ -151,7 +138,7 @@ module CommandSearch
151
138
  raw_type = command_types[key.to_sym]
152
139
 
153
140
  if raw_type.is_a?(Array)
154
- type = (raw_type - [:allow_boolean]).first
141
+ type = (raw_type - [:allow_existence_boolean]).first
155
142
  else
156
143
  type = raw_type
157
144
  end
@@ -161,12 +148,6 @@ module CommandSearch
161
148
  key = '$' + key
162
149
  val = [key, val]
163
150
  key = '$expr'
164
- elsif [Numeric, Integer].include?(type)
165
- if val == val.to_i.to_s
166
- val = val.to_i
167
- else
168
- val = val.to_f
169
- end
170
151
  elsif [Date, Time, DateTime].include?(type)
171
152
  # foo < day | day.start
172
153
  # foo <= day | day.end
@@ -187,6 +168,8 @@ module CommandSearch
187
168
  date = Chronic.parse(time_str, guess: nil) || Chronic.parse(val, guess: nil)
188
169
  end
189
170
 
171
+ date = date || []
172
+
190
173
  if date_pick == :start
191
174
  val = date.first
192
175
  elsif date_pick == :end
@@ -233,7 +216,7 @@ module CommandSearch
233
216
  end
234
217
  end
235
218
 
236
- def build_query(ast, fields, command_types = {})
219
+ def build_query(ast, fields, command_types)
237
220
  out = ast
238
221
  build_searches!(out, fields, command_types)
239
222
  build_tree!(out)
@@ -2,40 +2,42 @@ module CommandSearch
2
2
  module Parser
3
3
  module_function
4
4
 
5
- def parens_rindex(input)
6
- open_i = input.rindex { |x| x[:value] == '(' && x[:type] == :paren }
7
- return unless open_i
8
- close_offset = input.drop(open_i).index { |x| x[:value] == ')' && x[:type] == :paren }
9
- return unless close_offset
10
- [open_i, close_offset + open_i]
11
- end
12
-
13
5
  def group_parens!(input)
14
- while parens_rindex(input)
15
- (a, b) = parens_rindex(input)
16
- val = input[(a + 1)..(b - 1)]
17
- input[a..b] = { type: :nest, nest_type: :paren, value: val }
6
+ i = 0
7
+ opening_idxs = []
8
+ while i < input.length
9
+ next i += 1 unless input[i][:type] == :paren
10
+ if input[i][:value] == '('
11
+ opening_idxs.push(i)
12
+ elsif opening = opening_idxs.pop()
13
+ val = input[(opening + 1)..(i - 1)]
14
+ input[opening..i] = { type: :nest, nest_type: :paren, value: val }
15
+ i -= (val.length + 1)
16
+ end
17
+ i += 1
18
18
  end
19
19
  end
20
20
 
21
21
  def cluster!(type, input, cluster_type = :binary)
22
22
  binary = (cluster_type == :binary)
23
- input.compact!
24
- # rindex (vs index) important for nested prefixes
25
- while (i = input.rindex { |x| x[:type] == type })
26
- val = [input[i + 1]]
27
- val.unshift(input[i - 1]) if binary && i > 0
28
- front_offset = 0
29
- front_offset = 1 if binary && i > 0
30
- input[(i - front_offset)..(i + 1)] = {
31
- type: :nest,
32
- nest_type: type,
33
- nest_op: input[i][:value],
34
- value: val
35
- }
36
- end
37
- input.each do |x|
38
- cluster!(type, x[:value], cluster_type) if x[:type] == :nest
23
+ i = input.length - 1
24
+ while i >= 0
25
+ if input[i][:type] == type
26
+ val = [input[i + 1]]
27
+ val.compact!
28
+ val.unshift(input[i - 1]) if binary && i > 0
29
+ front_offset = 0
30
+ front_offset = 1 if binary && i > 0
31
+ input[(i - front_offset)..(i + 1)] = {
32
+ type: :nest,
33
+ nest_type: type,
34
+ nest_op: input[i][:value],
35
+ value: val
36
+ }
37
+ i -= 1 if binary
38
+ end
39
+ cluster!(type, input[i][:value], cluster_type) if input[i][:type] == :nest
40
+ i -= 1
39
41
  end
40
42
  end
41
43
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - zumbalogy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-17 00:00:00.000000000 Z
11
+ date: 2019-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
@@ -57,8 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
57
  - !ruby/object:Gem::Version
58
58
  version: '0'
59
59
  requirements: []
60
- rubyforge_project:
61
- rubygems_version: 2.6.11
60
+ rubygems_version: 3.0.4
62
61
  signing_key:
63
62
  specification_version: 4
64
63
  summary: Let users query collections with ease.