plunk 0.1.7 → 0.2.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
  SHA1:
3
- metadata.gz: 433f6b1bcee2104491679b8b3ba1e1a40bb901a8
4
- data.tar.gz: c36dcaf88b8f4bae8f866b55d79cecd0b386340f
3
+ metadata.gz: 9c820c78844ed02012137a7d2d0c347ae6a6ac8e
4
+ data.tar.gz: 1e6ae6df764d20dc39160a7d1706b3dcfdbd910a
5
5
  SHA512:
6
- metadata.gz: d87cf99d218b6154e3a8c368ce517f4f6eefe9021eb86e5fbb6b4440bd664663054b059900c50e9022bf7eb3214cdb5e02f53642062188aa8b510ee016af2263
7
- data.tar.gz: 8c2f64b11c8b1163278206b8f957c8e1a044ba84fca59d79a1618b58c6eb62262534312d2106f85fbe23ee4f90c1593b6fac9ed298f5926287f8f95617c7ac11
6
+ metadata.gz: ade712790a1a1088205e3d4dfd4020b5bbe7db8327ea87532bb2f4a02dd6220f03b161da675ffbf16a8f0dc34793bf361f53d0081d8668eda2d478cdc7ddfad5
7
+ data.tar.gz: 26f74070375857a46edcead703bc240b17bef95711189248b89838fe303bcdb011ef29dfd7e816de9211de7750c8a08625c642aa27d47076084a34c9ed73b263
data/Gemfile.lock CHANGED
@@ -1,11 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- plunk (0.1.5)
4
+ plunk (0.2.0)
5
5
  activesupport
6
+ elasticsearch
6
7
  json
7
8
  parslet
8
- rest-client
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
@@ -19,15 +19,23 @@ GEM
19
19
  atomic (1.1.14)
20
20
  blankslate (2.1.2.4)
21
21
  diff-lcs (1.2.5)
22
+ elasticsearch (0.4.3)
23
+ elasticsearch-api (= 0.4.3)
24
+ elasticsearch-transport (= 0.4.3)
25
+ elasticsearch-api (0.4.3)
26
+ multi_json
27
+ elasticsearch-transport (0.4.3)
28
+ faraday
29
+ multi_json
30
+ faraday (0.8.8)
31
+ multipart-post (~> 1.2.0)
22
32
  i18n (0.6.9)
23
33
  json (1.8.1)
24
- mime-types (2.0)
25
34
  minitest (4.7.5)
26
35
  multi_json (1.8.2)
36
+ multipart-post (1.2.0)
27
37
  parslet (1.5.0)
28
38
  blankslate (~> 2.0)
29
- rest-client (1.6.7)
30
- mime-types (>= 1.16)
31
39
  rspec (2.14.1)
32
40
  rspec-core (~> 2.14.0)
33
41
  rspec-expectations (~> 2.14.0)
data/README.md CHANGED
@@ -1,9 +1,39 @@
1
- plunk
1
+ Plunk
2
2
  =====
3
3
 
4
4
  Human-friendly query language for Elasticsearch
5
5
 
6
- Examples:
6
+ Currently assumes you're using Logstash to
7
+
8
+ ## Installation
9
+ ```
10
+ gem install plunk
11
+ ```
12
+
13
+ ## Usage
14
+ ```ruby
15
+ require 'plunk'
16
+
17
+ # configure is required
18
+ # elasticsearch_options accepts the same params as Elasticsearch::Client
19
+ Plunk.configure do |config|
20
+ config.elasticsearch_options = { host: 'localhost' }
21
+ end
22
+
23
+ # all documents of type syslog from the last week
24
+ Plunk.search 'last 1w _type = syslog'
25
+
26
+ # nested search. first runs the '_type=access_logs' search, extracts the values
27
+ # for fields user.name, user.nickname, and user.email, then ORs these together
28
+ # as the query for the outer search.
29
+ Plunk.search 'user = `_type=access_logs | user.name, user.nickname, user.email`'
30
+
31
+ # booleans are supported
32
+ Plunk.search 'foo.field = (bar OR baz)'
33
+ ```
34
+
35
+
36
+ ## Translation:
7
37
 
