influx_query 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d265802226b7cd24ecfa9d979cf6545b2199ac1796d0c8c196857c4733cc48cf
4
+ data.tar.gz: 8756e0f15784bd7d90fcd11c0cfb53f80361e5db1c28e4a770f314efafb6ede3
5
+ SHA512:
6
+ metadata.gz: b027c73985ec0c54c8b579344a65f85385d9ca7653728270715cf1183ba73d7cbd29bbbe54fc0b9fb123e6583ed7763c7dc38415590efdb4d898979fed89c2f1
7
+ data.tar.gz: b52f4ca54d4f823d069155bbcddaf857e69f5797b5f6a5d094251dd9c913d1ba4dfac9e93a91e2e2eb971d2f1315bd59e3ca8c554a1565961fdd8d901cd0800c
data/README.md ADDED
@@ -0,0 +1,152 @@
1
+ ### influx_query
2
+
3
+ This is a gem that provides a minimal chainable DSL for InfluxDB.
4
+
5
+ ### Installation
6
+
7
+ As usual, either
8
+
9
+ ```
10
+ # in the gemfile
11
+ gem 'influx_query'
12
+ ```
13
+
14
+ or
15
+
16
+ ```
17
+ # on the command line
18
+ gem install influx_query
19
+ ```
20
+
21
+ ### Background
22
+
23
+ However before going into the API it's important to understand the
24
+ core concept behind how a query is built.
25
+
26
+ The `InfluxDB::Client` gem has a method `query` that accepts a string
27
+ as its first argument. Queries can of course be written as regular strings and passed to this, but if user-generated values are to be used in the queries, then it is unsafe to directly interpolate them.
28
+
29
+ Thankfully, the `query` method can do parameterized queries using
30
+ the `params` keyword. It looks like this:
31
+
32
+ ```
33
+ influxdb_client.query(
34
+ "select * from things where foo=%{val} ;",
35
+ params: { val: "bar" }
36
+ )
37
+ ```
38
+
39
+ This gem abstracts this away and also makes a chainable dsl.
40
+
41
+ ### Usage
42
+
43
+ This gem depends on [`influxdb-ruby`](https://github.com/influxdata/influxdb-ruby)
44
+ but doesn't come with it included. You should require that gem separately and build
45
+ an `InfluxDB::Client` instance using your credentials, as described in their README.
46
+
47
+ Once you have such a client, you can initialize `InfluxQuery` -
48
+ each instance handles only a single query.
49
+
50
+ ```
51
+ client = InfluxDB::Client.new(host: "my_host.com")
52
+ query = InfluxQuery.new(client: client, source: "things")
53
+
54
+ # At any point, can call this to see the query
55
+ puts query.finalize
56
+
57
+ # This fires off "SELECT * FROM things":
58
+ result = query.resolve
59
+
60
+ ```
61
+
62
+ `#initialize` accepts some keyword opts in addition to `client` and also makes `attr_reader`s for them. However it is not necessary to manually read/write these since they all have default values set and
63
+ are controlled by the chaining dsl.
64
+
65
+ - `conditions`: an array of strings, such as `"foo = '%{val}'"`.
66
+ These are joined using `AND` when the query is evaluated.
67
+ - `params`: the values which will be interpolated into the final query.
68
+ - `select_columns`: an array of strings.
69
+ Defaults to `["*"]` and will stay that way unless manually altered.
70
+ - `source`: string, the measurement to fetch data from, e.g. "things"s
71
+
72
+ In between `#initialize` and `#resolve`, other methods can be called:
73
+
74
+ **filter!**
75
+
76
+ Args:
77
+
78
+ 1. is a key (used internally by `#params`).
79
+ 2. is a tag/field name.
80
+ 3. comparison operator
81
+ 4. value
82
+
83
+ ```
84
+ query
85
+ .filter!(:start, "time", "<", "now() - 30d")
86
+ .filter!(:start, "time", ">", "now() - 15d")
87
+ ```
88
+
89
+ This previous example has its functionality served by a helper method
90
+ as well:
91
+
92
+ **add_time_filters!**
93
+
94
+ Args are just `start_time` and `end_time` keywords.
95
+
96
+ ```
97
+ query
98
+ .add_time_filters!(
99
+ start_time: "now() - 30d",
100
+ end_time: "now() - 15d"
101
+ )
102
+ ```
103
+
104
+ Influx's SQL-like query language lacks a proper WHERE IN type clause, so
105
+ we have to emulate it using something like `WHERE foo=1 OR foo=2 OR foo=3`.
106
+ There's a method to help with this:
107
+
108
+ **add_where_in_filter!**
109
+
110
+ ```
111
+ query
112
+ .add_where_in_filter!(:foo, "foo", [1,2,3])
113
+ ```
114
+
115
+ Moving on, these should be self explanatory
116
+
117
+ **limit!**
118
+
119
+ ```
120
+ query
121
+ .limit!(500)
122
+ ```
123
+
124
+ **offset!**
125
+
126
+ ```
127
+ query
128
+ .offset!(250)
129
+ ```
130
+
131
+ **group by**
132
+
133
+ ```
134
+ query
135
+ .group_by("foo")
136
+ ```
137
+
138
+ ### Advanced usage
139
+
140
+ It is possible to use subqueries in conditions, if you manually push those condition strings into `#conditions`.
141
+
142
+ It is possible to build subqueries using the chaining dsl, if you call
143
+ `#finalize_subquery` instead of `#finalize`. This method is actually used by
144
+ `#finalize` under the hood. Literally the only the difference is it doesn't
145
+ insert a semicolon at the end. It's up to you to add parenthesis around the
146
+ subquery as needed.
147
+
148
+ If you have a subquery you want to include in the main FROM clause, manually set the `source` to point to it.
149
+
150
+ Please feel free to send an email for help with this.
151
+ maxpleaner@gmail.com
152
+
data/bin/influx_query ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require 'influx_query'
3
+ class InfluxQuery::CLI < Thor
4
+ desc "test", "run tests"
5
+ def test
6
+ puts "No tests have been wrritten"
7
+ exit
8
+ end
9
+ end
10
+ InfluxQuery::CLI.start ARGV
@@ -0,0 +1,124 @@
1
+ class InfluxQuery
2
+
3
+ attr_reader :params, :conditions, :select_columns, :source, :client
4
+
5
+ def initialize(
6
+ source:, client:, conditions: nil, params: nil, select_columns: nil
7
+ )
8
+ @conditions = conditions || []
9
+ @params = params || {}
10
+ @select_columns = select_columns || ["*"]
11
+ @source = source
12
+ @client = client
13
+ end
14
+
15
+ def resolve
16
+ client.query finalize, params: params
17
+ end
18
+
19
+ def finalize
20
+ "#{finalize_subquery} ;"
21
+ end
22
+
23
+ def finalize_subquery
24
+ (
25
+ "select #{@select_columns.join(",")} " +
26
+ "from #{@source}" +
27
+ "#{finalize_conditions}"
28
+ ).tap do |str|
29
+ str << " GROUP BY %{group_by}" if @params[:group_by]
30
+ str << " LIMIT %{limit}" if @params[:limit]
31
+ str << " OFFSET %{offset}" if @params[:offset]
32
+ end
33
+ end
34
+
35
+ def finalize_conditions
36
+ return "" unless @conditions.any?
37
+ " where #{@conditions.join(" AND ")}"
38
+ end
39
+
40
+ def limit!(limit)
41
+ return self unless limit
42
+ limit = limit.to_i
43
+ raise(ArgumentError, "Cannot add a limit of 0") if limit.zero?
44
+ @params.merge! limit: limit
45
+ self
46
+ end
47
+
48
+ def offset!(offset)
49
+ return self unless offset
50
+ offset = offset.to_i
51
+ @params.merge! offset: offset
52
+ self
53
+ end
54
+
55
+ def group_by!(group_by)
56
+ return self unless group_by
57
+ @params.merge! group_by: group_by
58
+ self
59
+ end
60
+
61
+ def add_conditions!(properties, operator: "AND", wrap_clause: false)
62
+ clause = build_query_constraints(
63
+ properties: properties,
64
+ operator: operator
65
+ )
66
+ clause = "(#{clause})" if wrap_clause
67
+ @conditions.push clause
68
+ self
69
+ end
70
+
71
+ def add_where_in_filter!(param_key_base, col_name, vals)
72
+ return self unless vals.any?
73
+ properties = vals.map.with_index do |val, idx|
74
+ param_key = :"#{param_key_base}_#{idx}"
75
+ @params.merge! param_key => val
76
+ {
77
+ key: col_name,
78
+ operator: "=",
79
+ value: "%{#{param_key}}"
80
+ }
81
+ end
82
+ add_conditions!(properties, operator: "OR", wrap_clause: true)
83
+ end
84
+
85
+ # Most of the filters have the same structure.
86
+ def filter!(param_name, col_name, operator, val)
87
+ return self unless val
88
+ @params.merge!(param_name => val)
89
+ add_conditions! [{
90
+ key: col_name,
91
+ operator: operator,
92
+ value: "%{#{param_name}}"
93
+ }]
94
+ end
95
+
96
+ def add_time_filters!(start_date: nil, end_date: nil, **_opts)
97
+ start_date ||= (Time.now.utc - 7.days).beginning_of_day.to_i
98
+ end_date ||= Time.now.utc.end_of_day.to_i
99
+ @params.merge! start_date: start_date, end_date: end_date
100
+ add_conditions! [
101
+ { key: 'time', operator: ">=", value: "%{start_date}s" },
102
+ { key: 'time', operator: "<=", value: "%{end_date}s" }
103
+ ], operator: "AND"
104
+ end
105
+
106
+ def build_query_constraints(properties: [], operator: 'AND')
107
+ conditions = []
108
+ properties.each do |property|
109
+ # Influx requires different quotation types for different situations.
110
+ # no quotes are used here:
111
+ # where time > 123123123s (s -> seconds)
112
+ # but single quotes are used here
113
+ # where controller_action = 'sessions_controller#create'
114
+ value = if property[:value].is_a?(String) && property[:key] != 'time'
115
+ "'#{property[:value]}'"
116
+ else
117
+ property[:value]
118
+ end
119
+ conditions << "#{property[:key]} #{property[:operator]} #{value}"
120
+ end
121
+ conditions.join(" #{operator} ")
122
+ end
123
+
124
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module InfluxQuery
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: influx_query
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - max pleaner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-03-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ''
14
+ email: maxpleaner@gmail.com
15
+ executables:
16
+ - influx_query
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - bin/influx_query
22
+ - lib/influx_query.rb
23
+ - lib/version.rb
24
+ homepage: http://github.com/maxp-edcast/influx_query
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '2.3'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.7.3
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.7.3
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: query DSL for Influx
48
+ test_files: []