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.
@@ -0,0 +1,345 @@
1
+ # RAG Use Case Demo - Knowledge Base Search
2
+ #
3
+ # This example demonstrates a complete RAG (Retrieval-Augmented Generation) workflow
4
+ # using a knowledge base of technical documentation.
5
+ #
6
+ # Features demonstrated:
7
+ # - Document ingestion with vector embeddings
8
+ # - Vector similarity search for semantic queries
9
+ # - Hybrid search combining vector + full-text
10
+ # - Category and tag-based filtering
11
+ # - Real-world query scenarios
12
+ #
13
+ # Run with: ruby examples/rag_usecase_demo.rb
14
+
15
+ require 'bundler/setup'
16
+ require 'arcade'
17
+
18
+ # =============================================================================
19
+ # Model Definition
20
+ # =============================================================================
21
+
22
+ module UseCase
23
+ class KnowledgeDocument < Arcade::Vertex
24
+ include Arcade::Ragschema
25
+
26
+ attribute :title, Types::String
27
+ attribute :content, Types::String
28
+ attribute :embedding?, Types::Array.of(Types::Float).optional
29
+ attribute :category, Types::String.optional
30
+ attribute :tags, Types::Array.of(Types::String).optional
31
+ attribute :author, Types::String.optional
32
+ attribute :source_url, Types::String.optional
33
+ attribute :created_at, Types::DateTime.optional
34
+ attribute :updated_at, Types::DateTime.optional
35
+
36
+ def self.db_init
37
+ rag_db_init(
38
+ vector_dimension: 1536,
39
+ full_text_fields: ['title', 'content', 'category']
40
+ )
41
+ end
42
+ end
43
+
44
+ # =============================================================================
45
+ # Helper for generating mock embeddings (for demo purposes)
46
+ # In production, use a real embedding service (OpenAI, HuggingFace, etc.)
47
+ # =============================================================================
48
+
49
+ module EmbeddingHelper
50
+ ##
51
+ # Generate a deterministic mock embedding for demonstration
52
+ #
53
+ # @param text [String] Text to embed
54
+ # @param seed [Integer] Seed for deterministic generation
55
+ # @return [Array<Float>] 1536-dimensional vector
56
+ def self.generate_mock_embedding(text, seed: 42)
57
+ base_hash = (text + seed.to_s).hash
58
+ vector = []
59
+ 1536.times do |i|
60
+ value = ((base_hash * (i + 1) * 31) % 10000) / 10000.0 - 0.5
61
+ vector << value
62
+ end
63
+ vector
64
+ end
65
+ end
66
+ end
67
+
68
+ # =============================================================================
69
+ # Demo Data - Technical Knowledge Base
70
+ # =============================================================================
71
+
72
+ KNOWLEDGE_BASE = [
73
+ {
74
+ title: 'Introduction to Graph Databases',
75
+ content: 'Graph databases are designed to store and treat relationships between data entities as first-class citizens. Unlike relational databases, graph databases use nodes and edges to represent and store data. This makes them ideal for analyzing complex relationships and patterns in data.',
76
+ category: 'Database Fundamentals',
77
+ tags: ['graph', 'database', 'nosql', 'fundamentals'],
78
+ author: 'Database Team'
79
+ },
80
+ {
81
+ title: 'ArcadeDB Overview',
82
+ content: 'ArcadeDB is a multi-model graph database built from the ground up with support for vertices, edges, documents, arrays, and vectors. It provides a SQL-like query language and supports RAG (Retrieval-Augmented Generation) workloads with native vector search capabilities.',
83
+ category: 'Database Systems',
84
+ tags: ['arcadedb', 'graph', 'vector', 'rag'],
85
+ author: 'ArcadeDB Team'
86
+ },
87
+ {
88
+ title: 'Vector Search Fundamentals',
89
+ content: 'Vector search, also known as semantic search, uses mathematical representations of data called embeddings. These embeddings capture the semantic meaning of text, allowing for similarity-based searches that understand context rather than just keyword matching.',
90
+ category: 'Search Technology',
91
+ tags: ['vector', 'embedding', 'similarity', 'ai'],
92
+ author: 'AI Research Team'
93
+ },
94
+ {
95
+ title: 'RAG Architecture Patterns',
96
+ content: 'Retrieval-Augmented Generation (RAG) combines retrieval-based and generation-based approaches. It first retrieves relevant documents from a knowledge base using vector similarity, then uses a language model to generate responses based on the retrieved context.',
97
+ category: 'AI Architecture',
98
+ tags: ['rag', 'llm', 'retrieval', 'generation'],
99
+ author: 'ML Engineering'
100
+ },
101
+ {
102
+ title: 'SQL Query Optimization',
103
+ content: 'Query optimization in SQL databases involves techniques like indexing, query planning, and execution strategy selection. Proper indexing strategies can dramatically improve query performance, especially for complex joins and aggregations.',
104
+ category: 'Database Performance',
105
+ tags: ['sql', 'optimization', 'indexing', 'performance'],
106
+ author: 'Performance Team'
107
+ },
108
+ {
109
+ title: 'NoSQL vs SQL Databases',
110
+ content: 'NoSQL databases offer flexible schemas and horizontal scaling, making them suitable for unstructured data and high-velocity applications. SQL databases provide ACID transactions and structured querying capabilities. The choice depends on your specific use case requirements.',
111
+ category: 'Database Comparison',
112
+ tags: ['nosql', 'sql', 'comparison', 'architecture'],
113
+ author: 'Architecture Team'
114
+ },
115
+ {
116
+ title: 'Full-Text Search Implementation',
117
+ content: 'Full-text search indexes allow for efficient text querying beyond simple LIKE matches. They support tokenization, stemming, and relevance scoring. Modern search engines combine full-text search with vector embeddings for hybrid search capabilities.',
118
+ category: 'Search Technology',
119
+ tags: ['fulltext', 'search', 'indexing', 'hybrid'],
120
+ author: 'Search Team'
121
+ },
122
+ {
123
+ title: 'Embedding Models for Text',
124
+ content: 'Text embedding models like BERT, Sentence Transformers, and OpenAI embeddings convert text into dense vectors. These vectors capture semantic relationships, enabling tasks like semantic search, clustering, and similarity detection.',
125
+ category: 'Machine Learning',
126
+ tags: ['embedding', 'bert', 'nlp', 'transformers'],
127
+ author: 'NLP Team'
128
+ }
129
+ ].freeze
130
+
131
+ # =============================================================================
132
+ # Demo Functions
133
+ # =============================================================================
134
+
135
+ def print_header(title)
136
+ puts "\n" + '=' * 70
137
+ puts title.center(70)
138
+ puts '=' * 70
139
+ end
140
+
141
+ def print_section(title)
142
+ puts "\n--- #{title} ---\n"
143
+ end
144
+
145
+ def print_document(doc, show_similarity: false)
146
+ puts " Title: #{doc.title}"
147
+ puts " Category: #{doc.category}"
148
+ puts " Author: #{doc.author}"
149
+ puts " Tags: #{doc.tags.join(', ')}"
150
+ puts " Content: #{doc.content[0..150]}..."
151
+ puts " Similarity: #{doc.similarity.round(4)}" if doc.respond_to?(:similarity) && doc.similarity
152
+ puts
153
+ end
154
+
155
+ def load_knowledge_base
156
+ print_section('Loading Knowledge Base')
157
+
158
+ KNOWLEDGE_BASE.each do |doc_data|
159
+ embedding = UseCase::EmbeddingHelper.generate_mock_embedding(
160
+ doc_data[:title] + ' ' + doc_data[:content]
161
+ )
162
+
163
+ UseCase::KnowledgeDocument.create(
164
+ title: doc_data[:title],
165
+ content: doc_data[:content],
166
+ embedding: embedding,
167
+ category: doc_data[:category],
168
+ tags: doc_data[:tags],
169
+ author: doc_data[:author],
170
+ created_at: DateTime.now
171
+ )
172
+
173
+ puts " ✓ Loaded: #{doc_data[:title]}"
174
+ end
175
+
176
+ total = UseCase::KnowledgeDocument.count
177
+ puts "\nTotal documents: #{total}"
178
+ end
179
+
180
+ def demo_vector_search
181
+ print_section('Vector Similarity Search')
182
+
183
+ queries = [
184
+ 'What is a graph database and how does it work?',
185
+ 'How does RAG architecture work?',
186
+ 'Explain vector embeddings for text search'
187
+ ]
188
+
189
+ queries.each do |query|
190
+ puts "\nQuery: \"#{query}\""
191
+ puts "-" * 50
192
+
193
+ query_embedding = UseCase::EmbeddingHelper.generate_mock_embedding(query)
194
+
195
+ results = UseCase::KnowledgeDocument.vector_search(
196
+ query_embedding,
197
+ limit: 3,
198
+ threshold: 0.0
199
+ )
200
+
201
+ results.each_with_index do |doc, i|
202
+ puts " [#{i + 1}] #{doc.title} (similarity: #{doc.similarity.round(4) if doc.similarity})"
203
+ end
204
+ end
205
+ end
206
+
207
+ def demo_hybrid_search
208
+ print_section('Hybrid Search (Vector + Full-Text)')
209
+
210
+ searches = [
211
+ { query: 'database performance', text: 'optimization' },
212
+ { query: 'search technology', text: 'Full-Text' },
213
+ { query: 'machine learning', text: 'embedding' }
214
+ ]
215
+
216
+ searches.each do |search|
217
+ puts "\nVector Query: \"#{search[:query]}\""
218
+ puts "Text Filter: \"#{search[:text]}\""
219
+ puts "-" * 50
220
+
221
+ query_embedding = UseCase::EmbeddingHelper.generate_mock_embedding(search[:query])
222
+
223
+ results = UseCase::KnowledgeDocument.hybrid_search(
224
+ query_embedding,
225
+ search[:text],
226
+ limit: 5,
227
+ vector_threshold: 0.0
228
+ )
229
+
230
+ if results.empty?
231
+ puts " No results found"
232
+ else
233
+ results.each_with_index do |doc, i|
234
+ puts " [#{i + 1}] #{doc.title}"
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ def demo_category_filtering
241
+ print_section('Category-Based Filtering')
242
+
243
+ categories = ['Search Technology', 'Database Systems', 'Machine Learning']
244
+
245
+ categories.each do |category|
246
+ puts "\nCategory: #{category}"
247
+ puts "-" * 50
248
+
249
+ results = UseCase::KnowledgeDocument.where("category = '#{category}'")
250
+
251
+ results.each do |doc|
252
+ puts " • #{doc.title} (#{doc.author})"
253
+ end
254
+ end
255
+ end
256
+
257
+ def demo_tag_search
258
+ print_section('Tag-Based Search')
259
+
260
+ tags = ['vector', 'graph', 'embedding']
261
+
262
+ tags.each do |tag|
263
+ puts "\nTag: #{tag}"
264
+ puts "-" * 50
265
+
266
+ results = UseCase::KnowledgeDocument.where("tags CONTAINS '#{tag}'")
267
+
268
+ results.each do |doc|
269
+ puts " • #{doc.title} [#{doc.tags.join(', ')}]"
270
+ end
271
+ end
272
+ end
273
+
274
+ def demo_real_world_queries
275
+ print_section('Real-World Query Scenarios')
276
+
277
+ scenarios = {
278
+ 'What is ArcadeDB?' => 'Definition query',
279
+ 'difference between SQL and NoSQL' => 'Comparison query',
280
+ 'How to optimize database queries?' => 'How-to query',
281
+ 'Explain RAG benefits' => 'Concept exploration'
282
+ }
283
+
284
+ scenarios.each do |query, type|
285
+ puts "\n[#{type}] \"#{query}\""
286
+ puts "-" * 50
287
+
288
+ query_embedding = UseCase::EmbeddingHelper.generate_mock_embedding(query)
289
+
290
+ results = UseCase::KnowledgeDocument.vector_search(
291
+ query_embedding,
292
+ limit: 2,
293
+ threshold: 0.0
294
+ )
295
+
296
+ results.each do |doc|
297
+ puts " → #{doc.title}"
298
+ end
299
+ end
300
+ end
301
+
302
+ # =============================================================================
303
+ # Main Demo Runner
304
+ # =============================================================================
305
+
306
+ def run_demo
307
+ print_header('RAG Use Case Demo - Knowledge Base Search')
308
+
309
+ # Connect to database
310
+ Arcade::Init.connect('test')
311
+
312
+ # Create type if needed
313
+ unless Arcade::Init.db.types.include?('UseCase_KnowledgeDocument')
314
+ puts 'Creating KnowledgeDocument type...'
315
+ UseCase::KnowledgeDocument.create_type
316
+ end
317
+
318
+ # Clear existing data for fresh demo
319
+ UseCase::KnowledgeDocument.delete_all rescue nil
320
+
321
+ # Run demos
322
+ load_knowledge_base
323
+ demo_vector_search
324
+ demo_hybrid_search
325
+ demo_category_filtering
326
+ demo_tag_search
327
+ demo_real_world_queries
328
+
329
+ print_header('Demo Complete!')
330
+ puts "\nThis demo showcased:"
331
+ puts " ✓ Document ingestion with vector embeddings"
332
+ puts " ✓ Vector similarity search for semantic queries"
333
+ puts " ✓ Hybrid search (vector + full-text)"
334
+ puts " ✓ Category and tag-based filtering"
335
+ puts " ✓ Real-world query scenarios"
336
+ puts "\nFor more information, see:"
337
+ puts " - RAG_SCHEMA.md"
338
+ puts " - spec/lib/usecase/rag_integration_spec.rb"
339
+ puts
340
+ end
341
+
342
+ # Run if executed directly
343
+ if $0 == __FILE__
344
+ run_demo
345
+ end
@@ -32,6 +32,13 @@ module Arcade
32
32
  get_data 'databases'