8
38
  ```last 24h _type=syslog```
9
39
 
data/lib/plunk.rb CHANGED
@@ -1,3 +1,38 @@
1
- class Plunk
1
+ require 'elasticsearch'
2
2
 
3
+ require 'plunk/utils'
4
+ require 'plunk/parser'
5
+ require 'plunk/transformer'
6
+ require 'plunk/result_set'
7
+
8
+ module Plunk
9
+ class << self
10
+ attr_accessor :elasticsearch_options, :elasticsearch_client,
11
+ :parser, :transformer
12
+ end
13
+
14
+ def self.configure(&block)
15
+ class_eval(&block)
16
+ initialize_elasticsearch
17
+ initialize_parser
18
+ initialize_transformer
19
+ end
20
+
21
+ def self.initialize_elasticsearch
22
+ self.elasticsearch_client ||= Elasticsearch::Client.new elasticsearch_options
23
+ end
24
+
25
+ def self.initialize_parser
26
+ self.parser ||= Parser.new
27
+ end
28
+
29
+ def self.initialize_transformer
30
+ self.transformer ||= Transformer.new
31
+ end
32
+
33
+ def self.search(query_string)
34
+ parsed = parser.parse query_string
35
+ result_set = transformer.apply parsed
36
+ result_set.eval
37
+ end
3
38
  end
data/lib/plunk/parser.rb CHANGED
@@ -1,119 +1,120 @@
1
1
  require 'parslet'
2
2
 
3
- class Plunk::Parser < Parslet::Parser
4
-
5
- def parenthesized(atom)
6
- lparen >> atom >> rparen
3
+ module Plunk
4
+ class Parser < Parslet::Parser
5
+
6
+ def parenthesized(atom)
7
+ lparen >> atom >> rparen
8
+ end
9
+
10
+ # Single character rules
11
+ rule(:lparen) { str('(') >> space? }
12
+ rule(:rparen) { str(')') >> space? }
13
+ rule(:comma) { str(',') >> space? }
14
+ rule(:digit) { match('[0-9]') }
15
+ rule(:space) { match('\s').repeat(1) }
16
+ rule(:space?) { space.maybe }
17
+
18
+ # Numbers
19
+ rule(:integer) { str('-').maybe >> digit.repeat(1) >> space? }
20
+ rule(:float) {
21
+ str('-').maybe >> digit.repeat(1) >> str('.') >> digit.repeat(1) >> space?
22
+ }
23
+ rule(:number) { integer | float }
24
+ rule(:datetime) {
25
+ # 1979-05-27T07:32:00Z
26
+ digit.repeat(4) >> str("-") >>
27
+ digit.repeat(2) >> str("-") >>
28
+ digit.repeat(2) >> str("T") >>
29
+ digit.repeat(2) >> str(":") >>
30
+ digit.repeat(2) >> str(":") >>
31
+ digit.repeat(2) >> str("Z")
32
+ }
33
+ rule(:escaped_special) {
34
+ str("\\") >> match['0tnr"\\\\']
35
+ }
36
+
37
+ rule(:string_special) {
38
+ match['\0\t\n\r"\\\\']
39
+ }
40
+ rule(:string) {
41
+ str('"') >>
42
+ (escaped_special | string_special.absent? >> any).repeat >>
43
+ str('"')
44
+ }
45
+
46
+ # Field / value
47
+ # rule(:identifier) { match['_@a-zA-Z.'].repeat(1) }
48
+ rule(:identifier) { match('[^=\s]').repeat(1) }
49
+ rule(:wildcard) { match('[a-zA-Z0-9.*]').repeat(1) }
50
+ rule(:searchop) { match('[=]').as(:op) }
51
+ rule(:query_value) { number | string | datetime | wildcard }
52
+
53
+ # boolean operators search
54
+ rule(:concatop) { (str('OR') | str('AND')) >> space? }
55
+ rule(:operator) { match('[|]').as(:op) >> space? }
56
+ rule(:timerange) {
57
+ integer.as(:quantity) >> match('s|m|h|d|w').as(:quantifier)
58
+ }
59
+
60
+ # Grammar parts
61
+ rule(:rhs) {
62
+ regexp | subsearch | booleanop
63
+ # regexp | subsearch | integer | wildcard | booleanop
64
+ }
65
+
66
+ rule(:boolean_value) {
67
+ booleanparen | query_value
68
+ }
69
+
70
+ rule(:booleanop) {
71
+ boolean_value >> (space >> concatop >> boolean_value).repeat
72
+ }
73
+
74
+ rule(:booleanparen) {
75
+ lparen >> space? >> booleanop >> space? >> rparen
76
+ }
77
+
78
+ rule(:regexp) {
79
+ str('/') >> match('[^/]').repeat >> str('/')
80
+ }
81
+
82
+ rule(:last) {
83
+ str("last") >> space >> timerange.as(:timerange) >> (space >>
84
+ search.as(:search)).maybe
85
+ }
86
+
87
+ rule(:search) {
88
+ identifier.as(:field) >> space? >> searchop >> space? >>
89
+ rhs.as(:value) | rhs.as(:match)
90
+ }
91
+
92
+ rule(:binaryop) {
93
+ (search | paren).as(:left) >> space? >> operator >> job.as(:right)
94
+ }
95
+
96
+ rule(:subsearch) {
97
+ str('`') >> space? >> nested_search >> str('`')
98
+ }
99
+
100
+ rule(:nested_search) {
101
+ job.as(:initial_query) >> space? >> str('|') >> space? >>
102
+ match('[^`]').repeat.as(:extractors)
103
+ }
104
+
105
+ rule(:paren) {
106
+ lparen >> space? >> job >> space? >> rparen
107
+ }
108
+
109
+ rule(:job) {
110
+ last | search | binaryop | paren
111
+ }
112
+
113
+ # root :job
114
+ rule(:plunk_query) {
115
+ job >> (space >> job).repeat
116
+ }
117
+
118
+ root :plunk_query
7
119
  end
