command_search 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa471a870d21295459efa58aa18e3777752841dc0cfec3033a6423996ed2d98c
4
- data.tar.gz: 37a0b72e6cb764c90fd14eb44b0a1c096d4e6c6e2756a4715db3e02471aa6889
3
+ metadata.gz: 6ab70b519f399ddeeba1a8862085f8017d327b60a2cbfddaa4682363e56d2f38
4
+ data.tar.gz: 204fc638f22b3ffddef67f3065c551885c9b116b4fcadffb118f52d4d7ccffb2
5
5
  SHA512:
6
- metadata.gz: fe02049b5e6eec1b62ffa6d030a747312b661f4db05befb66f5e3585bf0b5b679f65c10c7ba3348a19de97ecdb0d4f9f19b2e5466b75bd4604658b66021c6fe0
7
- data.tar.gz: a5e0fa90644d5c7dd3346e1b9c7e43ef04c6e652f9f293ac59b468a2667bb388be8614854680612b62cda2812179fd800ee45a9e4229c7d4774760facd2b0b79
6
+ metadata.gz: 15beca3aa277baf873b57ff539b46c7dc966e357801ed69ed3818b17d89e021a6ead6bc233e119fd227bfc62615e2e0078e954cd92b9a32b216e3414cf6313e4
7
+ data.tar.gz: 34da12042bd8b24d8b83bdceb08454a8ec72517cd3cfca155072cc286d0b54c683bfa16fc65f8f579759ea1a8279576c989fbd7599fdd818e0a0d45b2a7cd646
@@ -0,0 +1,94 @@
1
+ module CommandSearch
2
+ module Postgres
3
+ module_function
4
+
5
+ def quote_string(str)
6
+ # activerecord/lib/active_record/connection_adapters/abstract/quoting.rb:62
7
+ str.gsub!('\\', '\&\&')
8
+ str.gsub!("'", "''")
9
+ Regexp.escape(str)
10
+ end
11
+
12
+ def build_quoted_regex(input)
13
+ str = quote_string(input)
14
+ if str[/(^\W)|(\W$)/]
15
+ head_border = '(^|\s|[^:+\w])'
16
+ tail_border = '($|\s|[^:+\w])'
17
+ return head_border + str + tail_border
18
+ end
19
+ '\m' + str + '\y'
20
+ end
21
+
22
+ def command_search(node)
23
+ field_node = node[:value].first
24
+ field = field_node[:value]
25
+ search_node = node[:value].last
26
+ val = search_node[:value]
27
+ type = search_node[:type]
28
+ return '0 = 1' if field == '__CommandSearch_dummy_key__'
29
+ if type == Boolean || type == :existence
30
+ false_val = "'f'"
31
+ false_val = 0 if field_node[:field_type] == Numeric
32
+ if val
33
+ return "NOT ((#{field} = #{false_val}) OR (#{field} IS NULL))"
34
+ end
35
+ return "((#{field} = #{false_val}) OR (#{field} IS NULL))"
36
+ end
37
+ if type == Time
38
+ return '0 = 1' unless val
39
+ return "(#{field} >= '#{val[0]}') AND (#{field} < '#{val[1]}') AND (#{field} IS NOT NULL)"
40
+ end
41
+ if type == :quote
42
+ op = '~'
43
+ val = "'#{build_quoted_regex(val)}'"
44
+ elsif type == :str
45
+ op = '~*'
46
+ val = "'#{quote_string(val)}'"
47
+ elsif type == :number
48
+ op = '='
49
+ end
50
+ "(#{field} #{op} #{val}) AND (#{field} IS NOT NULL)"
51
+ end
52
+
53
+ def compare_search(node)
54
+ field_node = node[:value].first
55
+ field = field_node[:value]
56
+ search_node = node[:value].last
57
+ val = search_node[:value]
58
+ type = search_node[:type]
59
+ op = node[:nest_op]
60
+ if node[:compare_across_fields]
61
+ "(#{field} #{op} #{val}) AND (#{field} IS NOT NULL) AND (#{val} IS NOT NULL)"
62
+ elsif type == Time && val
63
+ "(#{field} #{op} '#{val}') AND (#{field} IS NOT NULL)"
64
+ elsif val.is_a?(Numeric) || val == val.to_i.to_s || val == val.to_f.to_s
65
+ "(#{field} #{op} #{val}) AND (#{field} IS NOT NULL)"
66
+ else
67
+ '0 = 1'
68
+ end
69
+ end
70
+
71
+ def build_query(ast)
72
+ out = []
73
+ ast = [ast] unless ast.is_a?(Array)
74
+ ast.each do |node|
75
+ type = node[:type]
76
+ if type == :colon
77
+ out.push(command_search(node))
78
+ elsif type == :compare
79
+ out.push(compare_search(node))
80
+ elsif type == :and
81
+ out.push(build_query(node[:value]))
82
+ elsif type == :or
83
+ clauses = node[:value].map { |x| build_query(x) }
84
+ clause = clauses.join(' OR ')
85
+ out.push("(#{clause})")
86
+ elsif type == :not
87
+ clause = build_query(node[:value])
88
+ out.push("NOT (#{clause})")
89
+ end
90
+ end
91
+ out.join(' AND ')
92
+ end
93
+ end
94
+ end
@@ -96,22 +96,28 @@ module CommandSearch
96
96
  { type: :or, value: new_val }
