redis-time-series 0.2.0 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51234a8f3542da255912f95982ebab2bf66dd657da9e38e55afdedd97029685d
4
- data.tar.gz: 42a49fdafb136d8ebe4b094f935b0427430672ab810691a35ca2f78328d92135
3
+ metadata.gz: 0d699daec01bb891952501de6a51cc4b966be74a0f8585030bafb4f6a908b36e
4
+ data.tar.gz: dfa027d344c4d5485b0a22a33edd67973c2a60cb60dbbaa1a57101ccce8833fc
5
5
  SHA512:
6
- metadata.gz: acc08d1745cf24bfc87c22d3809570daa0b2feae4e5d5b97d4a43a0626217b44ea3ffdb12c3f6799c068bb6e62f0cc7a1b65274e5d8eac5cb13017bfea73dff4
7
- data.tar.gz: 1ff7aa6fb44dd2729612ac24e2be45496d45bd5a0fc86ed692bd18c38bcb160971c0efed05e598ffabb6471cf4d6903d303a1d0b74e55672c479829b98b26012
6
+ metadata.gz: acadcad9c6919e810f0e100bfe20b60055507e2efa98d0853fca6c15a252f99b65c722c71acc99d37d8d9f72964d91f862c9604fb96458ca3faaddce9f841752
7
+ data.tar.gz: 489415e8247d8cffefcf13e4e70c82cde752170c4416bc0973495c5d38b1d27d2b554cdd62db8729c9c71b125cb4848aa5a1efd81b998aa5f6e5683c13be8ccc
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.0
6
+ * Added `TS.QUERYINDEX` command
7
+
5
8
  ## 0.2.0
6
9
  * Converted `#info` to a struct instead of a hash.
7
10
  * Added methods on time series for getting info attributes.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-time-series (0.1.1)