8
-
9
- # Single character rules
10
- rule(:lparen) { str('(') >> space? }
11
- rule(:rparen) { str(')') >> space? }
12
- rule(:comma) { str(',') >> space? }
13
- rule(:digit) { match('[0-9]') }
14
- rule(:space) { match('\s').repeat(1) }
15
- rule(:space?) { space.maybe }
16
-
17
- # Numbers
18
- rule(:integer) { str('-').maybe >> digit.repeat(1) >> space? }
19
- rule(:float) {
20
- str('-').maybe >> digit.repeat(1) >> str('.') >> digit.repeat(1) >> space?
21
- }
22
- rule(:number) { integer | float }
23
- rule(:datetime) {
24
- # 1979-05-27T07:32:00Z
25
- digit.repeat(4) >> str("-") >>
26
- digit.repeat(2) >> str("-") >>
27
- digit.repeat(2) >> str("T") >>
28
- digit.repeat(2) >> str(":") >>
29
- digit.repeat(2) >> str(":") >>
30
- digit.repeat(2) >> str("Z")
31
- }
32
- rule(:escaped_special) {
33
- str("\\") >> match['0tnr"\\\\']
34
- }
35
-
36
- rule(:string_special) {
37
- match['\0\t\n\r"\\\\']
38
- }
39
- rule(:string) {
40
- str('"') >>
41
- (escaped_special | string_special.absent? >> any).repeat >>
42
- str('"')
43
- }
44
-
45
- # Field / value
46
- # rule(:identifier) { match['_@a-zA-Z.'].repeat(1) }
47
- rule(:identifier) { match('[^=\s]').repeat(1) }
48
- rule(:wildcard) { match('[a-zA-Z0-9.*]').repeat(1) }
49
- rule(:searchop) { match('[=]').as(:op) }
50
- rule(:query_value) { number | string | datetime | wildcard }
51
-
52
- # boolean operators search
53
- rule(:concatop) { (str('OR') | str('AND')) >> space? }
54
- rule(:operator) { match('[|]').as(:op) >> space? }
55
- rule(:timerange) {
56
- integer.as(:quantity) >> match('s|m|h|d|w').as(:quantifier)
57
- }
58
-
59
- # Grammar parts
60
- rule(:rhs) {
61
- regexp | subsearch | booleanop
62
- # regexp | subsearch | integer | wildcard | booleanop
63
- }
64
-
65
- rule(:boolean_value) {
66
- booleanparen | query_value
67
- }
68
-
69
- rule(:booleanop) {
70
- boolean_value >> (space >> concatop >> boolean_value).repeat
71
- }
72
-
73
- rule(:booleanparen) {
74
- lparen >> space? >> booleanop >> space? >> rparen
75
- }
76
-
77
- rule(:regexp) {
78
- str('/') >> match('[^/]').repeat >> str('/')
79
- }
80
-
81
- rule(:last) {
82
- str("last") >> space >> timerange.as(:timerange) >> (space >>
83
- search.as(:search)).maybe
84
- }
85
-
86
- rule(:search) {
87
- identifier.as(:field) >> space? >> searchop >> space? >>
88
- rhs.as(:value) | rhs.as(:match)
89
- }
90
-
91
- rule(:binaryop) {
92
- (search | paren).as(:left) >> space? >> operator >> job.as(:right)
93
- }
94
-
95
- rule(:subsearch) {
96
- str('`') >> space? >> nested_search >> str('`')
97
- }
98
-
99
- rule(:nested_search) {
100
- job.as(:initial_query) >> space? >> str('|') >> space? >>
101
- match('[^`]').repeat.as(:extractors)
102
- }
103
-
104
- rule(:paren) {
105
- lparen >> space? >> job >> space? >> rparen
106
- }
107
-
108
- rule(:job) {
109
- last | search | binaryop | paren
110
- }
111
-
112
- # root :job
113
- rule(:plunk_query) {
114
- job >> (space >> job).repeat
115
- }
116
-
117
- root :plunk_query
118
120
  end
