blazer 2.2.6
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.
Potentially problematic release.
This version of blazer might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +310 -0
- data/CONTRIBUTING.md +42 -0
- data/LICENSE.txt +22 -0
- data/README.md +1041 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +288 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
- data/app/assets/images/blazer/favicon.png +0 -0
- data/app/assets/javascripts/blazer/Chart.js +14456 -0
- data/app/assets/javascripts/blazer/Sortable.js +1540 -0
- data/app/assets/javascripts/blazer/ace.js +6 -0
- data/app/assets/javascripts/blazer/ace/ace.js +21301 -0
- data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1993 -0
- data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -0
- data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -0
- data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -0
- data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -0
- data/app/assets/javascripts/blazer/application.js +81 -0
- data/app/assets/javascripts/blazer/bootstrap.js +2377 -0
- data/app/assets/javascripts/blazer/chartkick.js +2214 -0
- data/app/assets/javascripts/blazer/daterangepicker.js +1653 -0
- data/app/assets/javascripts/blazer/fuzzysearch.js +24 -0
- data/app/assets/javascripts/blazer/highlight.min.js +3 -0
- data/app/assets/javascripts/blazer/jquery-ujs.js +555 -0
- data/app/assets/javascripts/blazer/jquery.js +10364 -0
- data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +325 -0
- data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
- data/app/assets/javascripts/blazer/moment.js +3043 -0
- data/app/assets/javascripts/blazer/queries.js +110 -0
- data/app/assets/javascripts/blazer/routes.js +26 -0
- data/app/assets/javascripts/blazer/selectize.js +3891 -0
- data/app/assets/javascripts/blazer/stupidtable-custom-settings.js +13 -0
- data/app/assets/javascripts/blazer/stupidtable.js +281 -0
- data/app/assets/javascripts/blazer/vue.js +10947 -0
- data/app/assets/stylesheets/blazer/application.css +234 -0
- data/app/assets/stylesheets/blazer/bootstrap.css.erb +6756 -0
- data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
- data/app/assets/stylesheets/blazer/github.css +125 -0
- data/app/assets/stylesheets/blazer/selectize.css +403 -0
- data/app/controllers/blazer/base_controller.rb +124 -0
- data/app/controllers/blazer/checks_controller.rb +56 -0
- data/app/controllers/blazer/dashboards_controller.rb +101 -0
- data/app/controllers/blazer/queries_controller.rb +347 -0
- data/app/helpers/blazer/base_helper.rb +43 -0
- data/app/mailers/blazer/check_mailer.rb +27 -0
- data/app/mailers/blazer/slack_notifier.rb +79 -0
- data/app/models/blazer/audit.rb +6 -0
- data/app/models/blazer/check.rb +104 -0
- data/app/models/blazer/connection.rb +5 -0
- data/app/models/blazer/dashboard.rb +17 -0
- data/app/models/blazer/dashboard_query.rb +9 -0
- data/app/models/blazer/query.rb +40 -0
- data/app/models/blazer/record.rb +5 -0
- data/app/views/blazer/_nav.html.erb +15 -0
- data/app/views/blazer/_variables.html.erb +124 -0
- data/app/views/blazer/check_mailer/failing_checks.html.erb +7 -0
- data/app/views/blazer/check_mailer/state_change.html.erb +48 -0
- data/app/views/blazer/checks/_form.html.erb +79 -0
- data/app/views/blazer/checks/edit.html.erb +3 -0
- data/app/views/blazer/checks/index.html.erb +69 -0
- data/app/views/blazer/checks/new.html.erb +3 -0
- data/app/views/blazer/dashboards/_form.html.erb +76 -0
- data/app/views/blazer/dashboards/edit.html.erb +3 -0
- data/app/views/blazer/dashboards/new.html.erb +3 -0
- data/app/views/blazer/dashboards/show.html.erb +51 -0
- data/app/views/blazer/queries/_form.html.erb +250 -0
- data/app/views/blazer/queries/docs.html.erb +131 -0
- data/app/views/blazer/queries/edit.html.erb +2 -0
- data/app/views/blazer/queries/home.html.erb +163 -0
- data/app/views/blazer/queries/new.html.erb +2 -0
- data/app/views/blazer/queries/run.html.erb +198 -0
- data/app/views/blazer/queries/schema.html.erb +55 -0
- data/app/views/blazer/queries/show.html.erb +75 -0
- data/app/views/layouts/blazer/application.html.erb +24 -0
- data/config/routes.rb +20 -0
- data/lib/blazer.rb +231 -0
- data/lib/blazer/adapters/athena_adapter.rb +129 -0
- data/lib/blazer/adapters/base_adapter.rb +53 -0
- data/lib/blazer/adapters/bigquery_adapter.rb +68 -0
- data/lib/blazer/adapters/cassandra_adapter.rb +59 -0
- data/lib/blazer/adapters/drill_adapter.rb +28 -0
- data/lib/blazer/adapters/druid_adapter.rb +67 -0
- data/lib/blazer/adapters/elasticsearch_adapter.rb +46 -0
- data/lib/blazer/adapters/influxdb_adapter.rb +45 -0
- data/lib/blazer/adapters/mongodb_adapter.rb +39 -0
- data/lib/blazer/adapters/neo4j_adapter.rb +47 -0
- data/lib/blazer/adapters/presto_adapter.rb +45 -0
- data/lib/blazer/adapters/salesforce_adapter.rb +45 -0
- data/lib/blazer/adapters/snowflake_adapter.rb +73 -0
- data/lib/blazer/adapters/soda_adapter.rb +96 -0
- data/lib/blazer/adapters/sql_adapter.rb +221 -0
- data/lib/blazer/data_source.rb +195 -0
- data/lib/blazer/detect_anomalies.R +19 -0
- data/lib/blazer/engine.rb +43 -0
- data/lib/blazer/result.rb +218 -0
- data/lib/blazer/run_statement.rb +40 -0
- data/lib/blazer/run_statement_job.rb +18 -0
- data/lib/blazer/version.rb +3 -0
- data/lib/generators/blazer/install_generator.rb +22 -0
- data/lib/generators/blazer/templates/config.yml.tt +73 -0
- data/lib/generators/blazer/templates/install.rb.tt +46 -0
- data/lib/tasks/blazer.rake +11 -0
- metadata +231 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class BaseAdapter
|
4
|
+
attr_reader :data_source
|
5
|
+
|
6
|
+
def initialize(data_source)
|
7
|
+
@data_source = data_source
|
8
|
+
end
|
9
|
+
|
10
|
+
def run_statement(statement, comment)
|
11
|
+
# the one required method
|
12
|
+
end
|
13
|
+
|
14
|
+
def tables
|
15
|
+
[] # optional, but nice to have
|
16
|
+
end
|
17
|
+
|
18
|
+
def schema
|
19
|
+
[] # optional, but nice to have
|
20
|
+
end
|
21
|
+
|
22
|
+
def preview_statement
|
23
|
+
"" # also optional, but nice to have
|
24
|
+
end
|
25
|
+
|
26
|
+
def reconnect
|
27
|
+
# optional
|
28
|
+
end
|
29
|
+
|
30
|
+
def cost(statement)
|
31
|
+
# optional
|
32
|
+
end
|
33
|
+
|
34
|
+
def explain(statement)
|
35
|
+
# optional
|
36
|
+
end
|
37
|
+
|
38
|
+
def cancel(run_id)
|
39
|
+
# optional
|
40
|
+
end
|
41
|
+
|
42
|
+
def cachable?(statement)
|
43
|
+
true # optional
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def settings
|
49
|
+
@data_source.settings
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class BigQueryAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
results = bigquery.query(statement)
|
11
|
+
|
12
|
+
# complete? was removed in google-cloud-bigquery 0.29.0
|
13
|
+
# code is for backward compatibility
|
14
|
+
if !results.respond_to?(:complete?) || results.complete?
|
15
|
+
columns = results.first.keys.map(&:to_s) if results.size > 0
|
16
|
+
rows = results.map(&:values)
|
17
|
+
else
|
18
|
+
error = Blazer::TIMEOUT_MESSAGE
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
error = e.message
|
22
|
+
end
|
23
|
+
|
24
|
+
[columns, rows, error]
|
25
|
+
end
|
26
|
+
|
27
|
+
def tables
|
28
|
+
table_refs.map { |t| "#{t.project_id}.#{t.dataset_id}.#{t.table_id}" }
|
29
|
+
end
|
30
|
+
|
31
|
+
def schema
|
32
|
+
table_refs.map do |table_ref|
|
33
|
+
{
|
34
|
+
schema: table_ref.dataset_id,
|
35
|
+
table: table_ref.table_id,
|
36
|
+
columns: table_columns(table_ref)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def preview_statement
|
42
|
+
"SELECT * FROM `{table}` LIMIT 10"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def bigquery
|
48
|
+
@bigquery ||= begin
|
49
|
+
require "google/cloud/bigquery"
|
50
|
+
Google::Cloud::Bigquery.new(
|
51
|
+
project: settings["project"],
|
52
|
+
keyfile: settings["keyfile"]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def table_refs
|
58
|
+
bigquery.datasets.map(&:tables).flat_map { |table_list| table_list.map(&:table_ref) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def table_columns(table_ref)
|
62
|
+
schema = bigquery.service.get_table(table_ref.dataset_id, table_ref.table_id).schema
|
63
|
+
return [] if schema.nil?
|
64
|
+
schema.fields.map { |field| {name: field.name, data_type: field.type} }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class CassandraAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
response = session.execute("#{statement} /*#{comment}*/")
|
11
|
+
rows = response.map { |r| r.values }
|
12
|
+
columns = rows.any? ? response.first.keys : []
|
13
|
+
rescue => e
|
14
|
+
error = e.message
|
15
|
+
end
|
16
|
+
|
17
|
+
[columns, rows, error]
|
18
|
+
end
|
19
|
+
|
20
|
+
def tables
|
21
|
+
session.execute("SELECT table_name FROM system_schema.tables WHERE keyspace_name = '#{keyspace}'").map { |r| r["table_name"] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def schema
|
25
|
+
result = session.execute("SELECT keyspace_name, table_name, column_name, type, position FROM system_schema.columns WHERE keyspace_name = '#{keyspace}'")
|
26
|
+
result.map(&:values).group_by { |r| [r[0], r[1]] }.map { |k, vs| {schema: k[0], table: k[1], columns: vs.sort_by { |v| v[2] }.map { |v| {name: v[2], data_type: v[3]} }} }
|
27
|
+
end
|
28
|
+
|
29
|
+
def preview_statement
|
30
|
+
"SELECT * FROM {table} LIMIT 10"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def cluster
|
36
|
+
@cluster ||= begin
|
37
|
+
require "cassandra"
|
38
|
+
options = {hosts: [uri.host]}
|
39
|
+
options[:port] = uri.port if uri.port
|
40
|
+
options[:username] = uri.user if uri.user
|
41
|
+
options[:password] = uri.password if uri.password
|
42
|
+
::Cassandra.cluster(options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def session
|
47
|
+
@session ||= cluster.connect(keyspace)
|
48
|
+
end
|
49
|
+
|
50
|
+
def uri
|
51
|
+
@uri ||= URI.parse(data_source.settings["url"])
|
52
|
+
end
|
53
|
+
|
54
|
+
def keyspace
|
55
|
+
@keyspace ||= uri.path[1..-1]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class DrillAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
# remove trailing semicolon
|
11
|
+
response = drill.query(statement.sub(/;\s*\z/, ""))
|
12
|
+
rows = response.map { |r| r.values }
|
13
|
+
columns = rows.any? ? response.first.keys : []
|
14
|
+
rescue => e
|
15
|
+
error = e.message
|
16
|
+
end
|
17
|
+
|
18
|
+
[columns, rows, error]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def drill
|
24
|
+
@drill ||= ::Drill.new(url: settings["url"])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class DruidAdapter < BaseAdapter
|
4
|
+
TIMESTAMP_REGEX = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\z/
|
5
|
+
|
6
|
+
def run_statement(statement, comment)
|
7
|
+
columns = []
|
8
|
+
rows = []
|
9
|
+
error = nil
|
10
|
+
|
11
|
+
header = {"Content-Type" => "application/json", "Accept" => "application/json"}
|
12
|
+
timeout = data_source.timeout ? data_source.timeout.to_i : 300
|
13
|
+
data = {
|
14
|
+
query: statement,
|
15
|
+
context: {
|
16
|
+
timeout: timeout * 1000
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
uri = URI.parse("#{settings["url"]}/druid/v2/sql/")
|
21
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
http.read_timeout = timeout
|
23
|
+
|
24
|
+
begin
|
25
|
+
response = JSON.parse(http.post(uri.request_uri, data.to_json, header).body)
|
26
|
+
if response.is_a?(Hash)
|
27
|
+
error = response["errorMessage"] || "Unknown error: #{response.inspect}"
|
28
|
+
if error.include?("timed out")
|
29
|
+
error = Blazer::TIMEOUT_MESSAGE
|
30
|
+
end
|
31
|
+
else
|
32
|
+
columns = (response.first || {}).keys
|
33
|
+
rows = response.map { |r| r.values }
|
34
|
+
|
35
|
+
# Druid doesn't return column types
|
36
|
+
# and no timestamp type in JSON
|
37
|
+
rows.each do |row|
|
38
|
+
row.each_with_index do |v, i|
|
39
|
+
if v.is_a?(String) && TIMESTAMP_REGEX.match(v)
|
40
|
+
row[i] = Time.parse(v)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue => e
|
46
|
+
error = e.message
|
47
|
+
end
|
48
|
+
|
49
|
+
[columns, rows, error]
|
50
|
+
end
|
51
|
+
|
52
|
+
def tables
|
53
|
+
result = data_source.run_statement("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA') ORDER BY TABLE_NAME")
|
54
|
+
result.rows.map(&:first)
|
55
|
+
end
|
56
|
+
|
57
|
+
def schema
|
58
|
+
result = data_source.run_statement("SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE, ORDINAL_POSITION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA') ORDER BY 1, 2")
|
59
|
+
result.rows.group_by { |r| [r[0], r[1]] }.map { |k, vs| {schema: k[0], table: k[1], columns: vs.sort_by { |v| v[2] }.map { |v| {name: v[2], data_type: v[3]} }} }
|
60
|
+
end
|
61
|
+
|
62
|
+
def preview_statement
|
63
|
+
"SELECT * FROM {table} LIMIT 10"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class ElasticsearchAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
response = client.xpack.sql.query(body: {query: "#{statement} /*#{comment}*/"})
|
11
|
+
columns = response["columns"].map { |v| v["name"] }
|
12
|
+
# Elasticsearch does not differentiate between dates and times
|
13
|
+
date_indexes = response["columns"].each_index.select { |i| response["columns"][i]["type"] == "date" }
|
14
|
+
if columns.any?
|
15
|
+
rows = response["rows"]
|
16
|
+
date_indexes.each do |i|
|
17
|
+
rows.each do |row|
|
18
|
+
row[i] = Time.parse(row[i])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue => e
|
23
|
+
error = e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
[columns, rows, error]
|
27
|
+
end
|
28
|
+
|
29
|
+
def tables
|
30
|
+
indices = client.cat.indices(format: "json").map { |v| v["index"] }
|
31
|
+
aliases = client.cat.aliases(format: "json").map { |v| v["alias"] }
|
32
|
+
(indices + aliases).uniq.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
def preview_statement
|
36
|
+
"SELECT * FROM \"{table}\" LIMIT 10"
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def client
|
42
|
+
@client ||= Elasticsearch::Client.new(url: settings["url"])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class InfluxdbAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
result = client.query(statement, denormalize: false).first
|
11
|
+
columns = result["columns"]
|
12
|
+
rows = result["values"]
|
13
|
+
|
14
|
+
# parse time columns
|
15
|
+
# current approach isn't ideal, but result doesn't include types
|
16
|
+
# another approach would be to check the format
|
17
|
+
time_index = columns.index("time")
|
18
|
+
if time_index
|
19
|
+
rows.each do |row|
|
20
|
+
row[time_index] = Time.parse(row[time_index]) if row[time_index]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
rescue => e
|
24
|
+
error = e.message
|
25
|
+
end
|
26
|
+
|
27
|
+
[columns, rows, error]
|
28
|
+
end
|
29
|
+
|
30
|
+
def tables
|
31
|
+
client.list_series
|
32
|
+
end
|
33
|
+
|
34
|
+
def preview_statement
|
35
|
+
"SELECT * FROM {table} LIMIT 10"
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def client
|
41
|
+
@client ||= InfluxDB::Client.new(url: settings["url"])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class MongodbAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
documents = db.command({:$eval => "#{statement.strip}.toArray()", nolock: true}).documents.first["retval"]
|
11
|
+
columns = documents.flat_map { |r| r.keys }.uniq
|
12
|
+
rows = documents.map { |r| columns.map { |c| r[c] } }
|
13
|
+
rescue => e
|
14
|
+
error = e.message
|
15
|
+
end
|
16
|
+
|
17
|
+
[columns, rows, error]
|
18
|
+
end
|
19
|
+
|
20
|
+
def tables
|
21
|
+
db.collection_names
|
22
|
+
end
|
23
|
+
|
24
|
+
def preview_statement
|
25
|
+
"db.{table}.find().limit(10)"
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def client
|
31
|
+
@client ||= Mongo::Client.new(settings["url"], connect_timeout: 1, socket_timeout: 1, server_selection_timeout: 1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def db
|
35
|
+
@db ||= client.database
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class Neo4jAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
result = session.query("#{statement} /*#{comment}*/")
|
11
|
+
columns = result.columns.map(&:to_s)
|
12
|
+
rows = []
|
13
|
+
result.each do |row|
|
14
|
+
rows << columns.map do |c|
|
15
|
+
v = row.send(c)
|
16
|
+
v = v.properties if v.respond_to?(:properties)
|
17
|
+
v
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
error = e.message
|
22
|
+
end
|
23
|
+
|
24
|
+
[columns, rows, error]
|
25
|
+
end
|
26
|
+
|
27
|
+
def tables
|
28
|
+
result = session.query("CALL db.labels()")
|
29
|
+
result.rows.map(&:first)
|
30
|
+
end
|
31
|
+
|
32
|
+
def preview_statement
|
33
|
+
"MATCH (n:{table}) RETURN n LIMIT 10"
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def session
|
39
|
+
@session ||= begin
|
40
|
+
require "neo4j/core/cypher_session/adaptors/http"
|
41
|
+
http_adaptor = Neo4j::Core::CypherSession::Adaptors::HTTP.new(settings["url"])
|
42
|
+
Neo4j::Core::CypherSession.new(http_adaptor)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|