33
33
  end
34
34
 
35
+ # ------------------------------ database? ------------------------------------------------- #
36
+ # returns true if the database exists #
37
+
38
+ def self.database? name
39
+ get_data "exists/#{name}"
40
+ end
41
+
35
42
  # ------------------------------ create database ------------------------------------------------- #
36
43
  # creates a database if not present #
37
44
  def self.create_database name
@@ -54,23 +61,6 @@ module Arcade
54
61
  logger.fatal "Drop database #{name} through \"POST drop database/#{name}\" failed"
55
62
  raise
56
63
  end
57
- # ------------------------------ create document ------------------------------------------------- #
58
- # adds a document to the specified database table
59
- #
60
- # specify database-fields as hash-type parameters
61
- #
62
- # i.e Arcade::Api.create_document 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
63
- #
64
- # returns the rid of the inserted dataset
65
- #
66
- def self.create_document database, type, session_id: nil, **attributes
67
- payload = { "@type" => type }.merge( attributes )
68
- if session_id.nil?
69
- post_data "document/#{database}", payload
70
- else
71
- post_transaction "document/#{database}", payload, session_id: session_id
72
- end
73
- end
74
64
 
75
65
  # ------------------------------ execute ------------------------------------------------- #
76
66
  # executes a sql-query in the specified database
