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 +4 -4
- data/lib/command_search/backends/postgres.rb +94 -0
- data/lib/command_search/normalizer.rb +13 -7
- data/lib/command_search.rb +18 -7
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ab70b519f399ddeeba1a8862085f8017d327b60a2cbfddaa4682363e56d2f38
|
4
|
+
data.tar.gz: 204fc638f22b3ffddef67f3065c551885c9b116b4fcadffb118f52d4d7ccffb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
108
|
-
|
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
|
data/lib/command_search.rb
CHANGED
@@ -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
|
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
|
-
|
28
|
-
return source.where(
|
34
|
+
ast = CommandSearch.build(:mongo, query, options)
|
35
|
+
return source.where(ast)
|
29
36
|
end
|
30
|
-
|
31
|
-
|
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.
|
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
|
+
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.
|
61
|
+
rubygems_version: 3.0.3
|
61
62
|
signing_key:
|
62
63
|
specification_version: 4
|
63
64
|
summary: Let users query collections with ease.
|