timescaledb 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/timescaledb/acts_as_hypertable/core.rb +1 -1
- data/lib/timescaledb/database/quoting.rb +12 -0
- data/lib/timescaledb/database/schema_statements.rb +168 -0
- data/lib/timescaledb/database/types.rb +17 -0
- data/lib/timescaledb/database.rb +11 -0
- data/lib/timescaledb/toolkit/time_vector.rb +41 -4
- data/lib/timescaledb/version.rb +1 -1
- metadata +6 -95
- data/.github/workflows/ci.yml +0 -72
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.tool-versions +0 -1
- data/.travis.yml +0 -9
- data/CODE_OF_CONDUCT.md +0 -74
- data/Fastfile +0 -17
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -75
- data/Gemfile.scenic +0 -7
- data/Gemfile.scenic.lock +0 -119
- data/README.md +0 -490
- data/Rakefile +0 -21
- data/bin/console +0 -28
- data/bin/setup +0 -13
- data/docs/command_line.md +0 -178
- data/docs/img/lttb_example.png +0 -0
- data/docs/img/lttb_sql_vs_ruby.gif +0 -0
- data/docs/img/lttb_zoom.gif +0 -0
- data/docs/index.md +0 -72
- data/docs/migrations.md +0 -76
- data/docs/models.md +0 -78
- data/docs/toolkit.md +0 -507
- data/docs/toolkit_lttb_tutorial.md +0 -557
- data/docs/toolkit_lttb_zoom.md +0 -357
- data/docs/toolkit_ohlc.md +0 -315
- data/docs/videos.md +0 -16
- data/examples/all_in_one/all_in_one.rb +0 -94
- data/examples/all_in_one/benchmark_comparison.rb +0 -108
- data/examples/all_in_one/caggs.rb +0 -93
- data/examples/all_in_one/query_data.rb +0 -78
- data/examples/ranking/.gitattributes +0 -7
- data/examples/ranking/.gitignore +0 -29
- data/examples/ranking/.ruby-version +0 -1
- data/examples/ranking/Gemfile +0 -33
- data/examples/ranking/Gemfile.lock +0 -189
- data/examples/ranking/README.md +0 -166
- data/examples/ranking/Rakefile +0 -6
- data/examples/ranking/app/controllers/application_controller.rb +0 -2
- data/examples/ranking/app/controllers/concerns/.keep +0 -0
- data/examples/ranking/app/jobs/application_job.rb +0 -7
- data/examples/ranking/app/models/application_record.rb +0 -3
- data/examples/ranking/app/models/concerns/.keep +0 -0
- data/examples/ranking/app/models/game.rb +0 -2
- data/examples/ranking/app/models/play.rb +0 -7
- data/examples/ranking/bin/bundle +0 -114
- data/examples/ranking/bin/rails +0 -4
- data/examples/ranking/bin/rake +0 -4
- data/examples/ranking/bin/setup +0 -33
- data/examples/ranking/config/application.rb +0 -39
- data/examples/ranking/config/boot.rb +0 -4
- data/examples/ranking/config/credentials.yml.enc +0 -1
- data/examples/ranking/config/database.yml +0 -86
- data/examples/ranking/config/environment.rb +0 -5
- data/examples/ranking/config/environments/development.rb +0 -60
- data/examples/ranking/config/environments/production.rb +0 -75
- data/examples/ranking/config/environments/test.rb +0 -53
- data/examples/ranking/config/initializers/cors.rb +0 -16
- data/examples/ranking/config/initializers/filter_parameter_logging.rb +0 -8
- data/examples/ranking/config/initializers/inflections.rb +0 -16
- data/examples/ranking/config/initializers/timescale.rb +0 -2
- data/examples/ranking/config/locales/en.yml +0 -33
- data/examples/ranking/config/puma.rb +0 -43
- data/examples/ranking/config/routes.rb +0 -6
- data/examples/ranking/config/storage.yml +0 -34
- data/examples/ranking/config.ru +0 -6
- data/examples/ranking/db/migrate/20220209120747_create_games.rb +0 -10
- data/examples/ranking/db/migrate/20220209120910_create_plays.rb +0 -19
- data/examples/ranking/db/migrate/20220209143347_create_score_per_hours.rb +0 -5
- data/examples/ranking/db/schema.rb +0 -47
- data/examples/ranking/db/seeds.rb +0 -7
- data/examples/ranking/db/views/score_per_hours_v01.sql +0 -7
- data/examples/ranking/lib/tasks/.keep +0 -0
- data/examples/ranking/log/.keep +0 -0
- data/examples/ranking/public/robots.txt +0 -1
- data/examples/ranking/storage/.keep +0 -0
- data/examples/ranking/tmp/.keep +0 -0
- data/examples/ranking/tmp/pids/.keep +0 -0
- data/examples/ranking/tmp/storage/.keep +0 -0
- data/examples/ranking/vendor/.keep +0 -0
- data/examples/toolkit-demo/compare_volatility.rb +0 -104
- data/examples/toolkit-demo/lttb/README.md +0 -15
- data/examples/toolkit-demo/lttb/lttb.rb +0 -92
- data/examples/toolkit-demo/lttb/lttb_sinatra.rb +0 -139
- data/examples/toolkit-demo/lttb/lttb_test.rb +0 -21
- data/examples/toolkit-demo/lttb/views/index.erb +0 -27
- data/examples/toolkit-demo/lttb-zoom/README.md +0 -13
- data/examples/toolkit-demo/lttb-zoom/lttb_zoomable.rb +0 -90
- data/examples/toolkit-demo/lttb-zoom/views/index.erb +0 -33
- data/examples/toolkit-demo/ohlc.rb +0 -175
- data/mkdocs.yml +0 -34
- data/timescaledb.gemspec +0 -40
@@ -1,53 +0,0 @@
|
|
1
|
-
require "active_support/core_ext/integer/time"
|
2
|
-
|
3
|
-
# The test environment is used exclusively to run your application's
|
4
|
-
# test suite. You never need to work with it otherwise. Remember that
|
5
|
-
# your test database is "scratch space" for the test suite and is wiped
|
6
|
-
# and recreated between test runs. Don't rely on the data there!
|
7
|
-
|
8
|
-
Rails.application.configure do
|
9
|
-
# Settings specified here will take precedence over those in config/application.rb.
|
10
|
-
|
11
|
-
# Turn false under Spring and add config.action_view.cache_template_loading = true.
|
12
|
-
config.cache_classes = true
|
13
|
-
|
14
|
-
# Eager loading loads your whole application. When running a single test locally,
|
15
|
-
# this probably isn't necessary. It's a good idea to do in a continuous integration
|
16
|
-
# system, or in some way before deploying your code.
|
17
|
-
config.eager_load = ENV["CI"].present?
|
18
|
-
|
19
|
-
# Configure public file server for tests with Cache-Control for performance.
|
20
|
-
config.public_file_server.enabled = true
|
21
|
-
config.public_file_server.headers = {
|
22
|
-
"Cache-Control" => "public, max-age=#{1.hour.to_i}"
|
23
|
-
}
|
24
|
-
|
25
|
-
# Show full error reports and disable caching.
|
26
|
-
config.consider_all_requests_local = true
|
27
|
-
config.action_controller.perform_caching = false
|
28
|
-
config.cache_store = :null_store
|
29
|
-
|
30
|
-
# Raise exceptions instead of rendering exception templates.
|
31
|
-
config.action_dispatch.show_exceptions = false
|
32
|
-
|
33
|
-
# Disable request forgery protection in test environment.
|
34
|
-
config.action_controller.allow_forgery_protection = false
|
35
|
-
|
36
|
-
# Store uploaded files on the local file system in a temporary directory.
|
37
|
-
config.active_storage.service = :test
|
38
|
-
|
39
|
-
# Print deprecation notices to the stderr.
|
40
|
-
config.active_support.deprecation = :stderr
|
41
|
-
|
42
|
-
# Raise exceptions for disallowed deprecations.
|
43
|
-
config.active_support.disallowed_deprecation = :raise
|
44
|
-
|
45
|
-
# Tell Active Support which deprecation messages to disallow.
|
46
|
-
config.active_support.disallowed_deprecation_warnings = []
|
47
|
-
|
48
|
-
# Raises error for missing translations.
|
49
|
-
# config.i18n.raise_on_missing_translations = true
|
50
|
-
|
51
|
-
# Annotate rendered view with file names.
|
52
|
-
# config.action_view.annotate_rendered_view_with_filenames = true
|
53
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# Be sure to restart your server when you modify this file.
|
2
|
-
|
3
|
-
# Avoid CORS issues when API is called from the frontend app.
|
4
|
-
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
|
5
|
-
|
6
|
-
# Read more: https://github.com/cyu/rack-cors
|
7
|
-
|
8
|
-
# Rails.application.config.middleware.insert_before 0, Rack::Cors do
|
9
|
-
# allow do
|
10
|
-
# origins "example.com"
|
11
|
-
#
|
12
|
-
# resource "*",
|
13
|
-
# headers: :any,
|
14
|
-
# methods: [:get, :post, :put, :patch, :delete, :options, :head]
|
15
|
-
# end
|
16
|
-
# end
|
@@ -1,8 +0,0 @@
|
|
1
|
-
# Be sure to restart your server when you modify this file.
|
2
|
-
|
3
|
-
# Configure parameters to be filtered from the log file. Use this to limit dissemination of
|
4
|
-
# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported
|
5
|
-
# notations and behaviors.
|
6
|
-
Rails.application.config.filter_parameters += [
|
7
|
-
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
|
8
|
-
]
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# Be sure to restart your server when you modify this file.
|
2
|
-
|
3
|
-
# Add new inflection rules using the following format. Inflections
|
4
|
-
# are locale specific, and you may define rules for as many different
|
5
|
-
# locales as you wish. All of these examples are active by default:
|
6
|
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
7
|
-
# inflect.plural /^(ox)$/i, "\\1en"
|
8
|
-
# inflect.singular /^(ox)en/i, "\\1"
|
9
|
-
# inflect.irregular "person", "people"
|
10
|
-
# inflect.uncountable %w( fish sheep )
|
11
|
-
# end
|
12
|
-
|
13
|
-
# These inflection rules are supported but not enabled by default:
|
14
|
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
|
15
|
-
# inflect.acronym "RESTful"
|
16
|
-
# end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# Files in the config/locales directory are used for internationalization
|
2
|
-
# and are automatically loaded by Rails. If you want to use locales other
|
3
|
-
# than English, add the necessary files in this directory.
|
4
|
-
#
|
5
|
-
# To use the locales, use `I18n.t`:
|
6
|
-
#
|
7
|
-
# I18n.t "hello"
|
8
|
-
#
|
9
|
-
# In views, this is aliased to just `t`:
|
10
|
-
#
|
11
|
-
# <%= t("hello") %>
|
12
|
-
#
|
13
|
-
# To use a different locale, set it with `I18n.locale`:
|
14
|
-
#
|
15
|
-
# I18n.locale = :es
|
16
|
-
#
|
17
|
-
# This would use the information in config/locales/es.yml.
|
18
|
-
#
|
19
|
-
# The following keys must be escaped otherwise they will not be retrieved by
|
20
|
-
# the default I18n backend:
|
21
|
-
#
|
22
|
-
# true, false, on, off, yes, no
|
23
|
-
#
|
24
|
-
# Instead, surround them with single quotes.
|
25
|
-
#
|
26
|
-
# en:
|
27
|
-
# "true": "foo"
|
28
|
-
#
|
29
|
-
# To learn more, please read the Rails Internationalization guide
|
30
|
-
# available at https://guides.rubyonrails.org/i18n.html.
|
31
|
-
|
32
|
-
en:
|
33
|
-
hello: "Hello world"
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# Puma can serve each request in a thread from an internal thread pool.
|
2
|
-
# The `threads` method setting takes two numbers: a minimum and maximum.
|
3
|
-
# Any libraries that use thread pools should be configured to match
|
4
|
-
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
5
|
-
# and maximum; this matches the default thread size of Active Record.
|
6
|
-
#
|
7
|
-
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
|
8
|
-
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
9
|
-
threads min_threads_count, max_threads_count
|
10
|
-
|
11
|
-
# Specifies the `worker_timeout` threshold that Puma will use to wait before
|
12
|
-
# terminating a worker in development environments.
|
13
|
-
#
|
14
|
-
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
|
15
|
-
|
16
|
-
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
|
17
|
-
#
|
18
|
-
port ENV.fetch("PORT") { 3000 }
|
19
|
-
|
20
|
-
# Specifies the `environment` that Puma will run in.
|
21
|
-
#
|
22
|
-
environment ENV.fetch("RAILS_ENV") { "development" }
|
23
|
-
|
24
|
-
# Specifies the `pidfile` that Puma will use.
|
25
|
-
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
|
26
|
-
|
27
|
-
# Specifies the number of `workers` to boot in clustered mode.
|
28
|
-
# Workers are forked web server processes. If using threads and workers together
|
29
|
-
# the concurrency of the application would be max `threads` * `workers`.
|
30
|
-
# Workers do not work on JRuby or Windows (both of which do not support
|
31
|
-
# processes).
|
32
|
-
#
|
33
|
-
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
|
34
|
-
|
35
|
-
# Use the `preload_app!` method when specifying a `workers` number.
|
36
|
-
# This directive tells Puma to first boot the application and load code
|
37
|
-
# before forking the application. This takes advantage of Copy On Write
|
38
|
-
# process behavior so workers use less memory.
|
39
|
-
#
|
40
|
-
# preload_app!
|
41
|
-
|
42
|
-
# Allow puma to be restarted by `bin/rails restart` command.
|
43
|
-
plugin :tmp_restart
|
@@ -1,34 +0,0 @@
|
|
1
|
-
test:
|
2
|
-
service: Disk
|
3
|
-
root: <%= Rails.root.join("tmp/storage") %>
|
4
|
-
|
5
|
-
local:
|
6
|
-
service: Disk
|
7
|
-
root: <%= Rails.root.join("storage") %>
|
8
|
-
|
9
|
-
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
|
10
|
-
# amazon:
|
11
|
-
# service: S3
|
12
|
-
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
|
13
|
-
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
|
14
|
-
# region: us-east-1
|
15
|
-
# bucket: your_own_bucket-<%= Rails.env %>
|
16
|
-
|
17
|
-
# Remember not to checkin your GCS keyfile to a repository
|
18
|
-
# google:
|
19
|
-
# service: GCS
|
20
|
-
# project: your_project
|
21
|
-
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
|
22
|
-
# bucket: your_own_bucket-<%= Rails.env %>
|
23
|
-
|
24
|
-
# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
|
25
|
-
# microsoft:
|
26
|
-
# service: AzureStorage
|
27
|
-
# storage_account_name: your_account_name
|
28
|
-
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
|
29
|
-
# container: your_container_name-<%= Rails.env %>
|
30
|
-
|
31
|
-
# mirror:
|
32
|
-
# service: Mirror
|
33
|
-
# primary: local
|
34
|
-
# mirrors: [ amazon, google, microsoft ]
|
data/examples/ranking/config.ru
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
class CreatePlays < ActiveRecord::Migration[7.0]
|
2
|
-
def change
|
3
|
-
enable_extension("timescaledb") unless extensions.include? "timescaledb"
|
4
|
-
hypertable_options = {
|
5
|
-
time_column: 'created_at',
|
6
|
-
chunk_time_interval: '1 day',
|
7
|
-
compress_segmentby: 'game_id',
|
8
|
-
compress_orderby: 'created_at',
|
9
|
-
compression_interval: '7 days'
|
10
|
-
}
|
11
|
-
create_table :plays, hypertable: hypertable_options, id: false do |t|
|
12
|
-
t.references :game, null: false, foreign_key: false
|
13
|
-
t.integer :score
|
14
|
-
t.decimal :total_time
|
15
|
-
|
16
|
-
t.timestamps
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# This file is auto-generated from the current state of the database. Instead
|
2
|
-
# of editing this file, please use the migrations feature of Active Record to
|
3
|
-
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
-
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
-
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
|
-
# be faster and is potentially less error prone than running all of your
|
8
|
-
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
|
-
# migrations use external dependencies or application code.
|
10
|
-
#
|
11
|
-
# It's strongly recommended that you check this file into your version control system.
|
12
|
-
|
13
|
-
ActiveRecord::Schema[7.0].define(version: 2022_02_09_143347) do
|
14
|
-
# These are extensions that must be enabled in order to support this database
|
15
|
-
enable_extension "plpgsql"
|
16
|
-
enable_extension "timescaledb"
|
17
|
-
|
18
|
-
create_table "games", force: :cascade do |t|
|
19
|
-
t.string "name"
|
20
|
-
t.string "description"
|
21
|
-
t.datetime "created_at", null: false
|
22
|
-
t.datetime "updated_at", null: false
|
23
|
-
end
|
24
|
-
|
25
|
-
create_table "plays", id: false, force: :cascade do |t|
|
26
|
-
t.bigint "game_id", null: false
|
27
|
-
t.integer "score"
|
28
|
-
t.decimal "total_time"
|
29
|
-
t.datetime "created_at", null: false
|
30
|
-
t.datetime "updated_at", null: false
|
31
|
-
t.index ["created_at"], name: "plays_created_at_idx", order: :desc
|
32
|
-
t.index ["game_id"], name: "index_plays_on_game_id"
|
33
|
-
end
|
34
|
-
|
35
|
-
create_hypertable "plays", time_column: "created_at", chunk_time_interval: "1 minute", compress_segmentby: "game_id", compress_orderby: "created_at ASC", compression_interval: "P7D"
|
36
|
-
|
37
|
-
create_continuous_aggregate("score_per_hours", <<-SQL)
|
38
|
-
SELECT plays.game_id,
|
39
|
-
time_bucket('PT1H'::interval, plays.created_at) AS bucket,
|
40
|
-
avg(plays.score) AS avg,
|
41
|
-
max(plays.score) AS max,
|
42
|
-
min(plays.score) AS min
|
43
|
-
FROM plays
|
44
|
-
GROUP BY plays.game_id, (time_bucket('PT1H'::interval, plays.created_at))
|
45
|
-
SQL
|
46
|
-
|
47
|
-
end
|
@@ -1,7 +0,0 @@
|
|
1
|
-
# This file should contain all the record creation needed to seed the database with its default values.
|
2
|
-
# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
|
3
|
-
#
|
4
|
-
# Examples:
|
5
|
-
#
|
6
|
-
# movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }])
|
7
|
-
# Character.create(name: "Luke", movie: movies.first)
|
File without changes
|
data/examples/ranking/log/.keep
DELETED
File without changes
|
@@ -1 +0,0 @@
|
|
1
|
-
# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
File without changes
|
data/examples/ranking/tmp/.keep
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,104 +0,0 @@
|
|
1
|
-
# ruby compare_volatility.rb postgres://user:pass@host:port/db_name
|
2
|
-
require 'bundler/inline' #require only what you need
|
3
|
-
|
4
|
-
gemfile(true) do
|
5
|
-
gem 'timescaledb', path: '../..'
|
6
|
-
gem 'pry'
|
7
|
-
end
|
8
|
-
|
9
|
-
# TODO: get the volatility using the window function with plain postgresql
|
10
|
-
|
11
|
-
ActiveRecord::Base.establish_connection ARGV.last
|
12
|
-
|
13
|
-
# Compare volatility processing in Ruby vs SQL.
|
14
|
-
class Measurement < ActiveRecord::Base
|
15
|
-
acts_as_hypertable time_column: "ts"
|
16
|
-
acts_as_time_vector segment_by: "device_id", value_column: "val"
|
17
|
-
|
18
|
-
scope :volatility_sql, -> do
|
19
|
-
select("device_id, timevector(#{time_column}, #{value_column}) -> sort() -> delta() -> abs() -> sum() as volatility")
|
20
|
-
.group("device_id")
|
21
|
-
end
|
22
|
-
|
23
|
-
scope :volatility_ruby, -> {
|
24
|
-
volatility = Hash.new(0)
|
25
|
-
previous = Hash.new
|
26
|
-
find_all do |measurement|
|
27
|
-
device_id = measurement.device_id
|
28
|
-
if previous[device_id]
|
29
|
-
delta = (measurement.val - previous[device_id]).abs
|
30
|
-
volatility[device_id] += delta
|
31
|
-
end
|
32
|
-
previous[device_id] = measurement.val
|
33
|
-
end
|
34
|
-
volatility
|
35
|
-
}
|
36
|
-
scope :values_from_devices, -> {
|
37
|
-
ordered_values = select(:val, :device_id).order(:ts)
|
38
|
-
Hash[
|
39
|
-
from(ordered_values)
|
40
|
-
.group(:device_id)
|
41
|
-
.pluck("device_id, array_agg(val)")
|
42
|
-
]
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
class Volatility
|
47
|
-
def self.process(values)
|
48
|
-
previous = nil
|
49
|
-
deltas = values.map do |value|
|
50
|
-
if previous
|
51
|
-
delta = (value - previous).abs
|
52
|
-
volatility = delta
|
53
|
-
end
|
54
|
-
previous = value
|
55
|
-
volatility
|
56
|
-
end
|
57
|
-
#deltas => [nil, 1, 1]
|
58
|
-
deltas.shift
|
59
|
-
volatility = deltas.sum
|
60
|
-
end
|
61
|
-
def self.process_values(map)
|
62
|
-
map.transform_values(&method(:process))
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
ActiveRecord::Base.connection.add_toolkit_to_search_path!
|
67
|
-
|
68
|
-
|
69
|
-
ActiveRecord::Base.connection.instance_exec do
|
70
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
71
|
-
|
72
|
-
unless Measurement.table_exists?
|
73
|
-
hypertable_options = {
|
74
|
-
time_column: 'ts',
|
75
|
-
chunk_time_interval: '1 day',
|
76
|
-
}
|
77
|
-
create_table :measurements, hypertable: hypertable_options, id: false do |t|
|
78
|
-
t.integer :device_id
|
79
|
-
t.decimal :val
|
80
|
-
t.timestamp :ts
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
if Measurement.count.zero?
|
86
|
-
ActiveRecord::Base.connection.execute(<<~SQL)
|
87
|
-
INSERT INTO measurements (ts, device_id, val)
|
88
|
-
SELECT ts, device_id, random()*80
|
89
|
-
FROM generate_series(TIMESTAMP '2022-01-01 00:00:00',
|
90
|
-
TIMESTAMP '2022-02-01 00:00:00',
|
91
|
-
INTERVAL '5 minutes') AS g1(ts),
|
92
|
-
generate_series(0, 5) AS g2(device_id);
|
93
|
-
SQL
|
94
|
-
end
|
95
|
-
|
96
|
-
|
97
|
-
volatilities = nil
|
98
|
-
#ActiveRecord::Base.logger = nil
|
99
|
-
Benchmark.bm do |x|
|
100
|
-
x.report("sql") { Measurement.volatility_sql.map(&:attributes) }
|
101
|
-
x.report("ruby") { Measurement.volatility_ruby }
|
102
|
-
x.report("fetch") { volatilities = Measurement.values_from_devices }
|
103
|
-
x.report("process") { Volatility.process_values(volatilities) }
|
104
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# LTTB examples
|
2
|
-
|
3
|
-
This folder contains a few ideas to explore and learn more about the lttb algorithm.
|
4
|
-
|
5
|
-
There is a [./lttb.rb](./lttb.rb) file that is the Ruby implementation of lttb
|
6
|
-
and also contains the related [./lttb_test.rb](./lttb_test.rb) file that
|
7
|
-
verifies the same example from the Timescale Toolkit [implementation](https://github.com/timescale/timescaledb-toolkit/blob/6ee2ea1e8ff64bab10b90bdf4cd4b0f7ed763934/extension/src/lttb.rs#L512-L530).
|
8
|
-
|
9
|
-
The [./lttb_sinatra.rb](./lttb_sinatra.rb) is a small webserver that compares
|
10
|
-
the SQL vs Ruby implementation. It also uses the [./views](./views) folder which
|
11
|
-
contains the view rendering part.
|
12
|
-
|
13
|
-
You can learn more by reading the [LTTB tutorial](https://jonatas.github.io/timescaledb/toolkit_lttb_tutorial/).
|
14
|
-
|
15
|
-
|
@@ -1,92 +0,0 @@
|
|
1
|
-
module Triangle
|
2
|
-
module_function
|
3
|
-
def area(a, b, c)
|
4
|
-
(ax, ay),(bx,by),(cx,cy) = a,b,c
|
5
|
-
(
|
6
|
-
(ax - cx).to_f * (by - ay) -
|
7
|
-
(ax - bx).to_f * (cy - ay)
|
8
|
-
).abs * 0.5
|
9
|
-
end
|
10
|
-
end
|
11
|
-
class Lttb
|
12
|
-
class << self
|
13
|
-
def avg(array)
|
14
|
-
array.sum.to_f / array.size
|
15
|
-
end
|
16
|
-
|
17
|
-
def downsample(data, threshold)
|
18
|
-
new(data, threshold).downsample
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
attr_reader :data, :threshold
|
23
|
-
def initialize(data, threshold)
|
24
|
-
fail 'data is not an array' unless data.is_a? Array
|
25
|
-
fail "threshold should be >= 2. It's #{threshold}." if threshold < 2
|
26
|
-
@data = data
|
27
|
-
@threshold = threshold
|
28
|
-
end
|
29
|
-
|
30
|
-
def downsample
|
31
|
-
case @data.first.first
|
32
|
-
when Time, DateTime, Date
|
33
|
-
transformed_dates = true
|
34
|
-
dates_to_numbers()
|
35
|
-
end
|
36
|
-
process.tap do |downsampled|
|
37
|
-
numbers_to_dates(downsampled) if transformed_dates
|
38
|
-
end
|
39
|
-
end
|
40
|
-
private
|
41
|
-
|
42
|
-
def process
|
43
|
-
return data if threshold >= data.size || threshold == 0
|
44
|
-
|
45
|
-
sampled = [data.first, data.last] # Keep first and last point. append in the middle.
|
46
|
-
point_index = 0
|
47
|
-
|
48
|
-
(threshold - 2).times do |i|
|
49
|
-
step = [((i+1.0) * bucket_size).to_i, data.size].min
|
50
|
-
next_point = (i * bucket_size).to_i + 1
|
51
|
-
|
52
|
-
break if next_point > data.size - 2
|
53
|
-
|
54
|
-
points = data[step, slice]
|
55
|
-
avg_x = Lttb.avg(points.map(&:first)).to_i
|
56
|
-
avg_y = Lttb.avg(points.map(&:last))
|
57
|
-
|
58
|
-
max_area = -1.0
|
59
|
-
|
60
|
-
(next_point...(step + 1)).each do |idx|
|
61
|
-
area = Triangle.area(data[point_index], data[idx], [avg_x, avg_y])
|
62
|
-
|
63
|
-
if area > max_area
|
64
|
-
max_area = area
|
65
|
-
next_point = idx
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
sampled.insert(-2, data[next_point])
|
70
|
-
point_index = next_point
|
71
|
-
end
|
72
|
-
|
73
|
-
sampled
|
74
|
-
end
|
75
|
-
|
76
|
-
def bucket_size
|
77
|
-
@bucket_size ||= ((data.size - 2.0) / (threshold - 2.0))
|
78
|
-
end
|
79
|
-
|
80
|
-
def slice
|
81
|
-
@slice ||= bucket_size.to_i
|
82
|
-
end
|
83
|
-
|
84
|
-
def dates_to_numbers
|
85
|
-
@start_date = data[0][0].dup
|
86
|
-
data.each{|d| d[0] = d[0] - @start_date }
|
87
|
-
end
|
88
|
-
|
89
|
-
def numbers_to_dates(downsampled)
|
90
|
-
downsampled.each{|d| d[0] = @start_date + d[0]}
|
91
|
-
end
|
92
|
-
end
|