@@ -94,13 +84,28 @@ module Arcade
94
84
  # ------------------------------ query ------------------------------------------------- #
95
85
  # same for idempotent queries
96
86
  def self.query database, query, session_id: nil
97
- if session_id.nil?
98
- post_data "query/#{database}" , provide_payload(query)
99
- else
100
- post_transaction "query/#{database}" , provide_payload(query), session_id: session_id
101
- end
87
+ # if session_id.nil?
88
+ get_data "query/#{database}/sql/" + provide_payload(query, action: :get)[:query]
89
+ # else
90
+ # post_transaction "query/#{database}" , provide_payload(query), session_id: session_id
91
+ # end
102
92
  end
103
93
 
94
+ # ------------------------------ create Document ------------------------------------------------- #
95
+ # adds a record ( document, vertex or edge ) to the specified database type
96
+ #
97
+ # specify database-fields as hash-type parameters
98
+ #
99
+ # i.e Arcade::Api.create_record 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
100
+ #
101
+ # returns the rid of the inserted dataset
102
+ #
103
+ def self.create_document database, type, session_id: nil, **attributes
104
+ content = "CONTENT #{ attributes.to_json }"
105
+ result = execute( database, session_id: session_id ){ "INSERT INTO #{type} #{content} "}
106
+ result.first[:@rid] # emulate the »old« behavior of the /post/create_document api endpoint
107
+ end
108
+
104
109
  # ------------------------------ get_record ------------------------------------------------- #
