command_search 0.11.0 → 0.11.1
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/lib/command_search.rb +9 -0
- data/lib/command_search/backends/postgres.rb +23 -9
- data/lib/command_search/backends/sqlite.rb +111 -0
- data/lib/command_search/normalizer.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 647f1b1fa534c1187bd63333ae986e357fb122a777e9f56d9af2d7d6f34ee6c8
|
4
|
+
data.tar.gz: d070864c2e1bba273a8fa837b20c7732efcbb5768149046cf2e68ee666e6f0b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b37775511b9cb93c556c9ce766bc76b3df6f15fb8a2671c814fa0809d32c96a5ffff0371cce4f9fb53dd9de0123f477c233611be04280883c811635a0775208
|
7
|
+
data.tar.gz: e9a636dd33c9e53211e381a00e6af3cf2aa0901fdd5e6a12079053285465eb8b4775206f48b295146b2a2e08e4e440b3888e8bff0c1bd8270edff520ad00e0b5
|
data/lib/command_search.rb
CHANGED
@@ -7,6 +7,7 @@ load(__dir__ + '/command_search/optimizer.rb')
|
|
7
7
|
load(__dir__ + '/command_search/backends/memory.rb')
|
8
8
|
load(__dir__ + '/command_search/backends/mongoer.rb')
|
9
9
|
load(__dir__ + '/command_search/backends/postgres.rb')
|
10
|
+
load(__dir__ + '/command_search/backends/sqlite.rb')
|
10
11
|
|
11
12
|
class Boolean; end
|
12
13
|
|
@@ -24,6 +25,10 @@ module CommandSearch
|
|
24
25
|
Normalizer.normalize!(ast, fields, false)
|
25
26
|
return Postgres.build_query(ast)
|
26
27
|
end
|
28
|
+
if type == :sqlite
|
29
|
+
Normalizer.normalize!(ast, fields, false)
|
30
|
+
return Sqlite.build_query(ast)
|
31
|
+
end
|
27
32
|
Normalizer.normalize!(ast, fields)
|
28
33
|
return Mongoer.build_query(ast) if type == :mongo
|
29
34
|
ast
|
@@ -38,6 +43,10 @@ module CommandSearch
|
|
38
43
|
ast = CommandSearch.build(:postgres, query, options)
|
39
44
|
return source.where(ast)
|
40
45
|
end
|
46
|
+
if source.respond_to?(:sqlite3_connection)
|
47
|
+
ast = CommandSearch.build(:sqlite, query, options)
|
48
|
+
return source.where(ast)
|
49
|
+
end
|
41
50
|
ast = CommandSearch.build(:other, query, options)
|
42
51
|
source.select { |x| CommandSearch::Memory.check(x, ast) }
|
43
52
|
end
|
@@ -4,16 +4,15 @@ module CommandSearch
|
|
4
4
|
|
5
5
|
def quote_string(str)
|
6
6
|
# activerecord/lib/active_record/connection_adapters/abstract/quoting.rb:62
|
7
|
-
str.gsub
|
8
|
-
str.gsub!("'", "''")
|
9
|
-
Regexp.escape(str)
|
7
|
+
str.gsub('\\', '\&\&').gsub("'", "''")
|
10
8
|
end
|
11
9
|
|
12
10
|
def build_quoted_regex(input)
|
13
11
|
str = quote_string(input)
|
12
|
+
str = Regexp.escape(str)
|
14
13
|
if str[/(^\W)|(\W$)/]
|
15
|
-
head_border = '(
|
16
|
-
tail_border = '(
|
14
|
+
head_border = '(^|[^:+\w])'
|
15
|
+
tail_border = '($|[^:+\w])'
|
17
16
|
return head_border + str + tail_border
|
18
17
|
end
|
19
18
|
'\m' + str + '\y'
|
@@ -36,14 +35,23 @@ module CommandSearch
|
|
36
35
|
end
|
37
36
|
if type == Time
|
38
37
|
return '0 = 1' unless val
|
39
|
-
return "
|
38
|
+
return "
|
39
|
+
(
|
40
|
+
(#{field} >= '#{val[0]}') AND
|
41
|
+
(#{field} < '#{val[1]}') AND
|
42
|
+
(#{field} IS NOT NULL)
|
43
|
+
)
|
44
|
+
"
|
40
45
|
end
|
41
46
|
if type == :quote
|
42
47
|
op = '~'
|
43
48
|
val = "'#{build_quoted_regex(val)}'"
|
44
49
|
elsif type == :str
|
45
|
-
op = '
|
46
|
-
val =
|
50
|
+
op = '~~*'
|
51
|
+
val = quote_string(val)
|
52
|
+
val.gsub!('%', '\%')
|
53
|
+
val.gsub!('_', '\_')
|
54
|
+
val = "'%#{val}%'"
|
47
55
|
elsif type == :number
|
48
56
|
op = '='
|
49
57
|
end
|
@@ -58,7 +66,13 @@ module CommandSearch
|
|
58
66
|
type = search_node[:type]
|
59
67
|
op = node[:nest_op]
|
60
68
|
if node[:compare_across_fields]
|
61
|
-
"
|
69
|
+
"
|
70
|
+
(
|
71
|
+
(#{field} #{op} #{val}) AND
|
72
|
+
(#{field} IS NOT NULL) AND
|
73
|
+
(#{val} IS NOT NULL)
|
74
|
+
)
|
75
|
+
"
|
62
76
|
elsif type == Time && val
|
63
77
|
"(#{field} #{op} '#{val}') AND (#{field} IS NOT NULL)"
|
64
78
|
elsif val.is_a?(Numeric) || val == val.to_i.to_s || val == val.to_f.to_s
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module CommandSearch
|
2
|
+
module Sqlite
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def quote_string(str)
|
6
|
+
# activerecord/lib/active_record/connection_adapters/abstract/quoting.rb:62
|
7
|
+
str.gsub('\\', '\&\&').gsub("'", "''")
|
8
|
+
end
|
9
|
+
|
10
|
+
def command_search(node)
|
11
|
+
field_node = node[:value].first
|
12
|
+
field = field_node[:value]
|
13
|
+
search_node = node[:value].last
|
14
|
+
val = search_node[:value]
|
15
|
+
type = search_node[:type]
|
16
|
+
return '0 = 1' if field == '__CommandSearch_dummy_key__'
|
17
|
+
if type == Boolean || type == :existence
|
18
|
+
false_val = 'false'
|
19
|
+
false_val = 0 if field_node[:field_type] == Numeric
|
20
|
+
if val
|
21
|
+
return "NOT ((#{field} = #{false_val}) OR (#{field} IS NULL))"
|
22
|
+
end
|
23
|
+
return "((#{field} = #{false_val}) OR (#{field} IS NULL))"
|
24
|
+
end
|
25
|
+
if type == Time
|
26
|
+
return '0 = 1' unless val
|
27
|
+
return "
|
28
|
+
(
|
29
|
+
(#{field} > '#{val[0] - 1}') AND
|
30
|
+
(#{field} <= '#{val[1] - 1}') AND
|
31
|
+
(#{field} IS NOT NULL)
|
32
|
+
)
|
33
|
+
"
|
34
|
+
end
|
35
|
+
if type == :quote
|
36
|
+
val = quote_string(val)
|
37
|
+
val.gsub!('[', '[[]')
|
38
|
+
val.gsub!('*', '[*]')
|
39
|
+
val.gsub!('?', '[?]')
|
40
|
+
border = '[ .,()?"\'\']'
|
41
|
+
return "
|
42
|
+
(
|
43
|
+
(#{field} IS NOT NULL) AND
|
44
|
+
(
|
45
|
+
(#{field} GLOB '#{val}') OR
|
46
|
+
(#{field} GLOB '*#{border}#{val}#{border}*') OR
|
47
|
+
(#{field} GLOB '*#{border}#{val}') OR
|
48
|
+
(#{field} GLOB '#{val}#{border}*')
|
49
|
+
)
|
50
|
+
)
|
51
|
+
"
|
52
|
+
elsif type == :str
|
53
|
+
op = 'LIKE'
|
54
|
+
val = quote_string(val)
|
55
|
+
val.gsub!('%', '\%')
|
56
|
+
val.gsub!('_', '\_')
|
57
|
+
val = "'%#{val}%' ESCAPE '\\'"
|
58
|
+
elsif type == :number
|
59
|
+
op = '='
|
60
|
+
end
|
61
|
+
"((#{field} #{op} #{val}) AND (#{field} IS NOT NULL))"
|
62
|
+
end
|
63
|
+
|
64
|
+
def compare_search(node)
|
65
|
+
field_node = node[:value].first
|
66
|
+
field = field_node[:value]
|
67
|
+
search_node = node[:value].last
|
68
|
+
val = search_node[:value]
|
69
|
+
type = search_node[:type]
|
70
|
+
op = node[:nest_op]
|
71
|
+
if node[:compare_across_fields]
|
72
|
+
"
|
73
|
+
(
|
74
|
+
(#{field} #{op} #{val}) AND
|
75
|
+
(#{field} IS NOT NULL) AND
|
76
|
+
(#{val} IS NOT NULL)
|
77
|
+
)
|
78
|
+
"
|
79
|
+
elsif type == Time && val
|
80
|
+
"(#{field} #{op} '#{val}') AND (#{field} IS NOT NULL)"
|
81
|
+
elsif val.is_a?(Numeric) || val == val.to_i.to_s || val == val.to_f.to_s
|
82
|
+
"(#{field} #{op} #{val}) AND (#{field} IS NOT NULL)"
|
83
|
+
else
|
84
|
+
'0 = 1'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_query(ast)
|
89
|
+
out = []
|
90
|
+
ast = [ast] unless ast.is_a?(Array)
|
91
|
+
ast.each do |node|
|
92
|
+
type = node[:type]
|
93
|
+
if type == :colon
|
94
|
+
out.push(command_search(node))
|
95
|
+
elsif type == :compare
|
96
|
+
out.push(compare_search(node))
|
97
|
+
elsif type == :and
|
98
|
+
out.push(build_query(node[:value]))
|
99
|
+
elsif type == :or
|
100
|
+
clauses = node[:value].map { |x| build_query(x) }
|
101
|
+
clause = clauses.join(' OR ')
|
102
|
+
out.push("(#{clause})")
|
103
|
+
elsif type == :not
|
104
|
+
clause = build_query(node[:value])
|
105
|
+
out.push("NOT (#{clause})")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
out.join(' AND ')
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -53,8 +53,8 @@ module CommandSearch
|
|
53
53
|
str = Regexp.escape(raw)
|
54
54
|
return node[:value] = /#{str}/i unless type == :quote
|
55
55
|
return node[:value] = /\b#{str}\b/ unless raw[/(\A\W)|(\W\Z)/]
|
56
|
-
border_a = '(
|
57
|
-
border_b = '(
|
56
|
+
border_a = '(^|[^:+\w])'
|
57
|
+
border_b = '($|[^:+\w])'
|
58
58
|
node[:value] = Regexp.new(border_a + str + border_b)
|
59
59
|
end
|
60
60
|
|
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.11.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zumbalogy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chronic
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- lib/command_search/backends/memory.rb
|
36
36
|
- lib/command_search/backends/mongoer.rb
|
37
37
|
- lib/command_search/backends/postgres.rb
|
38
|
+
- lib/command_search/backends/sqlite.rb
|
38
39
|
- lib/command_search/lexer.rb
|
39
40
|
- lib/command_search/normalizer.rb
|
40
41
|
- lib/command_search/optimizer.rb
|