119
-
@@ -1,36 +1,42 @@
1
- class Plunk::ResultSet
2
- attr_accessor :query, :query_string
1
+ module Plunk
2
+ class ResultSet
3
+ attr_accessor :query, :query_string
3
4
 
4
- def initialize(opts={})
5
- @query = { query: { filtered: {}}}
5
+ def self.configure(&block)
6
+ class_eval(&block)
7
+ end
8
+
9
+ def initialize(opts={})
10
+ @query = { query: { filtered: {}}}
6
11
 
7
- if opts.size >= 3 # use "and" filter to AND filters
8
- @query_string = opts[:query_string]
9
- @query[:query][:filtered][:query] = {
10
- query_string: {
11
- query: opts[:query_string] }}
12
- @query[:query][:filtered][:filter] = {
13
- and: [
14
- range: {
15
- '@timestamp' => {
16
- gte: opts[:start_time],
17
- lte: opts[:end_time] }}]}
18
- else
19
- if @query_string = opts[:query_string]
12
+ if opts.size >= 3 # use "and" filter to AND filters
13
+ @query_string = opts[:query_string]
20
14
  @query[:query][:filtered][:query] = {
21
15
  query_string: {
22
16
  query: opts[:query_string] }}
23
- elsif opts[:start_time] and opts[:end_time]
24
- @query[:query][:filtered][:query] = {
25
- range: {
26
- '@timestamp' => {
27
- gte: opts[:start_time],
28
- lte: opts[:end_time] }}}
17
+ @query[:query][:filtered][:filter] = {
18
+ and: [
19
+ range: {
20
+ '@timestamp' => {
21
+ gte: opts[:start_time],
22
+ lte: opts[:end_time] }}]}
23
+ else
24
+ if @query_string = opts[:query_string]
25
+ @query[:query][:filtered][:query] = {
26
+ query_string: {
27
+ query: opts[:query_string] }}
28
+ elsif opts[:start_time] and opts[:end_time]
29
+ @query[:query][:filtered][:query] = {
30
+ range: {
31
+ '@timestamp' => {
32
+ gte: opts[:start_time],
33
+ lte: opts[:end_time] }}}
34
+ end
29
35
  end