105
110
  # fetches a record by providing database and rid
106
111
  # and returns the result as hash
@@ -114,7 +119,7 @@ module Arcade
114
119
  rid = rid.join(':')
115
120
  rid = rid[1..-1] if rid[0]=="#"
116
121
  if rid.rid?
117
- get_data "document/#{database}/#{rid}"
122
+ get_data( "query/#{database}/sql/" + URI.encode_uri_component("select from #{rid}")) &.first
118
123
  else
119
124
  raise Error "Get requires a rid input"
120
125
  end
@@ -181,7 +186,11 @@ module Arcade
181
186
  the_yield.map do | key, value |
182
187
  case key
183
188
  when :query
184
- action == :post ? [ :command, value ] : [ :query, value ]
189
+ if action == :post
190
+ [ :command, value ]
191
+ else
192
+ [ :query, URI.encode_uri_component(value )]
193
+ end
185
194
  when :limit
186
195
  [ :limit , value ]
187
196
  when :params
@@ -9,6 +9,12 @@ module Arcade
9
9
 
10
10
  def http
11
11
  # break_on = -> (response) { response.status == 500 }
12
+ # Version 23.12: Persistent connection are inactive after 3 sec.
13
+ # Implemented a walk around, that renews the connection after 2 sec. of inactivity
14
+ t = Time.now
15
+ @t ||= Time.now
16
+ @http = nil if t-@t > 2
17
+ @t = t
12
18
  @http ||= HTTPX.plugin(:basic_auth).basic_auth(auth[:username], auth[:password])
