arcadedb 0.5.2 → 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.
data/README.md CHANGED
@@ -1,392 +1,212 @@
1
- # ArcadeDB
1
+ # ArcadeDB — Ruby Interface
2
2
 
3
- Ruby Interface to a [Arcade Database](https://arcadedb.com/).
3
+ Ruby interface to [ArcadeDB](https://arcadedb.com/) via the HTTP-JSON API.
4
+ Responses map to a Dry::Struct based ORM: each database type is a model class; graph,
5
+ document and time-series queries are expressed in ruby. Includes a Query preprocessor
6
+ and a MATCH statement generator.
4
7
 
5
- The program utilizes the HTTP-JSON API to direct database queries to an ArcadeDB server.
6
- The server's response is then mapped to an ORM (Object-Relational Mapping) based on DRY::Struct.
7
- Each database type is represented by a dedicated Model Class, where complex queries are encapsulated.
8
- The program also includes a Query-Preprocessor and Match-statment generator for constructing custom queries in ruby fashion.
8
+ | | |
9
+ |---|---|
10
+ | Namespace | `Arcade` |
11
+ | Requires | Ruby ≥ 2.7, running ArcadeDB server (tested against 26.10.x) |
12
+ | Transport | HTTP/JSON (HTTPX, persistent connection, basic auth) |
13
+ | Model layer | Dry::Struct — not ActiveRecord |
14
+ | Sibling docs | `RAG_SCHEMA.md` (vector schema), `rails.md`, `CHANGELOG.md` |
9
15
 
10
- ***ArcadeDB internally uses `Arcade` as primary namespace***
16
+ ## Quick start
11
17
 
12
- ## Prerequisites
13
-
14
- A running AracdeDB-Instance. [Quick-Start-Guide](https://docs.arcadedb.com/#Quick-Start-Docker).
15
-
16
- ## New Project
17
-
18
- ```
18
+ ```bash
19
19
  mkdir project && cd project
20
- bundle init
21
- bundle add arcadedb
22
- bundle add pastel # for console output
23
- mkdir model
24
- mkdir bin
25
- ```
26
- copy `https://github.com/topofocus/arcadedb/blob/main/bin/console` to the `bin` directory
27
- copy `https://github.com/topofocus/arcadedb/blob/main/arcade.yml` to the project root and modify to your needs
28
-
29
- ## Console
30
-
31
- To start an interactive console, a script is provided in the bin-directory.
20
+ bundle init && bundle add arcadedb
21
+ mkdir model bin
22
+ # copy bin/console and arcade.yml from this repo, adapt arcade.yml
32
23
  ```
33
- $ cd bin && ./console.rb t ( or "d" or "p" for Test, Development and Production environment)
34
24
 
35
- **in Test environment Database definitions (model-files) of the test-suite are included!**
36
- ```
25
+ `arcade.yml` (project root or `config/`):
37
26
 
38
- ## Add to a project
39
- Just require it in your program:
40
- ```ruby
41
- require "arcade"
27
+ ```yaml
28
+ :environment:
29
+ :test: { dbname: playground, user: root, pass: xxx }
30
+ :development: { dbname: devel, user: root, pass: xxx }
31
+ :production: { dbname: production, user: root, pass: xxx }
32
+ :admin: { :host: localhost, :port: 2480, :user: root, :pass: xxx }
33
+ :logger: stdout
42
34
  ```
43
- ## Config
44
-
45
- Add a file `arcade.yml` at the root of your program or in the `config`-dir and provide suitable databases for test, development and production environment.
46
35
 
36
+ Console: `bin/console t|d|p` (test environment also loads the spec model files).
37
+ Connection handle: always available as `Arcade::Init.db` after `Arcade::Init.connect :production`.
47
38
 
48
- ## Examples & Specs
39
+ ## Architecture
49
40
 
50
- The `example` directory contains documented sample files for typical usecases
51
-
52
- The `spec`-files in the rspec-test-suite-section are worth reading, too.
41
+ ```
42
+ Arcade::Base (Dry::Struct) model layer
43
+ ├── Arcade::Vertex +nodes/edges/traverse/match
44
+ ├── Arcade::Edge
45
+ ├── Arcade::Document
46
+ ├── Arcade::Revision / RevisionRecord audit trail
47
+ └── Arcade::Timeseries native time-series types (0.5.4+)
53
48
 
54
- ## Implementation
49
+ Arcade::Database db.query / db.execute / db.transmit (transactions, model allocation)
50
+ Arcade::Api raw HTTP primitives (query, execute, post_text, get_json, ...)
51
+ ```
55
52
 
56
- The adapter uses a 3 layer concept.
53
+ Class name maps to database type via `snake_case` (`Demo::User` → `demo_user`).
57
54
 
58
- Top-Layer : `Arcade::Base`-Model-Objects.
59
- Similar to ActiveRecord Model Objects but based on [Dry-Struct](https://dry-rb.org/gems/dry-struct/1.0/).
55
+ ## Models
60
56
 
61
57
  ```ruby
62
- # Example model file /model/demo/user.rb
58
+ # model/demo/user.rb
63
59
  module Demo
64
60
  class User < Arcade::Vertex
65
61
  attribute :name, Types::Nominal::String
66
-
62
+
67
63
  def grandparents
68
- db.query( "select in('is_family') from #{rid} ") &.allocate_model
64
+ db.query("select in('is_family') from #{rid}")&.allocate_model
69
65
  end
70
66
  end
71
67
  end
72
68
  __END__
73
69
  CREATE PROPERTY demo_user.name STRING
74
- CREATE INDEX on demo_user( name ) UNIQUE
70
+ CREATE INDEX ON demo_user(name) UNIQUE
75
71
  ```
76
72
 
77
- Only the `name` attribute is declared.
78
-
79
- `Demo::User.create_type` creates the type and executes provided database-commands after `__END__`.
80
-
81
- Other properties are schemaless. They are present in the database and are mapped to the `value`-attribute on the ruby side.
73
+ `Demo::User.create_type` creates the type and runs the `__END__` DDL.
74
+ Undeclared properties are schemaless — they arrive in the `values` attribute.
82
75
 
83
76
  ```ruby
84
77
  Person.insert name: "Hubert", age: 35
85
78
  Person.update set: { age: 36 }, where: { name: 'Hubert' }
86
- persons = Person.where "age > 40"
87
- persons.first.update age: 37
88
- persons.first.update father: Person.create( name: 'Mike', age: 94 )
89
-
90
- Person.all
91
- Person.delete all: true || where: age: 56 , ...
92
- ```
93
-
94
- ### Nodes and Traverse
95
-
96
- `ArcadeDB` wraps common queries of bidirectional connected vertices.
97
-
98
- Suppose
99
- ```ruby
100
- m = Person.insert name: 'Hubert', age: 25
101
- f = Person.insert name: 'Pauline', age: 28
102
- m.assign via: IsMarriedTo, vertex: f , divorced: false
103
-
79
+ Person.where("age > 40").first.update age: 37
80
+ Person.upsert name: 'Hugo' # ensure-exists (needs index on field)
81
+ Person.delete all: true # or where: {...}
104
82
  ```
105
- This creates the simple graph
106
- > Hubert --- is_married_to --> Pauline
107
- > |
108
- > -- divorced (attribute on the edge)
109
83
 
110
- This can be queried through
84
+ ## Graph: nodes, traverse, match
111
85
 
112
86
  ```ruby
113
- hubert = Person.find name: 'Hubert'
114
- pauline = hubert.nodes( :out, via: IsMarriedTo ).first
115
- # or simply
116
- pauline = hubert.out.first
87
+ m = Person.insert(name: 'Hubert'); f = Person.insert(name: 'Pauline')
88
+ m.assign via: IsMarriedTo, vertex: f, divorced: false
117
89
 
118
- ```
119
- Conditions may be set, too.
120
- ```ruby
121
- hubert.nodes( :out, via: IsMarriedTo, where: "age < 30" )
122
- ```
123
- gets all wives of hubert, who are younger then 30 years.
124
- or
125
- ```ruby
126
- Person.nodes( :outE, via: IsMarriedTo, where: { divorced: false } )
90
+ hubert = Person.find(name: 'Hubert')
91
+ hubert.nodes(:out, via: IsMarriedTo, where: "age < 30").first # or hubert.out.first
92
+ hubert.edges(via: IsMarriedTo) # edges carry attributes
127
93
 
94
+ start_node.traverse :out, via: Connects, depth: 100 # depth-limited walk
128
95
  ```
129
- gets all wives where the divorced-condition, which is set on the edge, is false.
130
-
131
- ### Query
132
-
133
- A **Query Preprocessor** is implemented. Its adapted from ActiveOrient. The [documentation](https://github.com/topofocus/active-orient/wiki/OrientQuery)
134
- is still valid, but the class has changed to `Arcade::Query`.
135
-
136
- ### Match
137
-
138
- A simple **Match Statement Generator** is provided for convenience. As the [declarative syntax](https://github.com/ArcadeData/arcadedb-docs/blob/main/src/main/asciidoc/sql/SQL-Match.adoc) is very intuitive, a preprocessor should not be
139
- necessary. However, with the help of some ruby magic, the creation of match-statements is simply an no-brainer.
140
96
 
141
- ```ruby
142
- a = Arcade::Match.new( type: Person, as: :persons)
143
- .out( IsMarriedTo )
144
- .node( where: 'age < 30')
145
- a.to_s
146
- => Match { type: person, as: persons }.out('is_married_to'){ where: ( age < 30) } RETURN persons
147
-
148
- a.execute.select_results # sends the statement to the database and returns Arcade Objects.
149
- ```
97
+ MATCH generator — also from a vertex instance (handy when `params[:rid]` is given):
150
98
 
151
- To query properties on Edges, `inE`, and `outE` are present.
152
99
  ```ruby
153
- a = Arcade::Match.new( type: Person, as: :persons) -> Match { type: person, as: persons}
154
- a.outE via: IsMarriedTo, where: { year: 1990..2000 } -> .outE('is_married_to'){ where: year between 1990 and 2000}
155
- -> .outV('is_maried_to') ...
156
-
157
- a.outE via: [IsMarriedTo, IsMarriedIn], where: { year: 1990..2000 }, as: :bride
158
- -> .outV('is_married_in') ...
100
+ Arcade::Match.new(type: Person, as: :p).out(IsMarriedTo).node(where: 'age < 30').execute.select_results
101
+ Watchlist.match(symbol: 'iBit', as: :w).out(HasUnderlying).node(as: :u)
102
+ "#49:0".load_rid.match.out(HasUnderlying).node(as: :u)
159
103
  ```
160
104
 
161
- Match statements can also refer to a vertex:
162
- ```ruby
163
- p = Person.find name: 'tux'
164
- c = Person.find name: 'hilde'
165
- a = Arcade::Match.new( vertex: p ) -> Match { type: person, rid: 2:0 }
166
- .out( IsMarriedTo ) -> .out('is_married_to')
167
- .node( as: :wife ) -> { as: wife }
168
- .out( HasChild ) -> .out('has_child')
169
- .node( vertex: c ) -> { type: person, rid: 2:1 }
170
- -> Return wife
171
- ```
172
- fetches the vertex between two known vertices. This is the fastest lookup mechanism if the vertices are prefetched.
173
-
174
- Arcade::Match-objects can be used as `from:` argument to Arcade::Query-Statements; hybrid queries are
175
- easily constructed without extensive string-manipulations.
176
-
177
- #### Vertex based Match statements
178
-
179
- A `match`-method is implemented for Vertex-Classes and vertices.
180
-
181
- The class-method takes any argument (except `as:`) as part of a `where`-condition.
105
+ Query objects nest (hybrid queries without string surgery):
182
106
 
183
107
  ```ruby
184
-
185
- m = Watchlist.match( symbol: 'iBit', as: :ibit )
186
- .out( HasUnderlying)
187
- .node( as: :u )
188
-
189
- m.to_s
190
- => "MATCH { type: watchlist, where: ( symbol='iBit' ), as: ibit }
191
- .out('has_underlying'){ as: u }
192
- RETURN ibit,u "
108
+ Query.new(from: match_object, projection: 'median(note_count)', where: '$depth>=50')
193
109
  ```
194
110
 
195
- The instance-method is extremly handy in web-applications. If the `rid` is part of the `params`-hash,
196
- associated nodes are easily selected
111
+ ## TimeSeries (new in 0.5.4)
197
112
 
198
- ```ruby
199
- params[:rid] => "#49:0"
200
-
201
- m= params[:rid].load_rid.match.out( HasUnterlying ).node( as: :u )
202
- m.to_s
203
- => "MATCH { type: strategie, rid: #49:0 }.out('has_underlying'){ as: u } RETURN u "
204
- ```
205
- ## Low Level Database Requests
206
- The **second Layer** handles Database-Requests.
207
- In its actual implementation, these requests are delegated to the HTTP/JSON-API.
208
-
209
- `Arcade::Init` uses the database specification of the `arcade.yml` file.
210
- The database-handle is always present through `Arcade::Init.db`
211
-
212
- ```ruby
213
- db = Arcade::Init.db
214
-
215
- $ db.get nn, mm # nn=2; mm=0 # returns a Aracde:Base object
216
- # rid is either "#11:10:" or two numbers
217
- $ db.query querystring # returns either an Array of results (as Hash)
218
- $ db.execute { querystring } # execute a non idempotent query within a (nested) transaction
219
- $ db.create <name>, attribute: value .... # Creates a new <Document | Vertex> and returns the rid
220
- # Operation is performed as Database-Transaction and is rolled back on error
221
- $ db.insert <name>, attribute: value .... # Inserts a new <Document | Vertex> and returns the new object
222
- $ db.create_edge <name>, from: <rid> or [rid, rid, ..] , to: <rid> or [rid, rid, ..]
223
-
224
- ```
225
-
226
- **Convert database input to Arcade::Base Models**
227
-
228
- Either `db.query` or `db.execute` return the raw JSON-input from the database. It can always converted to model-objects by chaining
229
- `allocate_model` or `select_result`.
230
-
231
- ```ruby
232
- $ db.query "select from my_names limit 1"
233
- # which is identical to
234
- $ My::Names.query( limit: 1).execute
235
- => [{:@out=>0, :@rid=>"#225:6", :@in=>0, :@type=>"my_names", :@cat=>"v", :name=>"Zaber"}]
236
- # then
237
- $ My::Names.query( limit: 1).execute.allocate_model.to_human
238
- => ["<my_names[#225:6]: name: Zaber>" ]
239
- # replaces the hash with a My::Names Object
240
- ```
241
-
242
-
243
- The **Base Layer** implements a low level access to the database API.
113
+ Native ArcadeDB time-series types: columnar, compressed, tag-partitioned.
114
+ **Not vertices** — no rid, no edges. Membership lives in tags, values in typed fields.
244
115
 
245
116
  ```ruby
117
+ # model/ohlcv.rb
118
+ class Ohlcv < Arcade::Timeseries
119
+ timestamp :ts, precision: :second # date-granular series: use :second
120
+ tags symbol: String, currency: String
121
+ fields open: Float, high: Float, low: Float, close: Float, volume: Integer
122
+ end
246
123
 
247
- $ Arcade::Api.databases # returns an array of known databases
248
- $ Arcade::Api.create_database <a string> # returns true if successfull
249
- $ Arcade::Api.drop_database <a string> # returns true if successfull
124
+ Ohlcv.create_type
250
125
 
251
- $ Arcade::Api.begin_transaction <database>
252
- $ Arcade::Api.create_document <database>, <type>, attribute: value , ...
253
- $ Arcade::Api.execute( <database> ) { <query> }
254
- $ Arcade::Api.commit <database> # or Arcade::Api.rollback
126
+ # write — Line Protocol under the hood, batched
127
+ Ohlcv.write symbol: 'SAPX:GER', currency: 'EUR', ts: Date.new(2026,10,20),
128
+ open: 290.0, high: 296.0, low: 290.0, close: 295.5, volume: 120
129
+ Ohlcv.write_many rows # array of hashes, one POST per batch
255
130
 
131
+ # read — SQL range pushdown
132
+ Ohlcv.range from: '2026-01-01', tags: { symbol: 'SAPX:GER' }
133
+ Ohlcv.latest tags: { symbol: 'SAPX:GER' } # => {ts:, close:, ...} or nil
256
134
 
257
- $ Arcade::Api.query( <database> ) { <query> }
258
- $ Arcade::Api.get_record <database>, rid # returns a hash
135
+ # weekly candles in one query
136
+ Ohlcv.bucket '7d', from: Date.new(2024,10,1), tags: { symbol: 'SAPX:GER' },
137
+ aggregate: { close: :last, high: :max, low: :min, volume: :sum }
259
138
  ```
260
139
 
140
+ Accepted `ts` values: `Time`, `Date`, `DateTime`, ISO-String, epoch-seconds — normalised
141
+ to UTC and to the declared precision. Timezones: server timestamps are stored absolute;
142
+ build Date objects UTC (`Time.utc`) or comparisons drift by the host offset.
261
143
 
262
- `<query>` is either a string or a hash:
263
- ```ruby
264
- { :query => "<the query string> ",
265
- :language => one of :sql, :cypher, :gmelin: :neo4 ,
266
- :params => a hash of parameters,
267
- :limit => a number ,
268
- :serializer => one of :graph, :record }
269
- ```
144
+ ## RAG
270
145
 
271
- ## ORM Behavior
146
+ `require 'arcade/rag_schema'` — vector + full-text schema for retrieval pipelines.
147
+ See `RAG_SCHEMA.md` and `examples/rag_example.rb`.
272
148
 
273
- Simple tasks are implemented on the model layer.
274
-
275
- ### Ensure that a Record exists
276
-
277
- The `upsert` command either updates or creates a database record.
149
+ ## Transactions & audit
278
150
 
279
151
  ```ruby
280
- User.upsert name: 'Hugo'
281
- # or
282
- User.upsert set: { age: 46 }, where: { name: 'Hugo' }
152
+ db = Arcade::Init.db
153
+ db.begin_transaction; ... ; db.commit # or db.rollback
283
154
  ```
284
- either creates the record (and returns it) or returns the existing database entry. Obviously, the addressed attribute (where condition)
285
- must have a proper index.
286
-
287
- The `upsert` statement provides a smart method to ensure the presence of a defined starting point.
288
155
 
289
- ### Assign Nodes to a Vertex
156
+ `Arcade::Revision` subclasses keep an append-only `protocol` stack of
157
+ `Arcade::RevisionRecord` entries; every `update` appends (old value + action + user).
290
158
 
291
- Apart from accessing attributes by their method-name, adjacent edges and notes are fetched through
159
+ ## Low-level access
292
160
 
293
- ```ruby
294
- new_vertex = ->(n) { Node.create( note_count: n ) } ## lambda to create a Node type record
295
- nucleus = BaseNode.create item: 'b' ## create a start node
296
- (1..10).each{ |n| nucleus.assign( via: Connects, vertex: new_vertex[n]) } ## connect nodes via Connects-Edges
297
- ```
298
- After creating a star-like structure, the environment can be explored
299
- ```ruby
300
- nucleus.edges.to_human
301
- =>["<connects[#80:14] :.: #4:0->{}->#34:2>",
302
- "<connects[#79:13] :.: #4:0->{}->#31:1>",
303
- ( ... )
304
- "<connects[#79:14] :.: #4:0->{}->#31:2>"]
305
-
306
- nucleus.nodes.to_human
307
- => ["<node[#34:2]: item: 10>",
308
- "<node[#31:1]: item: 1>",
309
- ( ... )
310
- "<node[#31:2]: item: 9>"]
311
- ```
312
-
313
- Edges provide a `vertices`-method to load connected ones. Both vertices and edges expose `in`, `out`, `both`-methods to select connections further.
314
- Specific edge-classes (types) are provided with the `via:` parameter, as shown in `assign` above.
315
-
316
-
317
- ### Traverse Facility
318
-
319
- To ease queries to the graph database, `Arcade::Query` supports traversal of nodes
320
-
321
- Create a Chain of Nodes:
322
161
  ```ruby
323
-
324
- def linear_elements start, count #returns the edge created
325
- new_vertex = ->(n) { Arcade::ExtraNode.create( note_count: n ) }
326
- (2..count).each{ |n| start = start.assign vertex: new_vertex[n], via: Arcade::Connects }
327
- end
328
-
329
- start_node = Arcade::ExtraNode.create( item: 'linear' )
330
- linear_elements start_node , 200
162
+ Arcade::Api.query(db, "select from x") # parsed :result (array)
163
+ Arcade::Api.execute(db) { "update x set ..." } # write, retry on 'Please retry'
164
+ Arcade::Api.get_json("ts/#{db}/latest?type=...") # raw body (ts endpoints)
165
+ Arcade::Api.post_text("ts/#{db}/write?precision=s", lines) # Line Protocol
166
+ db.query("select ...").allocate_model # hashes -> model objects
331
167
  ```
332
168
 
333
- Select a range of nodes and perform a mathematical operation
169
+ Query payload hash: `{ query:, language: :sql|:cypher|:gremlin|:neo4j, params:, limit:, serializer: :graph|:record }`.
334
170
 
335
- ```ruby
336
- hundred_elements = start_node.traverse :out, via: Arcade::Connects, depth: 100
337
- median = Query.new from: hundred_elements,
338
- projection: 'median(note_count)',
339
- where: '$depth>=50'
340
- median.to_s
341
- -=> select median(note_count) from ( traverse out(connects) from #52:0 while $depth < 100 ) where $depth>=50
342
- => {:"median(note_count)"=>75.5 }
343
- ```
171
+ ## Server & version notes (verified ArcadeDB 26.10)
344
172
 
345
- ### Transactions
173
+ | Observation | Consequence |
174
+ |---|---|
175
+ | `schema:types` does not list timeseries types | existence probe: `POST /ts/{db}/query` → 400 `unknownTypes`/`does not exist` = absent (`type_exists?` uses this) |
176
+ | `CREATE TIMESERIES TYPE` registers asynchronously (~1 s) | `create_type` waits for visibility; a write right after create fails with `unknownTypes` otherwise |
177
+ | `DELETE FROM <ts-type> WHERE ...` reports a count but keeps rows (26.10.1) | no window-replace possible; re-writing a timestamp **appends** — deduplicate on read |
178
+ | timeseries timestamp precision defaults to NANOSECOND | declare `PRECISION` explicitly; numeric literals are interpreted per precision |
179
+ | `bucket` is a reserved word in SQL | aliased in backticks by `bucket()` |
180
+ | `IF NOT EXISTS` unsupported on `CREATE TIMESERIES TYPE` | guarded by `type_exists?` |
181
+ | Line Protocol needs pre-created types (`tsAutoCreateType` off by default) | `write_many` fails with `unknownTypes` if the type is missing |
182
+ | persistent HTTP connections die after ~3 s idle | Primitives renew the handle after 2 s inactivity |
346
183
 
347
- Database-Transactions are largely encapsulated
184
+ ## Testing
348
185
 
186
+ ```bash
187
+ bundle install && bundle exec rspec # needs a reachable ArcadeDB server
188
+ bundle exec rspec spec/lib/timeseries_spec.rb # single file; spec/lib/* against arcade.yml :test db
349
189
  ```
350
- db = Arcade::Init.db
351
- db.begin_transaction
352
-
353
- { perform insert, update, query, etc tasks in the database }
354
-
355
- db.commit
356
190
 
357
- # or
191
+ Model files for specs live in `spec/model/` (Zeitwerk-loaded). The spec suite is the
192
+ authoritative behaviour reference — read `spec/lib/*` before assuming API shape.
358
193
 
359
- db.rollback
194
+ ## Repository layout
360
195
 
361
196
  ```
362
- ### Audit proofed Bookings
363
-
364
- Although ArcadeDB is a mutable database, the ruby-Interface supports basic audit proofed transactions.
365
- Two specialized Database-Types are included:
366
-
367
- * Arcade::RevisionRecord – A document class, intended to be embedded in Revision-Records
368
- * Arcade::Revision – A vertex-class with specialized `insert-` and `update-` methods
369
-
370
- If a vertex is based on `Arcade::Revision`, a `protocol`-property is included. Its initialised with a single `Arcade::RevisionRecord` Object. Each `update` adds an `Arcade::RevisionRecord` to the protocol stack.
371
-
372
- ```ruby
373
- m= Member.insert( name: "Hubert",
374
- surname: "Hugo",
375
- birth: Date.new( 1976,3,15 ) ) { 'record initiated' }
376
-
377
- m.update( birth: Date.new( 1978,3,15 ) ) { "member provided correct date via mail" }
378
-
379
- m.protocol => Date | User | Action | old
380
- --------------------------------------------------------------------------------
381
- => 1.1.2024 | root | record initiated |
382
- => 1.2.2024 | root | member provided correct date via mail | birth: "1976-3-15"
383
-
197
+ lib/arcade.rb entry point (requires below)
198
+ lib/arcade/ base, database, query, match, timeseries, api/, support/
199
+ lib/model/ base classes (vertex, edge, document, revision, timeseries)
200
+ lib/models.rb base-class loader
201
+ examples/ documented use cases
202
+ spec/lib, spec/model rspec suite (authoritative)
203
+ bin/console irb shortcut per environment
384
204
  ```
385
205
 
386
206
  ## Contributing
387
207
 
388
- Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
389
-
390
- ## Code of Conduct
391
-
392
- Everyone interacting in the Core project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/topofocus/arcadedb/blob/master/CODE_OF_CONDUCT.md).
208
+ Bug reports and pull requests are welcome — this project follows the
209
+ [Contributor Covenant](http://contributor-covenant.org) code of conduct
210
+ ([detail](https://github.com/topofocus/arcadedb/blob/master/CODE_OF_CONDUCT.md)).
211
+ New behaviour ships with a spec in `spec/lib/` and, if server-quirk-sensitive, a row
212
+ in the Server & version notes table.
@@ -0,0 +1,72 @@
1
+ # RAG (Retrieval-Augmented Generation) Example
2
+ #
3
+ # This example demonstrates how to use the Arcade::RAGSchema module
4
+ # to create a RAG-compatible document store with vector embeddings.
5
+ #
6
+ # Prerequisites:
7
+ # - A running ArcadeDB server with vector search support
8
+ # - An embedding service (e.g., OpenAI, HuggingFace) to generate embeddings
9
+ #
10
+ # Setup:
11
+ # 1. Create a model that includes Arcade::RAGSchema
12
+ # 2. Call create_type to set up the database schema
13
+ # 3. Insert documents with their embeddings
14
+ # 4. Perform vector or hybrid search
15
+
16
+ require 'bundler/setup'
17
+ require 'arcade'
18
+
19
+ # Example model definition
20
+ module RAG
21
+ class Document < Arcade::Vertex
22
+ include Arcade::RAGSchema
23
+
24
+ attribute :content, Types::String
25
+ attribute :embedding?, Types::Array.of(Types::Float).optional
26
+ attribute :metadata?, Types::Hash.optional
27
+ attribute :source_url?, Types::String.optional
28
+ attribute :created_at?, Types::JSON::DateTime.optional
29
+
30
+ # Override db_init to use RAG schema
31
+ def self.db_init
32
+ rag_db_init(vector_dimension: 1536, full_text_fields: ['content', 'metadata'])
33
+ end
34
+ end
35
+ end
36
+
37
+ if $0 == __FILE__
38
+ # Connect to database
39
+ Arcade::Init.connect('test')
40
+
41
+ # Create the type with RAG schema
42
+ RAG::Document.create_type
43
+
44
+ # Example: Insert a document with embedding
45
+ # In practice, you would generate the embedding using an external service
46
+ sample_embedding = Array.new(1536) { rand } # Replace with actual embedding
47
+
48
+ doc = RAG::Document.create(
49
+ content: "ArcadeDB is a multi-model graph database built from the ground up.",
50
+ embedding: sample_embedding,
51
+ metadata: { author: "ArcadeDB Team", tags: ["database", "graph"] },
52
+ source_url: "https://arcadedb.com",
53
+ created_at: DateTime.now
54
+ )
55
+
56
+ puts "Created document: #{doc.rid}"
57
+
58
+ # Example: Vector similarity search
59
+ # query_embedding = generate_embedding("What is a graph database?")
60
+ # results = RAG::Document.vector_search(query_embedding, limit: 5, threshold: 0.7)
61
+ # puts "Found #{results.count} similar documents"
62
+
63
+ # Example: Hybrid search (vector + full-text)
64
+ # results = RAG::Document.hybrid_search(
65
+ # query_embedding,
66
+ # "multi-model graph database",
67
+ # limit: 5
68
+ # )
69
+ # puts "Found #{results.count} hybrid search results"
70
+
71
+ puts "RAG schema setup complete!"
72
+ end