30
36
  end
31
- end
32
37
 
33
- def eval
34
- @@elasticsearch.search(@query.to_json) if @query
38
+ def eval
39
+ Plunk.elasticsearch_client.search(body: @query.to_json).to_json if @query
40
+ end
35
41
  end
36
42
  end
@@ -1,145 +1,145 @@
1
1
  require 'parslet'
2
2
  require 'active_support/core_ext'
3
3
 
4
- class Plunk::Transformer < Parslet::Transform
5
-
6
- rule(
7
- field: simple(:field),
8
- value: {
9
- initial_query: subtree(:initial_query),
10
- extractors: simple(:extractors)
11
- },
12
- op: '=',
13
- timerange: {
14
- quantity: simple(:quantity),
15
- quantifier: simple(:quantifier)
16
- }) do
17
-
18
- int_quantity = quantity.to_s.to_i
19
-
20
- start_time =
21
- case quantifier
22
- when 's'
23
- int_quantity.seconds.ago
24
- when 'm'
25
- int_quantity.minutes.ago
26
- when 'h'
27
- int_quantity.hours.ago
28
- when 'd'
29
- int_quantity.days.ago
30
- when 'w'
31
- int_quantity.weeks.ago
4
+ module Plunk
5
+ class Transformer < Parslet::Transform
6
+
7
+ rule(
8
+ field: simple(:field),
9
+ value: {
10
+ initial_query: subtree(:initial_query),
11
+ extractors: simple(:extractors)
12
+ },
13
+ op: '=',
14
+ timerange: {
15
+ quantity: simple(:quantity),
16
+ quantifier: simple(:quantifier)
17
+ }) do
18
+
19
+ int_quantity = quantity.to_s.to_i
20
+
21
+ start_time =
22
+ case quantifier
23
+ when 's'
24
+ int_quantity.seconds.ago
25
+ when 'm'
26
+ int_quantity.minutes.ago
27
+ when 'h'
28
+ int_quantity.hours.ago
29
+ when 'd'
30
+ int_quantity.days.ago
31
+ when 'w'
32
+ int_quantity.weeks.ago
33
+ end
34
+
35
+ end_time = Time.now
36
+
37
+ # recursively apply nested query
38
+ result_set = Plunk::Transformer.new.apply(initial_query)
39
+
40
+ json = JSON.parse result_set.eval
41
+ values = Plunk::Utils.extract_values json, extractors.to_s.split(',')
42
+
43
+ if values.empty?
44
+ ResultSet.new(
45
+ start_time: start_time.utc.to_datetime.iso8601(3),
46
+ end_time: end_time.utc.to_datetime.iso8601(3))
47
+
48
+ else
49
+ ResultSet.new(
50
+ query_string: "#{field}:(#{values.uniq.join(' OR ')})",
51
+ start_time: start_time.utc.to_datetime.iso8601(3),
52
+ end_time: end_time.utc.to_datetime.iso8601(3))
32
53
  end
33
-
34
- end_time = Time.now
35
-
36
-
37
- # recursively apply nested query
38
- result_set = Plunk::Transformer.new.apply(initial_query)
39
-
40
- json = JSON.parse result_set.eval
41
- values = Plunk::Elasticsearch.extract_values json, extractors.to_s.split(',')
42
-
43
- if values.empty?
44
- Plunk::ResultSet.new(
45
- start_time: start_time.utc.to_datetime.iso8601(3),
46
- end_time: end_time.utc.to_datetime.iso8601(3))
47
-
48
- else
49
- Plunk::ResultSet.new(
50
- query_string: "#{field}:(#{values.uniq.join(' OR ')})",
51
- start_time: start_time.utc.to_datetime.iso8601(3),
52
- end_time: end_time.utc.to_datetime.iso8601(3))
53
54
  end