13
19
  .plugin(:persistent)
14
20
  .plugin(:circuit_breaker)
@@ -49,7 +55,7 @@ module Arcade
49
55
 
50
56
  # ------------------------------ post data -------------------------------------------------------- #
51
57
  def post_data command, payload
52
- case response = http.post( Config.base_uri + command, json: payload )
58
+ case response = http.post( Config.base_uri + command, json: payload )
53
59
  in {status: 200..299}
54
60
  # success
55
61
  JSON.parse( response.body, symbolize_names: true )[:result]
@@ -64,7 +70,7 @@ module Arcade
64
70
  sleep 1
65
71
  post_data command, payload
66
72
  else
67
- raise Arcade::QueryError.new **response.json( symbolize_names: true )
73
+ raise Arcade::QueryError.new **response.json( symbolize_names: true )
68
74
  end
69
75
  else
70
76
  # # http error
@@ -73,6 +79,49 @@ module Arcade
73
79
  # response.raise_for_status
74
80
  end
75
81
 
82
+ # ------------------------------ post text ----------------------------------------------------- #
83
+ # posts a plain-text body (used by the TimeSeries Line Protocol endpoint)
84
+ def post_text command, body
85
+ case response = http.post( Config.base_uri + command, body: body )
86
+ in {status: 200..299}
87
+ response.status
88
+ in {status: 400..}
89
+ answer = response.json( symbolize_names: true )
90
+ answer[:detail] ||= answer[:error] # ts-endpoints report error only
91
+ raise Arcade::QueryError.new **answer
92
+ else
93
+ raise response
94
+ end
95
+ end
96
+
97
+ # ------------------------------ post json raw ------------------------------------------------- #
98
+ # posts a json body, returns the parsed body (not the :result part)
99
+ def post_json command, payload
100
+ case response = http.post( Config.base_uri + command, json: payload )
101
+ in {status: 200..299}
102
+ JSON.parse( response.body, symbolize_names: true )
103
+ in {status: 400..}
104
+ answer = response.json( symbolize_names: true )
105
+ answer[:detail] ||= answer[:error]
106
+ raise Arcade::QueryError.new **answer
107
+ else
108
+ raise response
109
+ end
110
+ end
111
+
112
+ # ------------------------------ get json ------------------------------------------------------ #
113
+ # GET returning the parsed body (not only the :result part)
114
+ def get_json command
115
+ case response = http.get( Config.base_uri + command )
116
+ in {status: 200..299}
117
+ JSON.parse( response.body, symbolize_names: true )
118
+ in {status: 400..}
119
+ raise Arcade::QueryError.new **response.json( symbolize_names: true )
120
+ else
121
+ raise response
122
+ end
123
+ end
124
+
76
125
  # ------------------------------ transaction ------------------------------------------------- #