97
97
  end
98
98
 
99
- def type_cast!(node, fields)
99
+ def type_cast!(node, fields, cast_all)
100
100
  (key_node, search_node) = node[:value]
101
101
  key = key_node[:value]
102
102
  field = fields[key.to_sym] || fields[key.to_s]
103
103
  return unless field
104
104
  type = field.is_a?(Class) ? field : field[:type]
105
+ type = Numeric if type == Integer
106
+ key_node[:field_type] = type
105
107
  cast_bool!(field, search_node)
106
108
  return cast_time!(node) if [Time, Date, DateTime].include?(type)
107
- return cast_numeric!(search_node) if [Integer, Numeric].include?(type)
108
- cast_regex!(search_node)
109
+ return cast_numeric!(search_node) if Numeric == type
110
+ if cast_all
111
+ cast_regex!(search_node)
112
+ else
113
+ search_node[:type] = :str if search_node[:type] == :number
114
+ end
109
115
  end
110
116
 
111
- def normalize!(ast, fields)
117
+ def normalize!(ast, fields, cast_all = true)
112
118
  ast.map! do |node|
113
119
  if node[:type] == :and || node[:type] == :or || node[:type] == :not
114
- normalize!(node[:value], fields)
120
+ normalize!(node[:value], fields, cast_all)
115
121
  next node
116
122
  end
117
123
  if node[:type] == :colon || node[:type] == :compare
@@ -127,9 +133,9 @@ module CommandSearch
127
133
  node = split_general_fields(node, fields)
128
134
  end
129
135
  if node[:type] == :or
130
- node[:value].each { |x| type_cast!(x, fields) }
136
+ node[:value].each { |x| type_cast!(x, fields, cast_all) }
131
137
  else
132
- type_cast!(node, fields)
138
+ type_cast!(node, fields, cast_all)
133
139
  end
134
140
  node
135
141
  end
@@ -6,28 +6,39 @@ load(__dir__ + '/command_search/optimizer.rb')
6
6
 
7
7
  load(__dir__ + '/command_search/backends/memory.rb')
8
8
  load(__dir__ + '/command_search/backends/mongoer.rb')
9
+ load(__dir__ + '/command_search/backends/postgres.rb')
9
10
 
10
11
  class Boolean; end
11
12
 
12
13
  module CommandSearch
13
14
  module_function
14
15
 
15
- def search(source, query, options)
16
+ def build(type, query, options)
16
17
  aliases = options[:aliases] || {}
17
18
  fields = options[:fields] || {}
18
-
19
19
  aliased_query = Aliaser.alias(query, aliases)
20
20
  ast = Lexer.lex(aliased_query)
21
-
22
21
  Parser.parse!(ast)
23
22
  Optimizer.optimize!(ast)
23
+ if type == :postgres
24
+ Normalizer.normalize!(ast, fields, false)
25
+ return Postgres.build_query(ast)
26
+ end
24
27
  Normalizer.normalize!(ast, fields)
28
+ return Mongoer.build_query(ast) if type == :mongo
29
+ ast
30
+ end
25
31
 
32
+ def search(source, query, options)
26
33
  if source.respond_to?(:mongo_client) && source.queryable
27
- mongo_query = Mongoer.build_query(ast)
28
- return source.where(mongo_query)
34
+ ast = CommandSearch.build(:mongo, query, options)
35
+ return source.where(ast)
29
36
  end
30
-
31
- source.select { |x| Memory.check(x, ast) }
37
+ if source.respond_to?(:postgresql_connection)
38
+ ast = CommandSearch.build(:postgres, query, options)
39
+ return source.where(ast)
40
+ end
41
+ ast = CommandSearch.build(:other, query, options)
42
+ source.select { |x| CommandSearch::Memory.check(x, ast) }
32
43
  end
33
44
  end
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.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zumbalogy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-14 00:00:00.000000000 Z
11
+ date: 2019-12-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chronic
@@ -34,6 +34,7 @@ files:
34
34
  - lib/command_search/aliaser.rb
35
35
  - lib/command_search/backends/memory.rb
36
36
  - lib/command_search/backends/mongoer.rb
37
+ - lib/command_search/backends/postgres.rb
37
38
  - lib/command_search/lexer.rb
38
39
  - lib/command_search/normalizer.rb
39
40
  - lib/command_search/optimizer.rb
@@ -57,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
58
  - !ruby/object:Gem::Version
58
59
  version: '0'
59
60
  requirements: []
60
- rubygems_version: 3.0.6
61
+ rubygems_version: 3.0.3
61
62
  signing_key:
62
63
  specification_version: 4
63
64
  summary: Let users query collections with ease.