54
- end
55
-
56
- rule(match: simple(:value)) do
57
- Plunk::ResultSet.new(query_string: "#{value}")
58
- end
59
-
60
- rule(
61
- field: simple(:field),
62
- value: {
63
- initial_query: subtree(:initial_query),
64
- extractors: simple(:extractors)
65
- },
66
- op: '=') do
67
-
68
- # recursively apply nested query
69
- result_set = Plunk::Transformer.new.apply(initial_query)
70
-
71
- json = JSON.parse result_set.eval
72
- values = Plunk::Elasticsearch.extract_values json, extractors.to_s.split(',')
73
55
 
74
- if values.empty?
75
- Plunk::ResultSet.new
76
- else
77
- Plunk::ResultSet.new(query_string: "#{field}:(#{values.uniq.join(' OR ')})")
56
+ rule(match: simple(:value)) do
57
+ ResultSet.new(query_string: "#{value}")
78
58
  end
79
- end
80
-
81
- rule(field: simple(:field), value: simple(:value), op: '=') do
82
- Plunk::ResultSet.new(query_string: "#{field}:#{value}")
83
- end
84
59
 
85
- rule(
86
- timerange: {
87
- quantity: simple(:quantity),
88
- quantifier: simple(:quantifier)
89
- }) do
90
-
91
- int_quantity = quantity.to_s.to_i
92
-
93
- start_time =
94
- case quantifier
95
- when 's'
96
- int_quantity.seconds.ago
97
- when 'm'
98
- int_quantity.minutes.ago
99
- when 'h'
100
- int_quantity.hours.ago
101
- when 'd'
102
- int_quantity.days.ago
103
- when 'w'
104
- int_quantity.weeks.ago
105
- end
60
+ rule(
61
+ field: simple(:field),
62
+ value: {
63
+ initial_query: subtree(:initial_query),
64
+ extractors: simple(:extractors)
65
+ },
66
+ op: '=') do
106
67
 
107
- end_time = Time.now
68
+ # recursively apply nested query
69
+ result_set = Transformer.new.apply(initial_query)
108
70
 
109
- Plunk::ResultSet.new(
110
- start_time: start_time.utc.to_datetime.iso8601(3),
111
- end_time: end_time.utc.to_datetime.iso8601(3))
112
- end
71
+ json = JSON.parse result_set.eval
72
+ values = Utils.extract_values json, extractors.to_s.split(',')
113
73
 
114
- rule(
115
- search: simple(:result_set),
116
- timerange: {
117
- quantity: simple(:quantity),
118
- quantifier: simple(:quantifier)
119
- }) do
120
-
121
- int_quantity = quantity.to_s.to_i
122
-
123
- start_time =
124
- case quantifier
125
- when 's'
126
- int_quantity.seconds.ago
127
- when 'm'
128
- int_quantity.minutes.ago
129
- when 'h'
130
- int_quantity.hours.ago
131
- when 'd'
132
- int_quantity.days.ago
133
- when 'w'
134
- int_quantity.weeks.ago
74
+ if values.empty?
75
+ ResultSet.new
76
+ else
77
+ ResultSet.new(query_string: "#{field}:(#{values.uniq.join(' OR ')})")
135
78
  end
79
+ end
136
80
 
137
- end_time = Time.now
81
+ rule(field: simple(:field), value: simple(:value), op: '=') do
82
+ ResultSet.new(query_string: "#{field}:#{value}")
83
+ end
138
84
 