77
126
  #
78
127
  # The payload, optional as a JSON, accepts the following parameters:
@@ -87,6 +136,7 @@ module Arcade
87
136
  # ------------------------------ post transaction ------------------------------------------------- #
88
137
  def post_transaction command, params, session_id:
89
138
  http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
139
+ puts "params #{params.inspect}"
90
140
  case response = http_a.post( Config.base_uri + command, json: params )
91
141
  in {status: 200..299}
92
142
  # success
data/lib/arcade/base.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Arcade
2
+
2
3
  class Base < Dry::Struct
3
4
 
4
5
  extend Arcade::Support::Sql
@@ -20,6 +21,9 @@ module Arcade
20
21
  ## ----------------------------------------- Class Methods------------------------------------ ##
21
22
  # #
22
23
  class << self
24
+ def descendants
25
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
26
+ end
23
27
 
24
28
  # this has to be implemented on class level
25
29
  # otherwise it interfere with attributes
@@ -49,7 +53,7 @@ module Arcade
49
53
  begin
50
54
  loop do
51
55
  if the_class.respond_to?(:demodulize)
52
- if [ 'Document','Vertex', 'Edge'].include?(the_class.demodulize)
56
+ if [ 'Document','Vertex', 'Edge', 'Timeseries'].include?(the_class.demodulize)
53
57
  if the_class == superclass # no inheritance
54
58
  db.create_type the_class.demodulize, to_s.snake_case
55
59
  else
@@ -294,16 +298,19 @@ module Arcade
294
298
  Query.new( **{ from: self }.merge(args) )
295
299
  end
296
300
 
297
- # immutable support
298
- # to make a database type immutable add
301
+ # ## Immutable Support
302
+ #
303
+ # To make a database type immutable add
299
304
  # `not_permitted :update, :upsert, :delete`
300
305
  # to the model-specification
301
306
  #
307
+ # Even after applying `not_permitted` the database-type can be modified via class-methods.
308
+ #
302
309
  def not_permitted *m
303
310
  m.each do | def_m |
304
- define_method( def_m ) do | v = nil |
305
- raise ArcadeImmutableError "operation not permitted", caller
306
- end
311
+ define_method( def_m ) do | v = nil |
312
+ raise Arcade::ImmutableError.new( "operation #{def_m} not permitted" )
313
+ end
307
314
  end
308
315
  end
309
316
 
@@ -366,8 +373,6 @@ module Arcade
366
373
  end
367
374
 
368
375
  def to_human
369
-
370
-
371
376
  "<#{ self.class.to_s.snake_case }" + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
372
377
  v= case value
373
378
  when Base
@@ -428,7 +433,7 @@ module Arcade
428
433
 
429
434
  def update **args
430
435
  Query.new( from: rid , kind: :update, set: args).execute
431
- refresh
436
+ refresh # return the updated record (the object itself is untouched!)
432
437
  end
433
438
 
434
439
  # inserts or updates a embedded document
@@ -448,21 +453,37 @@ module Arcade
448
453
  db.transmit { " update #{rid} set `#{embedded}`.`#{embedded_property}` = #{value.to_or}" }
449
454
  end
450
455
 
