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.
data/README.md CHANGED
@@ -1,317 +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
20
+ bundle init && bundle add arcadedb
21
+ mkdir model bin
22
+ # copy bin/console and arcade.yml from this repo, adapt arcade.yml
25
23
  ```
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
24
 
29
- ## Console
25
+ `arcade.yml` (project root or `config/`):
30
26
 
31
- To start an interactive console, a script is provided in the bin-directory.
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
32
34
  ```
33
- $ cd bin && ./console.rb t ( or "d" or "p" for Test, Development and Production environment)
34
35
 
35
- **in Test environment Database definitions (model-files) of the test-suite are included!**
36
- ```
37
-
38
- ## Add to a project
39
- Just require it in your program:
40
- ```ruby
41
- require "arcade"
42
- ```
43
- ## Config
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`.
44
38
 
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.
39
+ ## Architecture
46
40
 
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+)
47
48
 
48
- ## Examples & Specs
49
-
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.
53
-
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, but have to be declared in the model-file.
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.create name: 'Hubert', age: '25'
101
- f = Person.create 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, to.
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, however the class has changed to `Arcade::Query`.
135
-
136
- ## Match
137
96
 
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) seems to be very intuitive, a preprocessor should not be
139
- necessary. With the help of some ruby magic, the creation of match-statements is simply an no-brainer.
97
+ MATCH generator — also from a vertex instance (handy when `params[:rid]` is given):
140
98
 
141
99
  ```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.
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)
149
103
  ```
150
104
 
151
- Arcade::Match-objects can be used as `from:` argument to Arcade::Query-Statements, thus hybrid queries are
152
- easily constructed without extensive string-manipulations.
153
-
154
- The **second Layer** handles Database-Requests.
155
- In its actual implementation, these requests are delegated to the HTTP/JSON-API.
156
-
157
- `Arcade::Init` uses the database specification of the `arcade.yml` file.
158
- The database-handle is always present through `Arcade::Init.db`
105
+ Query objects nest (hybrid queries without string surgery):
159
106
 
160
107
  ```ruby
161
- DB = Arcade::Init.db
162
-
163
- $ DB.get nn, mm # returns a Aracde:Base object
164
- # rid is either "#11:10:" or two numbers
165
- $ DB.query querystring # returns either an Array of results (as Hash)
166
- $ DB.execute { querystring } # execute a non idempotent query within a (nested) transaction
167
- $ DB.create <name>, attribute: value .... # Creates a new <Document | Vertex> and returns the rid
168
- # Operation is performed as Database-Transaction and is rolled back on error
169
- $ DB.insert <name>, attribute: value .... # Inserts a new <Document | Vertex> and returns the new object
170
- $ DB.create_edge <name>, from: <rid> or [rid, rid, ..] , to: <rid> or [rid, rid, ..]
171
-
108
+ Query.new(from: match_object, projection: 'median(note_count)', where: '$depth>=50')
172
109
  ```
173
110
 
174
- **Convert database input to Arcade::Base Models**
111
+ ## TimeSeries (new in 0.5.4)
175
112
 
176
- Either `DB.query` or `DB.execute` return the raw JSON-input from the database. It can always converted to model-objects by chaining
177
- `allocate_model` or `select_result`.
178
-
179
- ```ruby
180
- $ DB.query "select from my_names limit 1"
181
- # which is identical to
182
- $ My::Names.query( limit: 1).query
183
- => [{:@out=>0, :@rid=>"#225:6", :@in=>0, :@type=>"my_names", :@cat=>"v", :name=>"Zaber"}]
184
- # then
185
- $ My::Names.query( limit: 1).query.allocate_model.to_human
186
- => ["<my_names[#225:6]: name: Zaber>" ]
187
- # replaces the hash with a My::Names Object
188
- ```
189
-
190
- 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.
191
115
 
192
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
193
123
 
194
- $ Arcade::Api.databases # returns an array of known databases
195
- $ Arcade::Api.create_database <a string> # returns true if successfull
196
- $ Arcade::Api.drop_database <a string> # returns true if successfull
124
+ Ohlcv.create_type
197
125
 
198
- $ Arcade::Api.begin_transaction <database>
199
- $ Arcade::Api.create_document <database>, <type>, attribute: value , ...
200
- $ Arcade::Api.execute( <database> ) { <query> }
201
- $ 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
202
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
203
134
 
204
- $ Arcade::Api.query( <database> ) { <query> }
205
- $ 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 }
206
138
  ```
207
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.
208
143
 
209
- `<query>` is either a string or a hash:
210
- ```ruby
211
- { :query => "<the query string> ",
212
- :language => one of :sql, :cypher, :gmelin: :neo4 ,
213
- :params => a hash of parameters,
214
- :limit => a number ,
215
- :serializer => one of :graph, :record }
216
- ```
217
-
218
- ## ORM Behavior
219
-
220
- Simple tasks are implemented on the model layer.
144
+ ## RAG
221
145
 
222
- ### Ensure that a Record exists
146
+ `require 'arcade/rag_schema'` — vector + full-text schema for retrieval pipelines.
147
+ See `RAG_SCHEMA.md` and `examples/rag_example.rb`.
223
148
 
224
- The `upsert` command either updates or creates a database record.
149
+ ## Transactions & audit
225
150
 
226
151
  ```ruby
