arcadedb 0.5.0 → 0.5.4
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 +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +64 -58
- data/RAG_SCHEMA.md +239 -0
- data/README.md +131 -236
- data/arcade.yml +5 -5
- data/bin/console +1 -0
- data/examples/rag_example.rb +72 -0
- data/examples/rag_usecase_demo.rb +345 -0
- data/lib/arcade/api/operations.rb +33 -24
- data/lib/arcade/api/primitives.rb +52 -2
- data/lib/arcade/base.rb +35 -14
- data/lib/arcade/database.rb +16 -7
- data/lib/arcade/init.rb +5 -2
- data/lib/arcade/match.rb +85 -37
- data/lib/arcade/query.rb +10 -15
- data/lib/arcade/rag_schema.rb +131 -0
- data/lib/arcade/support/model.rb +20 -13
- data/lib/arcade/support/sql.rb +1 -1
- data/lib/arcade/support/string.rb +11 -14
- data/lib/arcade/timeseries.rb +211 -0
- data/lib/arcade/version.rb +1 -1
- data/lib/arcade.rb +7 -4
- data/lib/model/document.rb +1 -1
- data/lib/model/edge.rb +17 -0
- data/lib/model/revision.rb +41 -0
- data/lib/model/revision_record.rb +29 -0
- data/lib/model/timeseries.rb +6 -0
- data/lib/model/vertex.rb +52 -1
- data/lib/models.rb +3 -0
- data/lib/types.rb +35 -0
- metadata +12 -6
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
module Arcade
|
|
2
|
+
# Abstraction of ArcadeDB's native TimeSeries types.
|
|
3
|
+
#
|
|
4
|
+
# A TimeSeries type is not a vertex/document: it has no rid and carries no edges.
|
|
5
|
+
# Membership is expressed through tags, values through typed fields.
|
|
6
|
+
#
|
|
7
|
+
# class Ohlcv < Arcade::Timeseries
|
|
8
|
+
# timestamp :ts, precision: :second
|
|
9
|
+
# tags symbol: String, currency: String
|
|
10
|
+
# fields open: Float, high: Float, low: Float, close: Float, volume: Integer
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Ohlcv.create_type
|
|
14
|
+
# Ohlcv.write symbol: 'SAPX:GER', ts: Date.today, open: 1, high: 2, low: 0.5, close: 1.5, volume: 100
|
|
15
|
+
# Ohlcv.range from: '2026-01-01', tags: { symbol: 'SAPX:GER' }
|
|
16
|
+
# Ohlcv.latest tags: { symbol: 'SAPX:GER' }
|
|
17
|
+
# Ohlcv.bucket '7d', from: '2026-01-01', aggregate: { close: :last, volume: :sum }
|
|
18
|
+
#
|
|
19
|
+
# Conventions:
|
|
20
|
+
# * Writes go through the InfluxDB Line Protocol endpoint (fastest remote path);
|
|
21
|
+
# reads go through SQL (range pushdown, ts.* functions).
|
|
22
|
+
# * Timestamps are accepted as Time, DateTime, Date, ISO-String or epoch Numeric
|
|
23
|
+
# and normalised to the declared precision — never pass raw numbers of a
|
|
24
|
+
# different unit than declared, the engine will not complain but read wrong.
|
|
25
|
+
# * The server's DELETE on timeseries types reports a count but leaves the rows
|
|
26
|
+
# (ArcadeDB 26.10.1) — there is deliberately no window-replace helper here.
|
|
27
|
+
# Re-writing a timestamp appends a second sample; deduplicate on read if needed.
|
|
28
|
+
class Timeseries < Base
|
|
29
|
+
|
|
30
|
+
TYPES = { String => 'STRING', Float => 'DOUBLE', Integer => 'LONG',
|
|
31
|
+
:bool => 'BOOLEAN', :datetime => 'DATETIME' }.freeze
|
|
32
|
+
|
|
33
|
+
# SQL keyword and nanosecond-multiplier per supported precision
|
|
34
|
+
PRECISIONS = { second: ['SECOND', 1],
|
|
35
|
+
millisecond: ['MILLISECOND', 1_000],
|
|
36
|
+
microsecond: ['MICROSECOND', 1_000_000],
|
|
37
|
+
nanosecond: ['NANOSECOND', 1_000_000_000] }.freeze
|
|
38
|
+
|
|
39
|
+
LP_PRECISION = { second: 's', millisecond: 'ms', microsecond: 'us', nanosecond: 'ns' }.freeze
|
|
40
|
+
|
|
41
|
+
# ts.<fn>(field, ts) where the engine needs the timestamp, plain SQL otherwise
|
|
42
|
+
AGGREGATES = { last: ->(f, ts){ "ts.last(#{ f }, #{ ts })" },
|
|
43
|
+
first: ->(f, ts){ "ts.first(#{ f }, #{ ts })" },
|
|
44
|
+
avg: ->(f, _){ "avg(#{ f })" },
|
|
45
|
+
sum: ->(f, _){ "sum(#{ f })" },
|
|
46
|
+
min: ->(f, _){ "min(#{ f })" },
|
|
47
|
+
max: ->(f, _){ "max(#{ f })" },
|
|
48
|
+
count: ->(f, _){ "count(#{ f })" } }.freeze
|
|
49
|
+
|
|
50
|
+
class << self
|
|
51
|
+
|
|
52
|
+
# ---- schema declaration -----------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
def timestamp name = nil, precision: nil, shards: nil, retention: nil
|
|
55
|
+
if name.nil? && precision.nil? # getter use: ts_name via timestamp()
|
|
56
|
+
@ts_name ||= :ts
|
|
57
|
+
@ts_precision ||= :second
|
|
58
|
+
return @ts_name
|
|
59
|
+
end
|
|
60
|
+
@ts_name, @ts_precision = name, (precision || :second)
|
|
61
|
+
@ts_shards, @ts_retention = shards, retention
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def tags spec = nil
|
|
65
|
+
@ts_tags = spec if spec
|
|
66
|
+
@ts_tags ||= {}
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def fields spec = nil
|
|
70
|
+
@ts_fields = spec if spec
|
|
71
|
+
@ts_fields ||= {}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def ts_name = timestamp
|
|
75
|
+
def ts_precision = @ts_precision ||= :second
|
|
76
|
+
def type_name = database_name
|
|
77
|
+
|
|
78
|
+
# ---- type lifecycle ---------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
def create_type
|
|
81
|
+
return true if type_exists?
|
|
82
|
+
tag_cols = tags.map { |k, t| "#{ k } #{ sql_type t }" }.join(', ')
|
|
83
|
+
field_cols = fields.map { |k, t| "#{ k } #{ sql_type t }" }.join(', ')
|
|
84
|
+
cmd = "CREATE TIMESERIES TYPE #{ type_name } " \
|
|
85
|
+
"TIMESTAMP #{ ts_name } PRECISION #{ PRECISIONS[ts_precision].first } " \
|
|
86
|
+
"TAGS (#{ tag_cols }) FIELDS (#{ field_cols })"
|
|
87
|
+
cmd += " SHARDS #{ @ts_shards }" if @ts_shards
|
|
88
|
+
cmd += " RETENTION #{ @ts_retention }" if @ts_retention
|
|
89
|
+
result = db.transmit { cmd }
|
|
90
|
+
# the engine registers the type asynchronously — wait until readable
|
|
91
|
+
# (writes immediately after create otherwise fail with unknownTypes)
|
|
92
|
+
10.times { break if type_exists?; sleep 0.2 }
|
|
93
|
+
result
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# NB: schema:types does NOT list timeseries types on 26.10 —
|
|
97
|
+
# probe the ts/query endpoint instead (400 unknownTypes = absent).
|
|
98
|
+
def type_exists?
|
|
99
|
+
Api.post_json "ts/#{ db.database }/query", { type: type_name.to_s }
|
|
100
|
+
true
|
|
101
|
+
rescue Arcade::QueryError
|
|
102
|
+
false
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def drop_type
|
|
106
|
+
db.transmit { "DROP TIMESERIES TYPE #{ type_name }" }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# ---- writing ----------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
# one sample: Ohlcv.write symbol: 'SAP', ts: Date.today, close: 1.5
|
|
112
|
+
def write **sample
|
|
113
|
+
write_many [sample]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# batch: array of hashes, one per sample (Line Protocol, precision param matches class)
|
|
117
|
+
def write_many samples
|
|
118
|
+
return 0 if samples.empty?
|
|
119
|
+
lines = samples.map { |s| line_for s }
|
|
120
|
+
Api.post_text "ts/#{ db.database }/write?precision=#{ LP_PRECISION[ts_precision] }", lines.join("\n")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# ---- reading ----------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
# raw samples within [from, to] (both optional), filtered by tag equality
|
|
126
|
+
def range from: nil, to: nil, tags: {}, columns: nil, limit: nil
|
|
127
|
+
projections = columns ? ([ts_name] + Array(columns)).join(', ') : '*'
|
|
128
|
+
conditions = []
|
|
129
|
+
conditions << "#{ ts_name } >= #{ sql_time from }" if from
|
|
130
|
+
conditions << "#{ ts_name } <= #{ sql_time to }" if to
|
|
131
|
+
tags.each { |k, v| conditions << "#{ k } = '#{ v }'" }
|
|
132
|
+
sql = "SELECT #{ projections } FROM #{ type_name }"
|
|
133
|
+
sql += " WHERE #{ conditions.join ' AND ' }" unless conditions.empty?
|
|
134
|
+
sql += " ORDER BY #{ ts_name }"
|
|
135
|
+
sql += " LIMIT #{ limit }" if limit
|
|
136
|
+
db.transmit { sql }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# the most recent sample, via the dedicated latest-endpoint
|
|
140
|
+
# returns a Hash of column => value (nil when the series is empty)
|
|
141
|
+
def latest tags: {}
|
|
142
|
+
query = "ts/#{ db.database }/latest?type=#{ type_name }"
|
|
143
|
+
query += "&tag=#{ tags.first.join ':' }" unless tags.empty?
|
|
144
|
+
answer = Api.get_json query
|
|
145
|
+
return nil if answer[:latest].nil?
|
|
146
|
+
answer[:columns].map.with_index { |col, i| [ col.to_sym, answer[:latest][i] ] }.to_h
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# time-bucketed aggregation, e.g. weekly candles:
|
|
150
|
+
# Ohlcv.bucket '7d', aggregate: { close: :last, high: :max, low: :min, volume: :sum }
|
|
151
|
+
def bucket interval, from: nil, to: nil, tags: {}, aggregate: {}
|
|
152
|
+
measures = aggregate.map do |field, fn|
|
|
153
|
+
"#{ AGGREGATES.fetch(fn).call(field, ts_name) } as #{ field }_#{ fn }"
|
|
154
|
+
end.join(', ')
|
|
155
|
+
conditions = []
|
|
156
|
+
conditions << "#{ ts_name } >= #{ sql_time from }" if from
|
|
157
|
+
conditions << "#{ ts_name } <= #{ sql_time to }" if to
|
|
158
|
+
tags.each { |k, v| conditions << "#{ k } = '#{ v }'" }
|
|
159
|
+
sql = "SELECT ts.timeBucket('#{ interval }', #{ ts_name }) as `bucket`"
|
|
160
|
+
sql += ", #{ measures }" unless measures.empty?
|
|
161
|
+
sql += " FROM #{ type_name }"
|
|
162
|
+
sql += " WHERE #{ conditions.join ' AND ' }" unless conditions.empty?
|
|
163
|
+
sql += " GROUP BY `bucket` ORDER BY `bucket`"
|
|
164
|
+
db.transmit { sql }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
private
|
|
168
|
+
|
|
169
|
+
# ---- normalisation ------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
def sql_type type
|
|
172
|
+
TYPES.fetch(type) { type.to_s.upcase }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# sample hash -> InfluxDB line, epoch normalised to the declared precision
|
|
176
|
+
def line_for sample
|
|
177
|
+
stamp = to_epoch sample[ts_name] || sample[ts_name.to_s]
|
|
178
|
+
raise ArgumentError, "#{ type_name }: missing timestamp '#{ ts_name }'" if stamp.nil?
|
|
179
|
+
tag_part = tags.keys.map { |t| "#{ t }=#{ escape_tag sample[t] }" }.join(',')
|
|
180
|
+
field_part = fields.keys.map { |f| "#{ f }=#{ sample[f] }" }.join(',')
|
|
181
|
+
"#{ type_name },#{ tag_part } #{ field_part } #{ stamp }"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def to_epoch value
|
|
185
|
+
seconds = case value
|
|
186
|
+
when Time then value.to_i
|
|
187
|
+
when Numeric then value.to_i # already epoch seconds
|
|
188
|
+
when Date then Time.utc(value.year, value.month, value.day).to_i
|
|
189
|
+
when String then Time.parse(value).to_i
|
|
190
|
+
else nil end
|
|
191
|
+
return nil if seconds.nil?
|
|
192
|
+
seconds * PRECISIONS[ts_precision].last
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def sql_time value
|
|
196
|
+
t = case value
|
|
197
|
+
when Time then value
|
|
198
|
+
when Date then Time.utc(value.year, value.month, value.day)
|
|
199
|
+
when String then Time.parse(value, in: 'UTC')
|
|
200
|
+
when Numeric then Time.at(value)
|
|
201
|
+
else raise ArgumentError, "unsupported time: #{ value.inspect }" end
|
|
202
|
+
"'#{ t.utc.strftime('%Y-%m-%d %H:%M:%S') }'"
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def escape_tag value
|
|
206
|
+
value.to_s.gsub(/([,= ])/) { '\\' + $1 }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
data/lib/arcade/version.rb
CHANGED
data/lib/arcade.rb
CHANGED
|
@@ -9,13 +9,13 @@ require "dry/core/class_builder"
|
|
|
9
9
|
require "dry/core/class_attributes"
|
|
10
10
|
require 'json'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
require_relative '../lib/types'
|
|
13
|
+
#module Types
|
|
14
|
+
# include Dry.Types()
|
|
15
|
+
#end
|
|
15
16
|
require 'yaml'
|
|
16
17
|
require 'securerandom'
|
|
17
18
|
require 'httpx'
|
|
18
|
-
require 'arcade/errors'
|
|
19
19
|
require_relative '../lib/arcade/support/object'
|
|
20
20
|
require_relative '../lib/arcade/support/string'
|
|
21
21
|
require_relative '../lib/arcade/support/class'
|
|
@@ -24,13 +24,16 @@ require_relative '../lib/arcade/support/model'
|
|
|
24
24
|
require_relative '../lib/arcade/support/conversions'
|
|
25
25
|
require_relative '../lib/arcade/api/primitives'
|
|
26
26
|
require_relative '../lib/arcade/api/operations'
|
|
27
|
+
require_relative '../lib/arcade/errors'
|
|
27
28
|
require_relative '../lib/arcade/base'
|
|
28
29
|
require_relative '../lib/arcade/logging'
|
|
29
30
|
require_relative '../lib/arcade/config'
|
|
30
31
|
require_relative '../lib/arcade/database'
|
|
31
32
|
require_relative '../lib/arcade/match'
|
|
32
33
|
require_relative '../lib/arcade/query'
|
|
34
|
+
require_relative '../lib/arcade/timeseries'
|
|
33
35
|
require_relative '../lib/arcade/init'
|
|
36
|
+
require_relative '../lib/arcade/rag_schema'
|
|
34
37
|
require_relative "../lib/models"
|
|
35
38
|
require_relative '../lib/railtie' if defined? Rails::Railtie
|
|
36
39
|
|
data/lib/model/document.rb
CHANGED
data/lib/model/edge.rb
CHANGED
|
@@ -27,6 +27,23 @@ module Arcade
|
|
|
27
27
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
|
|
31
|
+
## class-method: Delete Edge between two vertices
|
|
32
|
+
##
|
|
33
|
+
## returns the count of deleted edges
|
|
34
|
+
##
|
|
35
|
+
## todo: add conditions
|
|
36
|
+
def self.delete from:, to:
|
|
37
|
+
return 0 if from.nil? || to.nil?
|
|
38
|
+
raise "parameter ( from: ) must be a String or a Vertex" unless from.is_a?(String) || from.is_a?( Arcade::Vertex)
|
|
39
|
+
raise "parameter ( to: ) must be a String or a Vertex" unless to.is_a?(String) || to.is_a?( Arcade::Vertex)
|
|
40
|
+
raise "parameters (from: + to:) must respond to `.rid`" unless from.respond_to?( :rid) && to.respond_to?(:rid)
|
|
41
|
+
|
|
42
|
+
db.execute{ "delete edge from #{from.rid} to #{to.rid} " } &.select_result &.first
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## instance method: Delete specified edge
|
|
30
47
|
def delete
|
|
31
48
|
db.execute{ "delete edge #{ rid }" }.select_result
|
|
32
49
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Arcade
|
|
2
|
+
class Revision < Arcade::Vertex
|
|
3
|
+
attribute :protocol?, Types::Nominal::Array
|
|
4
|
+
|
|
5
|
+
@@u='root' # default user
|
|
6
|
+
@@i = 'Record initiated'
|
|
7
|
+
|
|
8
|
+
def self.set_user u
|
|
9
|
+
@@u = u
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.set_initial_message m
|
|
13
|
+
@@i = m
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.insert **attr
|
|
17
|
+
|
|
18
|
+
action = block_given? ? yield : @@i
|
|
19
|
+
i= super **attr
|
|
20
|
+
i.update_list :protocol,
|
|
21
|
+
Arcade::RevisionRecord.create( user: @@u, action: action ),
|
|
22
|
+
modus: :first
|
|
23
|
+
i.refresh
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def update **attr
|
|
29
|
+
hist_state = attr.keys.map do |k|
|
|
30
|
+
[ k, send( k ) ]
|
|
31
|
+
end.to_h
|
|
32
|
+
super **attr
|
|
33
|
+
revision = if block_given?
|
|
34
|
+
Arcade::RevisionRecord.create( user: @@u, action: yield, fields: hist_state )
|
|
35
|
+
else
|
|
36
|
+
Arcade::RevisionRecord.create( fields: hist_state )
|
|
37
|
+
end
|
|
38
|
+
update_list :protocol, revision, modus: :append
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Arcade
|
|
2
|
+
class RevisionRecord < Document
|
|
3
|
+
attribute :user, Types::Nominal::String.default( 'root'.freeze )
|
|
4
|
+
attribute :action, Types::Nominal::String.default( "Property changed".freeze )
|
|
5
|
+
attribute :date, Types::Nominal::Date.default( Date.today.freeze )
|
|
6
|
+
attribute :fields?, Types::Nominal::Hash
|
|
7
|
+
|
|
8
|
+
# Revision Records are always embedded.
|
|
9
|
+
# create and insert methods return an new ruby record
|
|
10
|
+
def self.create **attr
|
|
11
|
+
new **( { rid: '#0:0', date: Date.today}.merge attr )
|
|
12
|
+
end
|
|
13
|
+
def self.insert **attr
|
|
14
|
+
new **( { rid: '#0:0', date: Date.today}.merge **attr )
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
=begin
|
|
18
|
+
Its not allowed to delete records.
|
|
19
|
+
=end
|
|
20
|
+
def self.delete where: {} , **args
|
|
21
|
+
raise ArcadeQueryError, "Its not possible to delete revision records"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete
|
|
25
|
+
raise ArcadeQueryError, "Its not possible to delete revision records"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/model/vertex.rb
CHANGED
|
@@ -213,6 +213,50 @@ or
|
|
|
213
213
|
nil
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
+
=begin
|
|
217
|
+
----------------------------------- Match ------------------------------------------
|
|
218
|
+
(ClassMethod)
|
|
219
|
+
|
|
220
|
+
Creates a Match-Statement based on the vertex type
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
m = Watchlist.match( symbol: 'iBit', as: :ibit )
|
|
224
|
+
.out( HasUnderlying)
|
|
225
|
+
.node( as: :u )
|
|
226
|
+
|
|
227
|
+
m.to_s
|
|
228
|
+
=> "MATCH { type: watchlist, where: ( symbol='iBit' ), as: ibit }
|
|
229
|
+
.out('has_underlying'){ as: u }
|
|
230
|
+
RETURN ibit,u "
|
|
231
|
+
```
|
|
232
|
+
=end
|
|
233
|
+
def self.match **args
|
|
234
|
+
as = args.delete( :as) || :a
|
|
235
|
+
args = args.delete( :where ) if args.key?( :where )
|
|
236
|
+
Arcade::Match.new type: self, where: args, as: as
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
=begin
|
|
240
|
+
----------------------------------- Match ------------------------------------------
|
|
241
|
+
(InstanceMethod)
|
|
242
|
+
|
|
243
|
+
Creates a Match-Statement based on the current vertex ( i.e as response to a website-request )
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
params[:rid] => "#49:0"
|
|
247
|
+
|
|
248
|
+
m= params[:rid].load_rid.match.out( HasUnterlying ).node( as: :u )
|
|
249
|
+
m.to_s
|
|
250
|
+
=> "MATCH { type: strategie, rid: #49:0 }.out('has_underlying'){ as: u } RETURN u "
|
|
251
|
+
```
|
|
252
|
+
=end
|
|
253
|
+
def match as: nil
|
|
254
|
+
if as.nil?
|
|
255
|
+
Arcade::Match.new vertex: self
|
|
256
|
+
else
|
|
257
|
+
Arcade::Match.new vertex: self, as: as
|
|
258
|
+
end
|
|
259
|
+
end
|
|
216
260
|
|
|
217
261
|
def remove
|
|
218
262
|
db.execute{ "delete vertex #{rid}" }
|
|
@@ -224,7 +268,7 @@ Format: < Classname: Edges, Attributes >
|
|
|
224
268
|
=end
|
|
225
269
|
def to_human
|
|
226
270
|
|
|
227
|
-
in_and_out = "{#{
|
|
271
|
+
in_and_out = coe.then{| i,o| "{#{i}->}{->#{o}}, "}
|
|
228
272
|
|
|
229
273
|
#Default presentation of Arcade::Base::Model-Objects
|
|
230
274
|
|
|
@@ -244,6 +288,13 @@ Format: < Classname: Edges, Attributes >
|
|
|
244
288
|
end
|
|
245
289
|
|
|
246
290
|
|
|
291
|
+
# Count Of Edges (coe)
|
|
292
|
+
# returns an Array: [ count-of-in-edges, count-of-out-edges ]
|
|
293
|
+
def coe
|
|
294
|
+
db.query( "select in().size() as ie, out().size() as oe from #{rid}" ) &.first.values
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
|
|
247
298
|
def refresh
|
|
248
299
|
# force reloading of edges and nodes
|
|
249
300
|
# edges are not cached (now)
|
data/lib/models.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
require_relative "../lib/model/document.rb"
|
|
2
|
+
require_relative "../lib/model/revision_record.rb"
|
|
2
3
|
require_relative "../lib/model/vertex.rb"
|
|
3
4
|
require_relative "../lib/model/edge.rb"
|
|
5
|
+
require_relative "../lib/model/revision.rb"
|
|
4
6
|
require_relative "../lib/model/basicvertex.rb"
|
|
5
7
|
require_relative "../lib/model/basicdocument.rb"
|
|
6
8
|
require_relative "../lib/model/basicedge.rb"
|
|
9
|
+
require_relative "../lib/model/timeseries.rb"
|
data/lib/types.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Types
|
|
2
|
+
include Dry.Types()
|
|
3
|
+
|
|
4
|
+
# include in attribute definitions
|
|
5
|
+
Rid = String.constrained( format: /\A[#]{1}[0-9]{1,}:[0-9]{1,}\z/ )
|
|
6
|
+
Blockchain = String.constrained( format: /^(algo|eth|btc)$/ ) # add other blockchain symbols here
|
|
7
|
+
Email = String.constrained( format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i )
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Define a type for your DateTime with timezone. Crucially, use
|
|
11
|
+
# the `.constructor` to handle the string parsing and potential errors. (source: Gemini)
|
|
12
|
+
Types::DateTime = Types::Any.constructor do |value|
|
|
13
|
+
begin
|
|
14
|
+
case value
|
|
15
|
+
when String
|
|
16
|
+
::DateTime.parse(value)
|
|
17
|
+
when Integer
|
|
18
|
+
::Time.at(value).to_datetime
|
|
19
|
+
else
|
|
20
|
+
::DateTime.now
|
|
21
|
+
end
|
|
22
|
+
rescue ArgumentError
|
|
23
|
+
# Handle invalid date strings. Options:
|
|
24
|
+
# 1. Raise an exception (recommended for API responses)
|
|
25
|
+
raise Dry::Types::CoercionError.new(
|
|
26
|
+
value, DateTime, "Invalid DateTime format: #{value}"
|
|
27
|
+
)
|
|
28
|
+
# 2. Return nil (less strict, but might lead to unexpected behavior)
|
|
29
|
+
# nil
|
|
30
|
+
# 3. Use a default value (be careful with this, as it might mask errors)
|
|
31
|
+
# DateTime.now
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: arcadedb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hartmut Bischoff
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
@@ -134,6 +133,7 @@ files:
|
|
|
134
133
|
- Gemfile.lock
|
|
135
134
|
- Guardfile
|
|
136
135
|
- LICENSE
|
|
136
|
+
- RAG_SCHEMA.md
|
|
137
137
|
- README.md
|
|
138
138
|
- Rakefile
|
|
139
139
|
- arcade.yml
|
|
@@ -141,6 +141,8 @@ files:
|
|
|
141
141
|
- bin/+
|
|
142
142
|
- bin/console
|
|
143
143
|
- examples/books.rb
|
|
144
|
+
- examples/rag_example.rb
|
|
145
|
+
- examples/rag_usecase_demo.rb
|
|
144
146
|
- examples/relation_1__1.rb
|
|
145
147
|
- examples/relation_1__n.rb
|
|
146
148
|
- examples/relation_n_n.rb
|
|
@@ -158,20 +160,26 @@ files:
|
|
|
158
160
|
- lib/arcade/logging.rb
|
|
159
161
|
- lib/arcade/match.rb
|
|
160
162
|
- lib/arcade/query.rb
|
|
163
|
+
- lib/arcade/rag_schema.rb
|
|
161
164
|
- lib/arcade/support/class.rb
|
|
162
165
|
- lib/arcade/support/conversions.rb
|
|
163
166
|
- lib/arcade/support/model.rb
|
|
164
167
|
- lib/arcade/support/object.rb
|
|
165
168
|
- lib/arcade/support/sql.rb
|
|
166
169
|
- lib/arcade/support/string.rb
|
|
170
|
+
- lib/arcade/timeseries.rb
|
|
167
171
|
- lib/arcade/version.rb
|
|
168
172
|
- lib/model/basicdocument.rb
|
|
169
173
|
- lib/model/basicedge.rb
|
|
170
174
|
- lib/model/basicvertex.rb
|
|
171
175
|
- lib/model/document.rb
|
|
172
176
|
- lib/model/edge.rb
|
|
177
|
+
- lib/model/revision.rb
|
|
178
|
+
- lib/model/revision_record.rb
|
|
179
|
+
- lib/model/timeseries.rb
|
|
173
180
|
- lib/model/vertex.rb
|
|
174
181
|
- lib/models.rb
|
|
182
|
+
- lib/types.rb
|
|
175
183
|
- rails.md
|
|
176
184
|
- rails/arcade.rb
|
|
177
185
|
- rails/config.yml
|
|
@@ -180,7 +188,6 @@ homepage: https://github.com/topofocus/arcadedb
|
|
|
180
188
|
licenses:
|
|
181
189
|
- MIT
|
|
182
190
|
metadata: {}
|
|
183
|
-
post_install_message:
|
|
184
191
|
rdoc_options: []
|
|
185
192
|
require_paths:
|
|
186
193
|
- lib
|
|
@@ -195,8 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
195
202
|
- !ruby/object:Gem::Version
|
|
196
203
|
version: '0'
|
|
197
204
|
requirements: []
|
|
198
|
-
rubygems_version:
|
|
199
|
-
signing_key:
|
|
205
|
+
rubygems_version: 4.0.8
|
|
200
206
|
specification_version: 4
|
|
201
207
|
summary: Ruby Interface to ArcadeDB
|
|
202
208
|
test_files: []
|