451
- def update_list list, value
452
- value = if value.is_a? Document
456
+ # Adds List-Elements to embedded List
457
+ #
458
+ # Arguments:
459
+ # * list: A symbol of the list property
460
+ # * value: A embedded document or a hash
461
+ # * modus: :auto, :first, :append
462
+ #
463
+ # Prefered modus operandi
464
+ # * the-element.insert (...) , #{list}:[]
465
+ # * the_element.update_list list, value: :append
466
+ #
467
+ def update_list list, value, modus: :auto
468
+ value = if value.is_a? Document # embedded mode
453
469
  value.to_json
454
470
  else
455
471
  value.to_or
456
472
  end
457
- if send( list ).nil? || send( list ).empty?
458
- db.transmit { "update #{ rid } set #{ list } = [#{ value }]" }
473
+ if modus == :auto
474
+ modus = db.query( "select #{list}.size() from #{rid}" ).select_result.first.zero? ? :first : :append
475
+ end
476
+
477
+ if modus == :first
478
+ db.transmit { "update #{ rid } set #{ list } = [#{ value }]" }
459
479
  else
460
480
  db.transmit { "update #{ rid } set #{ list } += #{ value }" }
461
481
  end
462
- refresh
482
+ # refresh
463
483
  end
464
484
 
465
485
  # updates a map property , actually adds the key-value pair to the property
486
+ ## does not work on reduced model records
466
487
  def update_map m, key, value
467
488
  if send( m ).nil?
468
489
  db.transmit { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) " }
@@ -157,7 +157,7 @@ module Arcade
157
157
 
158
158
 
159
159
 
160
- # ------------ create -----------
160
+ # ------------ create ----------- ## not supported anymore by the api
161
161
  # returns an rid of the successfully created vertex or document
162
162
  #
163
163
  # Parameter: name of the vertex or document type
@@ -186,19 +186,28 @@ module Arcade
186
186
 
187
187
  content_params = params.except( :type, :bucket, :index, :from, :return, :session_id )
188
188
  target_params = params.slice( :type, :bucket, :index )
189
- # session_id = params[:session_id] # extraxt session_id --> future-use?
189
+ # session_id = params[:session_id] # extraxt session_id --> future-use?
190
190
  if target_params.empty?
191
191
  raise "Could not insert: target missing (type:, bucket:, index:)"
192
192
  elsif content_params.empty?
193
193
  logger.error "Nothing to Insert"
194
194
  else
195
- content = "CONTENT #{ content_params.to_json }"
195
+ content = "CONTENT #{ content_params.to_json }"
196
196
  target = target_params.map{|y,z| y==:type ? z : "#{y.to_s} #{ z } "}.join
197
- result = Api.execute( database, session_id: session ){ "INSERT INTO #{target} #{content} "}
198
- result &.first.allocate_model(false)
197
+ result = Api.execute( database, session_id: session ){ "INSERT INTO #{target} #{content} "}
198
+ #
199
+ result &.record&.allocate_model(false)
200
+ ## commented for now. reactivate if necessary (performance issue)
201
+ #record = result&.first
202
+ # Ensure @type is present for proper model allocation
203
+ #if record && !record.key?(:@type) && target_params[:type]
204
+ # record = record.merge( :"@type" => target_params[:type] )
205
+ #end
206
+ #record&.allocate_model(false)
199
207
  end
200
208
  end
201
209
 
210
+
202
211
  # ------------------------------ get ------------------------------------------------------ #
203
212
  # Get fetches the record associated with the rid given as parameter.
204
213
  #
@@ -224,7 +233,7 @@ module Arcade
224
233
  end
225
234
  end
226
235
 
227
- # ------------------------------ get ------------------------------------------------------ #
236
+ # ------------------------------ delete ------------------------------------------------------ #
228
237
  #
229
238
  # Delete the specified rid
230
239
  #
@@ -272,7 +281,7 @@ module Arcade
272
281
  end
273
282
  rescue Dry::Struct::Error, Arcade::QueryError => e
274
283
  Api.rollback database, session_id: s, log: false
275
- logger.info "Execution FAILED --> Status #{e.status}"
284
+ logger.fatal "Execution FAILED --> Status #{e}"
276
285
  [] # return empty result
277
286
  end
278
287