139
- Plunk::ResultSet.new(
140
- query_string: result_set.query_string,
141
- start_time: start_time.utc.to_datetime.iso8601(3),
142
- end_time: end_time.utc.to_datetime.iso8601(3))
85
+ rule(
86
+ timerange: {
87
+ quantity: simple(:quantity),
88
+ quantifier: simple(:quantifier)
89
+ }) do
90
+
91
+ int_quantity = quantity.to_s.to_i
92
+
93
+ start_time =
94
+ case quantifier
95
+ when 's'
96
+ int_quantity.seconds.ago
97
+ when 'm'
98
+ int_quantity.minutes.ago
99
+ when 'h'
100
+ int_quantity.hours.ago
101
+ when 'd'
102
+ int_quantity.days.ago
103
+ when 'w'
104
+ int_quantity.weeks.ago
105
+ end
106
+
107
+ end_time = Time.now
108
+
109
+ ResultSet.new(
110
+ start_time: start_time.utc.to_datetime.iso8601(3),
111
+ end_time: end_time.utc.to_datetime.iso8601(3))
112
+ end
113
+
114
+ rule(
115
+ search: simple(:result_set),
116
+ timerange: {
117
+ quantity: simple(:quantity),
118
+ quantifier: simple(:quantifier)
119
+ }) do
120
+
121
+ int_quantity = quantity.to_s.to_i
122
+
123
+ start_time =
124
+ case quantifier
125
+ when 's'
126
+ int_quantity.seconds.ago
127
+ when 'm'
128
+ int_quantity.minutes.ago
129
+ when 'h'
130
+ int_quantity.hours.ago
131
+ when 'd'
132
+ int_quantity.days.ago
133
+ when 'w'
134
+ int_quantity.weeks.ago
135
+ end
136
+
137
+ end_time = Time.now
138
+
139
+ ResultSet.new(
140
+ query_string: result_set.query_string,
141
+ start_time: start_time.utc.to_datetime.iso8601(3),
142
+ end_time: end_time.utc.to_datetime.iso8601(3))
143
+ end
143
144
  end
144
145
  end
145
-
@@ -0,0 +1,25 @@
1
+ module Plunk
2
+ class Utils
3
+ # nested field matcher
4
+ def self.extract_values(hash, keys)
5
+ @vals ||= []
6
+
7
+ hash.each_pair do |k, v|
8
+ if v.is_a? Hash
9
+ extract_values(v, keys)
10
+ elsif v.is_a? Array
11
+ v.flatten!
12
+ if v.first.is_a? Hash
13
+ v.each { |el| extract_values(el, keys) }
14
+ elsif keys.include? k
15
+ @vals += v
16
+ end
17
+ elsif keys.include? k
18
+ @vals << v
19
+ end
20
+ end
21
+
22
+ return @vals
23
+ end
24
+ end
25
+ end
data/plunk.gemspec CHANGED
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "plunk"
3
- s.version = "0.1.7"
3
+ s.version = "0.2.0"
4
4
  s.date = "2013-12-03"
5
5
  s.add_runtime_dependency "json"
6
6
  s.add_runtime_dependency "parslet"
7
- s.add_runtime_dependency "rest-client"
7
+ s.add_runtime_dependency "elasticsearch"
8
8
  s.add_runtime_dependency "activesupport"
9
9
  s.add_development_dependency "rspec"
10
10
  s.summary = "Elasticsearch query language"
data/spec/spec_helper.rb CHANGED
@@ -1,18 +1,16 @@
1
1
  require 'rspec'
2
2
  require 'plunk'
3
- require 'plunk/parser'
4
- require 'plunk/transformer'
5
- require 'plunk/result_set'
6
- require 'plunk/elasticsearch'
7
3
  require 'parslet/rig/rspec'
8
4
 
9
5
  # Print ascii_tree when exception occurs
10
- class Plunk::ParserWrapper < Plunk::Parser
11
- def parse(query)
12
- begin
13
- super(query)
14
- rescue Parslet::ParseFailed => failure
15
- puts failure.cause.ascii_tree
6
+ module Plunk
7
+ class ParserWrapper < Parser
8
+ def parse(query)
9
+ begin
10
+ super(query)
11
+ rescue Parslet::ParseFailed => failure
12
+ puts failure.cause.ascii_tree
13
+ end
16
14
  end