4
+ redis-time-series (0.3.0)
5
5
  redis (~> 4.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- ![](https://github.com/dzunk/redis-time-series/workflows/RSpec/badge.svg)
1
+ [![RSpec](https://github.com/dzunk/redis-time-series/workflows/RSpec/badge.svg)](https://github.com/dzunk/redis-time-series/actions?query=workflow%3ARSpec+branch%3Amaster)
2
+ [![Gem Version](https://badge.fury.io/rb/redis-time-series.svg)](https://badge.fury.io/rb/redis-time-series)
2
3
 
3
4
  # RedisTimeSeries
4
5
 
@@ -159,36 +160,50 @@ ts.length
159
160
  ts.size
160
161
  => 3
161
162
  ```
163
+ Find series matching specific label(s)
164
+ ```ruby
165
+ Redis::TimeSeries.queryindex('foo=bar')
166
+ => [#<Redis::TimeSeries:0x00007fc115ba1610
167
+ @key="ts3",
168
+ @redis=#<Redis client v4.2.1 for redis://127.0.0.1:6379/0>,
169
+ @retention=nil,
170
+ @uncompressed=false>]
171
+ # Note that you need at least one "label equals value" filter
172
+ Redis::TimeSeries.queryindex('foo!=bar')
173
+ => RuntimeError: Filtering requires at least one equality comparison
174
+ ```
175
+
162
176
 
163
177
  ### TODO
164
178
  * `TS.REVRANGE`
165
179
  * `TS.MRANGE`/`TS.MREVRANGE`
166
- * `TS.QUERYINDEX`
167
180
  * Compaction rules
168
181
  * Filters
169
182
  * Probably a bunch more stuff
170
183
 
171
184
  ## Development
172
185
 
173
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
174
-
175
- In order to run the specs or use the console, you'll need a Redis server running on the default port 6379 with the RedisTimeSeries module enabled. The easiest way to do so is by running the Docker image:
176
- ```
177
- docker run -p 6379:6379 -it --rm redislabs/redistimeseries
178
- ```
186
+ After checking out the repo, run `bin/setup`. You need the `docker` daemon installed and running. This script will:
187
+ * Install gem dependencies
188
+ * Pull the latest `redislabs/redistimeseries` image
189
+ * Start a Redis server on port 6379
190
+ * Seed three time series with some sample data
191
+ * Attach to the running server and print logs to `STDOUT`
179
192
 
180
- The `bin/console` script will set up three time series, `@ts1`, `@ts2`, and `@ts3`, with three values in each. **It will also flush the local Redis server each time you run it**, so don't try it if you have data you don't want to lose!
193
+ With the above script running, or after starting a server manually, you can run `bin/console` to interact with it. The three series are named `ts1`, `ts2`, and `ts3`, and are available as instance variables in the console.
181
194
 
182
195
  If you want to see the commands being executed, run the console with `DEBUG=true bin/console` and it will output the raw command strings as they're executed.
183
196
  ```ruby
184
197
  [1] pry(main)> @ts1.increment
185
- DEBUG: TS.INCRBY foo 1
198
+ DEBUG: TS.INCRBY ts1 1
186
199
  => 1593159795467
187
200
  [2] pry(main)> @ts1.get
188
- DEBUG: TS.GET foo
201
+ DEBUG: TS.GET ts1
189
202
  => #<Redis::TimeSeries::Sample:0x00007f8e1a190cf8 @time=2020-06-26 01:23:15 -0700, @value=0.4e1>
190
203
  ```
191
204
 
205
+ Use `rake spec` to run the test suite.
206
+
192
207
  ## Contributing
193
208
 
194
209
  Bug reports and pull requests are welcome on GitHub at https://github.com/dzunk/redis-time-series.
@@ -6,15 +6,9 @@ require 'pry'
6
6
  require 'redis'
7
7
  require 'redis-time-series'
8
8
 
9
- Redis.current.flushall
10
-
11
- @ts1 = Redis::TimeSeries.create('foo')
12
- @ts2 = Redis::TimeSeries.create('bar')
13
- @ts3 = Redis::TimeSeries.create('baz')
14
-
9
+ @ts1 = Redis::TimeSeries.new('ts1')
10
+ @ts2 = Redis::TimeSeries.new('ts2')
11
+ @ts3 = Redis::TimeSeries.new('ts3')
15
12
  @series = [@ts1, @ts2, @ts3]
16
- @series.each do |ts|
17
- 3.times { ts.increment; sleep 0.01 }
18
- end
19
13
 
20
14
  Pry.start
data/bin/setup CHANGED
@@ -1,8 +1,30 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
1
+ #!/usr/bin/env ruby
5
2
 
6
- bundle install
3
+ system 'bundle install'
4
+ system 'docker pull redislabs/redistimeseries:latest'
5
+ container_id = `docker run -p 6379:6379 -dit --rm redislabs/redistimeseries`
7
6
 
8
- # Do any other automated setup that you need to do here
7
+ require 'bundler/setup'
8
+ require 'active_support/core_ext/numeric/time'
9
+ require 'redis'
10
+
11
+ Redis.current.flushall
12
+ ts1 = Redis::TimeSeries.create('ts1')
13
+ ts2 = Redis::TimeSeries.create('ts2')
14
+ ts3 = Redis::TimeSeries.create('ts3')
15
+
16
+ ts1.add 12, 6.minutes.ago
17
+ ts1.add 34, 4.minutes.ago
18
+ ts1.add 56, 2.minutes.ago
19
+
20
+ 10.times { ts2.increment; sleep 0.01 }
21
+
22
+ ts3.labels = { foo: 'bar' }
23
+ ts3.add 1
24
+ sleep 0.01
25
+ ts3.incrby 2
26
+ sleep 0.01
27
+ ts3.decrement
28
+
29
+ at_exit { system "docker stop #{container_id}" }
30
+ system "docker logs -f #{container_id}"
@@ -1,10 +1,11 @@
1
1
  require 'bigdecimal'
2
2
  require 'forwardable'
3
3
  require 'time/msec'
4
+ require 'redis/time_series/filter'
4
5
  require 'redis/time_series/info'
5
- require 'redis/time_series'
6
6
  require 'redis/time_series/sample'
7
+ require 'redis/time_series'
7
8
 
8
9
  class RedisTimeSeries
9
- VERSION = '0.2.0'
10
+ VERSION = '0.3.0'
10
11
  end
@@ -33,6 +33,12 @@ class Redis
33
33
  end
34
34
  end
35
35
 
36
+ def queryindex(filter_value)
37
+ filters = Filter.new(filter_value)
38
+ filters.validate!
39
+ redis.call('TS.QUERYINDEX', *filters.to_a).map { |key| new(key) }
40
+ end
41
+
36
42
  def redis
37
43
  @redis ||= Redis.current
38
44
  end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+ class Redis
3
+ class TimeSeries
4
+ class Filter
5
+ Equal = Struct.new(:label, :value) do
6
+ self::REGEX = /^[^!]+=[^(]+/
7
+
8
+ def self.parse(str)
9
+ new(*str.split('='))
10
+ end
11
+
12
+ def to_s
13
+ "#{label}=#{value}"
14
+ end
15
+ end
16
+
17
+ NotEqual = Struct.new(:label, :value) do
18
+ self::REGEX = /^.+!=[^(]+/
19
+
20
+ def self.parse(str)
21
+ new(*str.split('!='))
22
+ end
23
+
24
+ def to_s
25
+ "#{label}!=#{value}"
26
+ end
27
+ end
28
+
29
+ Absent = Struct.new(:label) do
30
+ self::REGEX = /^[^!]+=$/
31
+
32
+ def self.parse(str)
33
+ new(str.delete('='))
34
+ end
35
+
36
+ def to_s
37
+ "#{label}="
38
+ end
39
+ end
40
+
41
+ Present = Struct.new(:label) do
42
+ self::REGEX = /^.+!=$/
43
+
44
+ def self.parse(str)
45
+ new(str.delete('!='))
46
+ end
47
+
48
+ def to_s
49
+ "#{label}!="
50
+ end
51
+ end
52
+
53
+ AnyValue = Struct.new(:label, :values) do
54
+ self::REGEX = /^[^!]+=\(.+\)/
55
+
56
+ def self.parse(str)
57
+ label, values = str.split('=')
58
+ values = values.tr('()', '').split(',')
59
+ new(label, values)
60
+ end
61
+
62
+ def to_s
63
+ "#{label}=(#{values.join(',')})"
64
+ end
65
+ end
66
+
67
+ NoValues = Struct.new(:label, :values) do
68
+ self::REGEX = /^.+!=\(.+\)/
69
+
70
+ def self.parse(str)
71
+ label, values = str.split('!=')
72
+ values = values.tr('()', '').split(',')
73
+ new(label, values)
74
+ end
75
+
76
+ def to_s
77
+ "#{label}!=(#{values.join(',')})"
78
+ end
79
+ end
80
+
81
+ TYPES = [Equal, NotEqual, Absent, Present, AnyValue, NoValues]
82
+ TYPES.each do |type|
83
+ define_method "#{type.to_s.split('::').last.gsub(/(.)([A-Z])/,'\1_\2').downcase}_filters" do
84
+ filters.select { |f| f.is_a? type }
85
+ end
86
+ end
87
+
88
+ attr_reader :filters
89
+
90
+ def initialize(filters = nil)
91
+ filters = parse_string(filters) if filters.is_a?(String)
92
+ @filters = filters.presence || {}
93
+ end
94
+
95
+ def validate!
96
+ valid? || raise('Filtering requires at least one equality comparison')
97
+ end
98
+
99
+ def valid?
100
+ !!filters.find { |f| f.is_a? Equal }
101
+ end
102
+
103
+ def to_a
104
+ filters.map(&:to_s)
105
+ end
106
+
107
+ private
108
+
109
+ def parse_string(filter_string)
110
+ filter_string.split(' ').map do |str|
111
+ match = TYPES.find { |f| f::REGEX.match? str }
112
+ raise "Unable to parse '#{str}'" unless match
113
+ match.parse(str)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-time-series
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Duszynski
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-06 00:00:00.000000000 Z
11
+ date: 2020-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -114,6 +114,7 @@ files:
114
114
  - bin/setup
115
115
  - lib/redis-time-series.rb
116
116
  - lib/redis/time_series.rb
117
+ - lib/redis/time_series/filter.rb
117
118
  - lib/redis/time_series/info.rb
118
119
  - lib/redis/time_series/sample.rb
119
120
  - lib/time/msec.rb