227
- User.upsert name: 'Hugo'
228
- # or
229
- User.upsert set: { age: 46 }, where: { name: 'Hugo' }
152
+ db = Arcade::Init.db
153
+ db.begin_transaction; ... ; db.commit # or db.rollback
230
154
  ```
231
- either creates the record (and returns it) or returns the existing database entry. Obviously, the addressed attribute (where condition)
232
- must have a proper index.
233
155
 
234
- The `upsert` statement provides a smart method to ensure the presence of a defined starting point.
156
+ `Arcade::Revision` subclasses keep an append-only `protocol` stack of
157
+ `Arcade::RevisionRecord` entries; every `update` appends (old value + action + user).
235
158
 
236
- ### Assign Nodes to a Vertex
159
+ ## Low-level access
237
160
 
238
- Apart from accessing attributes by their method-name, adjacent edges and notes are fetched through
239
-
240
- ```ruby
241
- new_vertex = ->(n) { Node.create( note_count: n ) } ## lambda to create a Node type record
242
- nucleus = BaseNode.create item: 'b' ## create a start node
243
- (1..10).each{ |n| nucleus.assign( via: Connects, vertex: new_vertex[n]) } ## connect nodes via Connects-Edges
244
- ```
245
- After creating a star-like structure, the environment can be explored
246
- ```ruby
247
- nucleus.edges.to_human
248
- =>["<connects[#80:14] :.: #4:0->{}->#34:2>",
249
- "<connects[#79:13] :.: #4:0->{}->#31:1>",
250
- ( ... )
251
- "<connects[#79:14] :.: #4:0->{}->#31:2>"]
252
-
253
- nucleus.nodes.to_human
254
- => ["<node[#34:2]: item: 10>",
255
- "<node[#31:1]: item: 1>",
256
- ( ... )
257
- "<node[#31:2]: item: 9>"]
258
- ```
259
-
260
- Edges provide a `vertices`-method to load connected ones. Both vertices and edges expose `in`, `out`, `both`-methods to select connections further.
261
- Specific edge-classes (types) are provided with the `via:` parameter, as shown in `assign` above.
262
-
263
-
264
- ### Traverse Facility
265
-
266
- To ease queries to the graph database, `Arcade::Query` supports traversal of nodes
267
-
268
- Create a Chain of Nodes:
269
161
  ```ruby
270
-
271
- def linear_elements start, count #returns the edge created
272
- new_vertex = ->(n) { Arcade::ExtraNode.create( note_count: n ) }
273
- (2..count).each{ |n| start = start.assign vertex: new_vertex[n], via: Arcade::Connects }
274
- end
275
-
276
- start_node = Arcade::ExtraNode.create( item: 'linear' )
277
- 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
278
167
  ```
279
168
 
280
- 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 }`.
281
170
 
282
- ```ruby
283
- hundred_elements = start_node.traverse :out, via: Arcade::Connects, depth: 100
284
- median = Query.new from: hundred_elements,
285
- projection: 'median(note_count)',
286
- where: '$depth>=50'
287
- median.to_s
288
- -=> select median(note_count) from ( traverse out(connects) from #52:0 while $depth < 100 ) where $depth>=50
289
- => {:"median(note_count)"=>75.5 }
290
- ```
171
+ ## Server & version notes (verified ArcadeDB 26.10)
291
172
 
292
- ### 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 |
293
183
 
294
- Database-Transactions are largely encapsulated
184
+ ## Testing
295
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
296
189
  ```
297
- db = Arcade::Init.db
298
- db.begin_transaction
299
-
300
- { perform insert, update, query, etc tasks in the database }
301
190
 
302
- db.commit
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.
303
193
 
304
- # or
305
-
306
- db.rollback
194
+ ## Repository layout
307
195
 
308
196
  ```
309
-
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
204
+ ```
310
205
 
311
206
  ## Contributing
312
207
 
313
- 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.
314
-
315
- ## Code of Conduct
316
-
317
- 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.
data/arcade.yml CHANGED
@@ -3,20 +3,20 @@
3
3
  :test:
4
4
  dbname: playground
5
5
  user: root
6
- pass: ***
6
+ pass: topo1focus
7
7
  :development:
8
8
  dbname: devel
9
9
  user: root
10
- pass: ***
10
+ pass: topo1focus
11
11
  :production:
12
12
  dbname: production
13
13
  user: root
14
- pass: ***
14
+ pass: topo1focus
15
15
  :admin:
16
- :host: 10.247.8.109
16
+ :host: localhost
17
17
  :port: 2480
18
18
  :user: root
19
- :pass: ***
19
+ :pass: topo1focus
20
20
  :logger: stdout # 'file' or 'stdout'
21
21
  :namespace: Arcade # Default Namespace
22
22
  :autoload: true # load model if a link is detected in a record
data/bin/console CHANGED
@@ -9,6 +9,7 @@
9
9
  require 'bundler/setup'
10
10
  #require 'terminal-table'
11
11
  require 'zeitwerk'
12
+ ProjectRoot = Pathname.new( Dir.pwd ).parent
12
13
  require 'pastel'
13
14
  require 'arcade'
14
15
  #require 'pry'
@@ -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