17
15
  end
18
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plunk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ram Mehta
@@ -40,7 +40,7 @@ dependencies:
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
- name: rest-client
43
+ name: elasticsearch
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - '>='
@@ -97,15 +97,14 @@ files:
97
97
  - README.md
98
98
  - Rakefile
99
99
  - lib/plunk.rb
100
- - lib/plunk/elasticsearch.rb
101
100
  - lib/plunk/parser.rb
102
101
  - lib/plunk/result_set.rb
103
102
  - lib/plunk/transformer.rb
103
+ - lib/plunk/utils.rb
104
104
  - plunk.gemspec
105
105
  - spec/basic_spec.rb
106
106
  - spec/boolean_spec.rb
107
107
  - spec/chained_search_spec.rb
108
- - spec/elasticseach_spec.rb
109
108
  - spec/field_value_spec.rb
110
109
  - spec/last_spec.rb
111
110
  - spec/nested_search_spec.rb
@@ -1,95 +0,0 @@
1
- # TODO: refactor this to speed up
2
-
3
- require 'json'
4
- require 'rest-client'
5
-
6
- # Wrapper for Elasticsearch API
7
- class Plunk::Elasticsearch
8
- attr_accessor :host, :port, :fields
9
-
10
- def initialize(opts={})
11
- @scheme = opts[:scheme] || "http"
12
- @host = opts[:host] || "localhost"
13
- @port = opts[:port] || "9200"
14
- @size = opts[:size] || "10000"
15
- @endpoint = "#{@scheme}://#{@host}:#{@port}"
16
- end
17
-
18
- # Get list of all field mappings stored in ES
19
- # TODO: cache response from ES
20
- def available_fields
21
- uri = URI.escape "#{@endpoint}/_mapping"
22
- result = JSON.parse(RestClient.get(uri))
23
-
24
- @fields = {}
25
- nested_values(result, 'properties')
26
-
27
- @fields
28
- end
29
-
30
- def active_record_errors_for(query)
31
- return " can't be blank." if query.blank?
32
-
33
- uri = URI.escape "#{@endpoint}/_validate/query?explain=true"
34
- response = RestClient.post(uri, build_ES_validator(query))
35
-
36
- json = JSON.parse(response)
37
-
38
- json['valid'] ? nil : json['explanations'].collect { |exp| exp['error'] }
39
- end
40
-
41
- #
42
- # UTILITY METHODS
43
- #
44
- def search(query)
45
- uri = URI.escape "#{@endpoint}/_search?size=#{@size}"
46
-
47
- RestClient.post uri, query
48
- end
49
-
50
- # returns all values for all occurences of the given nested key
51
- def nested_values(hash, key)
52
- hash.each do |k, v|
53
- if k == key
54
- @fields.merge! v
55
- else
56
- nested_values(v, key) if v.is_a? Hash
57
- end
58
- end
59
- end
60
-
61
- # nested field matcher
62
- def self.extract_values(hash, keys)
63
- @vals ||= []
64
-
65
- hash.each_pair do |k, v|
66
- if v.is_a? Hash
67
- extract_values(v, keys)
68
- elsif v.is_a? Array
69
- v.flatten!
70
- if v.first.is_a? Hash
71
- v.each { |el| extract_values(el, keys) }
72
- elsif keys.include? k
73
- @vals += v
74
- end
75
- elsif keys.include? k
76
- @vals << v
77
- end
78
- end
79
-
80
- return @vals
81
- end
82
-
83
- def build_ES_validator(query)
84
- {
85
- filtered: {
86
- query: {
87
- query_string: {
88
- query: query
89
- }
90
- }
91
- }
92
- }.to_json
93
- end
94
- end
95
-
@@ -1,6 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Plunk::Elasticsearch do
4
- pending 'mock and test Elasticsearch methods' do
5
- end
6
- end