arango-driver 3.5.0.alpha0

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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +1073 -0
  4. data/arango_opal.js +15 -0
  5. data/lib/arango-driver.rb +61 -0
  6. data/lib/arango.rb +96 -0
  7. data/lib/arango/aql.rb +188 -0
  8. data/lib/arango/collection.rb +575 -0
  9. data/lib/arango/collection/documents.rb +122 -0
  10. data/lib/arango/collection/edges.rb +149 -0
  11. data/lib/arango/collection/importing.rb +57 -0
  12. data/lib/arango/collection/indexes.rb +53 -0
  13. data/lib/arango/collection/replication.rb +24 -0
  14. data/lib/arango/collection/user.rb +28 -0
  15. data/lib/arango/cursor.rb +67 -0
  16. data/lib/arango/database.rb +188 -0
  17. data/lib/arango/database/analyzer.rb +21 -0
  18. data/lib/arango/database/aql_functions.rb +54 -0
  19. data/lib/arango/database/aql_queries.rb +114 -0
  20. data/lib/arango/database/aql_query_cache.rb +27 -0
  21. data/lib/arango/database/collections.rb +100 -0
  22. data/lib/arango/database/foxx_services.rb +103 -0
  23. data/lib/arango/database/graph_access.rb +27 -0
  24. data/lib/arango/database/http_route.rb +9 -0
  25. data/lib/arango/database/replication.rb +96 -0
  26. data/lib/arango/database/stream_transactions.rb +25 -0
  27. data/lib/arango/database/tasks.rb +67 -0
  28. data/lib/arango/database/transactions.rb +15 -0
  29. data/lib/arango/database/user.rb +26 -0
  30. data/lib/arango/database/view_access.rb +37 -0
  31. data/lib/arango/document.rb +443 -0
  32. data/lib/arango/edge.rb +164 -0
  33. data/lib/arango/error.rb +97 -0
  34. data/lib/arango/error_db.rb +27 -0
  35. data/lib/arango/foxx.rb +255 -0
  36. data/lib/arango/graph.rb +202 -0
  37. data/lib/arango/graph/basics.rb +39 -0
  38. data/lib/arango/graph/edge_access.rb +56 -0
  39. data/lib/arango/graph/vertex_access.rb +33 -0
  40. data/lib/arango/helper/collection_assignment.rb +13 -0
  41. data/lib/arango/helper/database_assignment.rb +14 -0
  42. data/lib/arango/helper/request_method.rb +45 -0
  43. data/lib/arango/helper/return.rb +21 -0
  44. data/lib/arango/helper/satisfaction.rb +28 -0
  45. data/lib/arango/helper/server_assignment.rb +13 -0
  46. data/lib/arango/helper/traversal.rb +12 -0
  47. data/lib/arango/index.rb +103 -0
  48. data/lib/arango/replication.rb +231 -0
  49. data/lib/arango/request.rb +92 -0
  50. data/lib/arango/request_batch.rb +174 -0
  51. data/lib/arango/result.rb +130 -0
  52. data/lib/arango/search_view.rb +23 -0
  53. data/lib/arango/server.rb +68 -0
  54. data/lib/arango/server/administration.rb +296 -0
  55. data/lib/arango/server/agency.rb +23 -0
  56. data/lib/arango/server/async.rb +51 -0
  57. data/lib/arango/server/batch.rb +35 -0
  58. data/lib/arango/server/config.rb +76 -0
  59. data/lib/arango/server/databases.rb +71 -0
  60. data/lib/arango/server/monitoring.rb +17 -0
  61. data/lib/arango/server/opal_support.rb +95 -0
  62. data/lib/arango/server/tasks.rb +69 -0
  63. data/lib/arango/server/user.rb +22 -0
  64. data/lib/arango/task.rb +223 -0
  65. data/lib/arango/transaction.rb +113 -0
  66. data/lib/arango/traversal.rb +212 -0
  67. data/lib/arango/user.rb +174 -0
  68. data/lib/arango/version.rb +3 -0
  69. data/lib/arango/vertex.rb +112 -0
  70. data/lib/arango/view.rb +124 -0
  71. data/lib/arango/view/basics.rb +25 -0
  72. metadata +296 -0
@@ -0,0 +1,103 @@
1
+ module Arango
2
+ class Database
3
+ module FoxxServices
4
+ # === FOXX ===
5
+ def install_service
6
+ # TODO
7
+ end
8
+
9
+ def replace_service
10
+ # TODO
11
+ end
12
+
13
+ def upgrade_service
14
+ # TODO
15
+ end
16
+
17
+ def uninstall_service
18
+
19
+ end
20
+
21
+ def list_services
22
+ # TODO
23
+ end
24
+
25
+ def get_service
26
+ # TODO
27
+ end
28
+
29
+ def get_service_configuration
30
+ # TODO
31
+ end
32
+
33
+ def replace_service_configuration
34
+ # TODO
35
+ end
36
+
37
+ def update_service_configuration
38
+ # TODO
39
+ end
40
+
41
+ def get_service_dependencies
42
+ # TODO
43
+ end
44
+
45
+ def replace_service_dependencies
46
+ # TODO
47
+ end
48
+
49
+ def update_service_dependencies
50
+ # TODO
51
+ end
52
+
53
+ def enable_service_development_mode
54
+ # TODO
55
+ end
56
+
57
+ def disable_service_development_mode
58
+ # TODO
59
+ end
60
+
61
+ def list_service_scripts
62
+ # TODO
63
+ end
64
+
65
+ def run_service_script
66
+ # TODO
67
+ end
68
+
69
+ def run_service_tests
70
+ # TODO
71
+ end
72
+
73
+ def download_service
74
+ # TODO
75
+ end
76
+
77
+ def get_service_readme
78
+ # TODO
79
+ end
80
+
81
+ def get_service_documentation
82
+ # TODO
83
+ end
84
+
85
+ def commit_local_Service_state
86
+ # TODO
87
+ end
88
+ # def foxxes
89
+ # result = request("GET", "_api/foxx")
90
+ # return result if return_directly?(result)
91
+ # result.map do |fox|
92
+ # Arango::Foxx.new(database: self, mount: fox[:mount], body: fox)
93
+ # end
94
+ # end
95
+
96
+ # def foxx(body: {}, development: nil, legacy: nil, mount:, name: nil, provides: nil, setup: nil, teardown: nil, type: "application/json",
97
+ # version: nil)
98
+ # Arango::Foxx.new(body: body, database: self, development: development, legacy: legacy, mount: mount, name: name, provides: provides,
99
+ # setup: setup, teardown: teardown, type: type, version: version)
100
+ # end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,27 @@
1
+ module Arango
2
+ class Database
3
+ module GraphAccess
4
+ # == GRAPH ==
5
+
6
+ def create_graph
7
+
8
+ end
9
+ def graphs
10
+ result = request("GET", "_api/gharial")
11
+ return result if return_directly?(result)
12
+ result[:graphs].map do |graph|
13
+ Arango::Graph.new(database: self, name: graph[:_key], body: graph)
14
+ end
15
+ end
16
+
17
+ def graph(name:, edge_definitions: [], orphan_collections: [],
18
+ body: {})
19
+ Arango::Graph.new(name: name, database: self, edge_definitions: edge_definitions, orphan_collections: orphan_collections, body: body)
20
+ end
21
+
22
+ def list_graphs
23
+ # TODO
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Arango
2
+ class Database
3
+ module HTTPRoute
4
+ def route
5
+ # TODO
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,96 @@
1
+ module Arango
2
+ class Database
3
+ module Replication
4
+ # === REPLICATION ===
5
+
6
+ def inventory(batch_id:, global: nil, include_system: nil)
7
+ query = {
8
+ batchId: batch_id,
9
+ global: global,
10
+ includeSystem: include_system
11
+ }
12
+ request("GET", "_api/replication/inventory", query: query)
13
+ end
14
+
15
+ def cluster_inventory(include_system: nil)
16
+ query = { includeSystem: include_system }
17
+ request("GET", "_api/replication/clusterInventory", query: query)
18
+ end
19
+
20
+ def logger
21
+ request("GET", "_api/replication/logger-state")
22
+ end
23
+
24
+ def logger_follow(from: nil, to: nil, chunk_size: nil, include_system: nil)
25
+ query = {
26
+ from: from,
27
+ to: to,
28
+ chunkSize: chunk_size,
29
+ includeSystem: include_system
30
+ }
31
+ request("GET", "_api/replication/logger-follow", query: query)
32
+ end
33
+
34
+ def logger_first_tick
35
+ request("GET", "_api/replication/logger-first-tick", key: :firstTick)
36
+ end
37
+
38
+ def logger_range_tick
39
+ request("GET", "_api/replication/logger-tick-ranges")
40
+ end
41
+
42
+ def server_id
43
+ request("GET", "_api/replication/server-id", key: :serverId)
44
+ end
45
+
46
+ def range
47
+ request("GET", "_api/wal/range")
48
+ end
49
+
50
+ def last_tick
51
+ request("GET", "_api/wal/lastTick")
52
+ end
53
+
54
+ def tail(from: nil, to: nil, global: nil, chunk_size: nil,
55
+ server_id: nil, barrier_id: nil)
56
+ query = {
57
+ from: from,
58
+ to: to,
59
+ barrierID: barrier_id,
60
+ chunkSize: chunk_size,
61
+ global: global,
62
+ serverID: server_id,
63
+ }
64
+ request("GET", "_api/wal/tail", query: query)
65
+ end
66
+
67
+ def replication(master:, adaptive_polling: nil, auto_resync: nil, auto_resync_retries: nil, chunk_size: nil, connect_timeout: nil,
68
+ connection_retry_wait_time: nil, idle_max_wait_time: nil, idle_min_wait_time: nil, include_system: true, incremental: nil,
69
+ initial_sync_max_wait_time: nil, max_connect_retries: nil, request_timeout: nil, require_from_present: nil,
70
+ restrict_collections: nil, restrict_type: nil, verbose: nil)
71
+ Arango::Replication.new(slave: self, master: master, adaptive_polling: adaptive_polling, auto_resync: auto_resync,
72
+ auto_resync_retries: auto_resync_retries, chunk_size: chunk_size, connect_timeout: connect_timeout,
73
+ connection_retry_wait_time: connection_retry_wait_time, idle_max_wait_time: idle_max_wait_time,
74
+ idle_min_wait_time: idle_min_wait_time, include_system: include_system, incremental: incremental,
75
+ initial_sync_max_wait_time: initial_sync_max_wait_time, max_connect_retries: max_connect_retries,
76
+ request_timeout: request_timeout, require_from_present: require_from_present,
77
+ restrict_collections: restrict_collections, restrict_type: restrict_type, verbose: verbose)
78
+ end
79
+
80
+ def replication_as_master(slave:, adaptive_polling: nil, auto_resync: nil, auto_resync_retries: nil, chunk_size: nil, connect_timeout: nil,
81
+ connection_retry_wait_time: nil, idle_max_wait_time: nil, idle_min_wait_time: nil, include_system: true,
82
+ incremental: nil, initial_sync_max_wait_time: nil, max_connect_retries: nil, request_timeout: nil,
83
+ require_from_present: nil, restrict_collections: nil, restrict_type: nil, verbose: nil)
84
+ Arango::Replication.new(master: self, slave: slave, adaptive_polling: adaptive_polling, auto_resync: auto_resync,
85
+ auto_resync_retries: auto_resync_retries, chunk_size: chunk_size, connect_timeout: connect_timeout,
86
+ connection_retry_wait_time: connection_retry_wait_time, idle_max_wait_time: idle_max_wait_time,
87
+ idle_min_wait_time: idle_min_wait_time, include_system: include_system, incremental: incremental,
88
+ initial_sync_max_wait_time: initial_sync_max_wait_time, max_connect_retries: max_connect_retries,
89
+ request_timeout: request_timeout, require_from_present: require_from_present,
90
+ restrict_collections: restrict_collections, restrict_type: restrict_type, verbose: verbose)
91
+ end
92
+
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,25 @@
1
+ module Arango
2
+ class Database
3
+ module StreamTransactions
4
+ def begin_stream_transaction
5
+
6
+ end
7
+
8
+ def stream_transaction
9
+
10
+ end
11
+
12
+ def list_stream_transactions
13
+
14
+ end
15
+
16
+ def commit_stream_transaction
17
+
18
+ end
19
+
20
+ def abort_stream_transaction
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,67 @@
1
+ module Arango
2
+ class Database
3
+ module Tasks
4
+ # Get all tasks.
5
+ # @return [Array<Arango::Task>]
6
+ def all_tasks
7
+ Arango::Task.all(database: self)
8
+ end
9
+
10
+ # Create a new task with given id, task is saved to the database.
11
+ # @param id [String]
12
+ # @param command [String] The javascript code to execute.
13
+ # @param name [String] The task name, optional.
14
+ # @param offset [Integer] The number of seconds initial delay, optional.
15
+ # @param params [Hash] Hash of params to pass to the command, optional.
16
+ # @param period [Integer] Number of seconds between executions, optional.
17
+ # @return [Arango::Task]
18
+ def create_task(id, command:, name: nil, offset: nil, params: nil, period: nil)
19
+ Arango::Task.new(id, command: command, name: name, offset: offset, params: params, period: period, database: self).create
20
+ end
21
+
22
+ # Get a task from the database.
23
+ # @param id [String]
24
+ # @return [Arango::Task]
25
+ def get_task(id)
26
+ Arango::Task.get(id, database: self)
27
+ end
28
+ alias fetch_task get_task
29
+ alias retrieve_task get_task
30
+
31
+ # Instantiate a new task with given id, task is not saved to the database.
32
+ # @param id [String]
33
+ # @param command [String] The javascript code to execute, optional.
34
+ # @param name [String] The task name, optional.
35
+ # @param offset [Integer] The number of seconds initial delay, optional.
36
+ # @param params [Hash] Hash of params to pass to the command, optional.
37
+ # @param period [Integer] Number of seconds between executions, optional.
38
+ # @return [Arango::Task]
39
+ def new_task(id, command: nil, name: nil, offset: nil, params: nil, period: nil)
40
+ Arango::Task.new(id, command: command, name: name, offset: offset, params: params, period: period, database: self)
41
+ end
42
+
43
+ # Get a list of all task ids.
44
+ # @return [Array<String>]
45
+ def list_tasks
46
+ Arango::Task.list(database: self)
47
+ end
48
+
49
+ # Delete task with given id.
50
+ # @param id [String]
51
+ # @return [Boolean] Returns true if task has been deleted.
52
+ def drop_task(id)
53
+ Arango::Task.delete(id, database: self)
54
+ end
55
+ alias delete_task drop_task
56
+ alias destroy_task drop_task
57
+
58
+ # Checks existence of a task.
59
+ # @param id [String]
60
+ # @return [Boolean] Returns true if the task exists, otherwise false.
61
+ def exist_task?(id)
62
+ Arango::Task.exist?(id, database: self)
63
+ end
64
+ alias task_exist? exist_task?
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ module Arango
2
+ class Database
3
+ module Transactions
4
+ # === TRANSACTION ===
5
+
6
+ def transaction(action:, intermediate_commit_count: nil, intermediate_commit_size: nil, lock_timeout: nil, max_transaction_size: nil, params: nil,
7
+ read: [], wait_for_sync: nil, write: [])
8
+ Arango::Transaction.new(action: action, database: self, intermediate_commit_count: intermediate_commit_count,
9
+ intermediate_commit_size: intermediate_commit_size, lock_timeout: lock_timeout,
10
+ max_transaction_size: max_transaction_size, params: params, read: read, wait_for_sync: wait_for_sync, write: write)
11
+ # TODO execute
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Arango
2
+ class Database
3
+ module User
4
+ def check_user(user)
5
+ user = Arango::User.new(user: user) if user.is_a?(String)
6
+ return user
7
+ end
8
+ private :check_user
9
+
10
+ def add_user_access(grant:, user:)
11
+ user = check_user(user)
12
+ user.add_database_access(grant: grant, database: @name)
13
+ end
14
+
15
+ def revoke_user_access(user:)
16
+ user = check_user(user)
17
+ user.revoke_database_access(database: @name)
18
+ end
19
+
20
+ def user_access(user:)
21
+ user = check_user(user)
22
+ user.database_access(database: @name)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,37 @@
1
+ module Arango
2
+ class Database
3
+ module ViewAccess
4
+ # === VIEW ===
5
+
6
+ def create_search_view
7
+
8
+ end
9
+
10
+ def search_view(name)
11
+ # TODO Returns a ArangoSearchView instance for the given view name
12
+ end
13
+
14
+ def list_views
15
+ # TODO Fetches all views from the database and returns an array of view descriptions.
16
+ end
17
+
18
+ # verified, in js api
19
+ def views
20
+ result = request("GET", "_api/view", key: :result)
21
+ return result if return_directly?(result)
22
+ result.map do |view|
23
+ Arango::View.new(database: self, id: view[:id], name: view[:name], type: view[:type])
24
+ end
25
+ end
26
+
27
+ # not found
28
+ def view(name)
29
+ Arango::View.new(database: self, name: name)
30
+ end
31
+
32
+ def create_view
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,443 @@
1
+ # ==== DOCUMENT ====
2
+
3
+ module Arango
4
+ class Document
5
+ include Arango::Helper::Satisfaction
6
+ include Arango::Helper::Return
7
+ include Arango::Helper::CollectionAssignment
8
+ include Arango::Helper::Traversal
9
+
10
+ extend Arango::Helper::RequestMethod
11
+
12
+ class << self
13
+ Arango.aql_request_class_method(Arango::Document, :all) do |offset: 0, limit: nil, batch_size: nil, collection:|
14
+ bind_vars = {}
15
+ query = "FOR doc IN #{collection.name}"
16
+ if limit && offset
17
+ query << "\n LIMIT @offset, @limit"
18
+ bind_vars[:offset] = offset
19
+ bind_vars[:limit] = limit
20
+ end
21
+ raise Arango::Error.new err: "offset must be used with limit" if offset > 0 && !limit
22
+ query << "\n RETURN doc"
23
+ # aql = Arango::AQL.new(database: collection.database, query: query, bind_vars: bind_vars, batch_size: batch_size)
24
+ # result = aql.execute
25
+ { query: query, bind_vars: bind_vars, batch_size: batch_size, block: -> (aql, result) do
26
+ result_proc = ->(b) { b.result.map { |d| Arango::Document.new(d, collection: collection) }}
27
+ final_result = result_proc.call(result)
28
+ if aql.has_more?
29
+ collection.instance_variable_set(:@aql, aql)
30
+ collection.instance_variable_set(:@batch_proc, result_proc)
31
+ unless batch_size
32
+ while aql.has_more?
33
+ final_result += collection.next_batch
34
+ end
35
+ end
36
+ end
37
+ final_result
38
+ end
39
+ }
40
+ end
41
+
42
+ Arango.aql_request_class_method(Arango::Document, :list) do |offset: 0, limit: nil, batch_size: nil, collection:|
43
+ bind_vars = {}
44
+ query = "FOR doc IN #{collection.name}"
45
+ if limit && offset
46
+ query << "\n LIMIT @offset, @limit"
47
+ bind_vars[:offset] = offset
48
+ bind_vars[:limit] = limit
49
+ end
50
+ raise Arango::Error.new err: "offset must be used with limit" if offset > 0 && !limit
51
+ query << "\n RETURN doc._key"
52
+ # aql = Arango::AQL.new(database: collection.database, query: query, bind_vars: bind_vars, batch_size: batch_size)
53
+ # result = aql.execute
54
+ { database: collection.database, query: query, bind_vars: bind_vars, batch_size: batch_size, block: -> (aql, result) do
55
+ result_proc = ->(b) { b.result }
56
+ final_result = result_proc.call(result)
57
+ if aql.has_more?
58
+ collection.instance_variable_set(:@aql, aql)
59
+ collection.instance_variable_set(:@batch_proc, result_proc)
60
+ unless batch_size
61
+ while aql.has_more?
62
+ final_result += collection.next_batch
63
+ end
64
+ end
65
+ end
66
+ final_result
67
+ end
68
+ }
69
+ end
70
+
71
+ Arango.request_class_method(Arango::Document, :exist?) do |document, match_rev: nil, collection:|
72
+ body = _body_from_arg(document)
73
+ raise Arango::Error err: "Document with key required!" unless body.key?(:_key)
74
+ request = { head: "_api/document/#{collection.name}/#{body[:_key]}" }
75
+ if body.key?(:_key) && body.key?(:_rev) && match_rev == true
76
+ request[:headers] = {'If-Match' => body[:_rev] }
77
+ elsif body.key?(:_key) && body.key?(:_rev) && match_rev == false
78
+ request[:headers] = {'If-None-Match' => body[:_rev] }
79
+ end
80
+ request[:block] = ->(result) do
81
+ case result.response_code
82
+ when 200 then true # document was found
83
+ when 304 then true # “If-None-Match” header is given and the document has the same version
84
+ when 412 then true # “If-Match” header is given and the found document has a different version.
85
+ else
86
+ false
87
+ end
88
+ end
89
+ request
90
+ end
91
+
92
+ Arango.request_class_method(Arango::Document, :create_documents) do |documents, wait_for_sync: nil, collection:|
93
+ documents = [documents] unless documents.is_a? Array
94
+ documents = documents.map{ |d| _body_from_arg(d) }
95
+ query = { returnNew: true }
96
+ query[:waitForSync] = wait_for_sync unless wait_for_sync.nil?
97
+ { post: "_api/document/#{collection.name}", body: documents, query: query, block: ->(result) do
98
+ result.map do |doc|
99
+ Arango::Document.new(doc[:new], collection: collection)
100
+ end
101
+ end
102
+ }
103
+ end
104
+
105
+ Arango.request_class_method(Arango::Document, :get) do |document, collection:|
106
+ document = _body_from_arg(document)
107
+ if document.key?(:_key)
108
+ { get: "_api/document/#{collection.name}/#{document[:_key]}", block: ->(result) { Arango::Document.new(result, collection: collection) }}
109
+ else
110
+ bind_vars = {}
111
+ query = "FOR doc IN #{collection.name}"
112
+ i = 0
113
+ document.each do |k,v|
114
+ i += 1
115
+ query << "\n FILTER doc.@key#{i} == @value#{i}"
116
+ bind_vars["key#{i}"] = k.to_s
117
+ bind_vars["value#{i}"] = v
118
+ end
119
+ query << "\n LIMIT 1"
120
+ query << "\n RETURN doc"
121
+ aql = AQL.new(query: query, database: collection.database, bind_vars: bind_vars, block: ->(_, result) do
122
+ Arango::Document.new(result.result.first, collection: collection) if result.result.first
123
+ end
124
+ )
125
+ aql.request
126
+ end
127
+ end
128
+ alias fetch get
129
+ alias retrieve get
130
+ alias batch_fetch batch_get
131
+ alias batch_retrieve batch_get
132
+
133
+ Arango.multi_request_class_method(Arango::Document, :get_documents) do |documents, collection:|
134
+ documents = [documents] unless documents.is_a? Array
135
+ documents = documents.map{ |d| _body_from_arg(d) }
136
+ requests = []
137
+ result_documents = []
138
+ documents.each do |document|
139
+ if document.key?(:_key)
140
+ requests << { get: "_api/document/#{collection.name}/#{document[:_key]}", block: ->(result) do
141
+ result_documents << Arango::Document.new(result, collection: collection)
142
+ end
143
+ }
144
+ else
145
+ bind_vars = {}
146
+ query = "FOR doc IN #{collection.name}"
147
+ i = 0
148
+ document.each do |k,v|
149
+ i += 1
150
+ query << "\n FILTER doc.@key#{i} == @value#{i}"
151
+ bind_vars["key#{i}"] = k.to_s
152
+ bind_vars["value#{i}"] = v
153
+ end
154
+ query << "\n LIMIT 1"
155
+ query << "\n RETURN doc"
156
+ aql = AQL.new(query: query, database: collection.database, bind_vars: bind_vars, block: ->(_, result) do
157
+ result_documents << Arango::Document.new(result.result.first, collection: collection) if result.result.first
158
+ result_documents
159
+ end
160
+ )
161
+ requests << aql.request
162
+ end
163
+ end
164
+ requests
165
+ end
166
+ alias fetch_documents get_documents
167
+ alias retrieve_documents get_documents
168
+ alias batch_fetch_documents batch_get_documents
169
+ alias batch_retrieve_documents batch_get_documents
170
+
171
+ Arango.request_class_method(Arango::Document, :replace_documents) do |documents, ignore_revs: false, wait_for_sync: nil, collection:|
172
+ documents = [documents] unless documents.is_a? Array
173
+ documents = documents.map{ |d| _body_from_arg(d) }
174
+ query = { returnNew: true, ignoreRevs: ignore_revs }
175
+ query[:waitForSync] = wait_for_sync unless wait_for_sync.nil?
176
+ { put: "_api/document/#{collection.name}", body: documents, query: query, block: ->(result) do
177
+ result.map do |doc|
178
+ Arango::Document.new(doc[:new], collection: collection)
179
+ end
180
+ end
181
+ }
182
+ end
183
+
184
+ Arango.request_class_method(Arango::Document, :update_documents) do |documents, ignore_revs: false, wait_for_sync: nil, merge_objects: nil, collection:|
185
+ documents = [documents] unless documents.is_a? Array
186
+ documents = documents.map{ |d| _body_from_arg(d) }
187
+ query = { returnNew: true, ignoreRevs: ignore_revs }
188
+ query[:waitForSync] = wait_for_sync unless wait_for_sync.nil?
189
+ query[:mergeObjects] = merge_objects unless merge_objects.nil?
190
+ { patch: "_api/document/#{collection.name}", body: documents, query: query, block: ->(result) do
191
+ result.map do |doc|
192
+ Arango::Document.new(doc[:new], collection: collection)
193
+ end
194
+ end
195
+ }
196
+ end
197
+
198
+ Arango.request_class_method(Arango::Document, :drop) do |document, ignore_revs: false, wait_for_sync: nil, collection:|
199
+ document = _body_from_arg(document)
200
+ query = { ignoreRevs: ignore_revs }
201
+ query[:waitForSync] = wait_for_sync unless wait_for_sync.nil?
202
+ headers = nil
203
+ headers = { "If-Match": document[:_rev] } if !ignore_revs && document.key?(:_rev)
204
+ { delete: "_api/document/#{collection.name}/#{document[:_key]}", query: query, headers: headers, block: ->(_) { nil }}
205
+ end
206
+ alias delete drop
207
+ alias destroy drop
208
+ alias batch_delete batch_drop
209
+ alias batch_destroy batch_drop
210
+
211
+ Arango.request_class_method(Arango::Document, :drop_documents) do |documents, ignore_revs: false, wait_for_sync: nil, collection:|
212
+ documents = [documents] unless documents.is_a? Array
213
+ documents = documents.map{ |d| _body_from_arg(d) }
214
+ query = { ignoreRevs: ignore_revs }
215
+ query[:waitForSync] = wait_for_sync unless wait_for_sync.nil?
216
+ { delete: "_api/document/#{collection.name}", body: documents, query: query, block: ->(_) { nil }}
217
+ end
218
+ alias delete_documents drop_documents
219
+ alias destroy_documents drop_documents
220
+ alias batch_delete_documents batch_drop_documents
221
+ alias batch_destroy_documents batch_drop_documents
222
+
223
+ private
224
+
225
+ def _body_from_arg(arg)
226
+ case arg
227
+ when String then { _key: arg }
228
+ when Hash
229
+ arg.transform_keys!(&:to_sym)
230
+ arg[:_id] = arg.delete(:id) if arg.key?(:id) && !arg.key?(:_id)
231
+ arg[:_key] = arg.delete(:key) if arg.key?(:key) && !arg.key?(:_key)
232
+ arg[:_rev] = arg.delete(:rev) if arg.key?(:rev) && !arg.key?(:_rev)
233
+ arg.delete_if{|_,v| v.nil?}
234
+ arg
235
+ when Arango::Document then arg.to_h
236
+ when Arango::Result then arg.to_h
237
+ else
238
+ raise "Unknown arg type, must be String, Hash, Arango::Result or Arango::Document"
239
+ end
240
+ end
241
+ end
242
+
243
+ def initialize(document, collection:, ignore_revs: false, wait_for_sync: nil)
244
+ @body = _body_from_arg(document)
245
+ @changed_body = {}
246
+ @ignore_revs = ignore_revs
247
+ @wait_for_sync = wait_for_sync
248
+ assign_collection(collection)
249
+ end
250
+
251
+ def id
252
+ return @changed_body[:_id] if @changed_body.key?(:_id)
253
+ @body[:_id]
254
+ end
255
+
256
+ def id=(i)
257
+ @changed_body[:_id] = i
258
+ end
259
+
260
+ def key
261
+ return @changed_body[:_key] if @changed_body.key?(:_key)
262
+ @body[:_key]
263
+ end
264
+
265
+ def key=(k)
266
+ @changed_body[:_key] = k
267
+ end
268
+
269
+ def revision
270
+ return @changed_body[:_rev] if @changed_body.key?(:_rev)
271
+ @body[:_rev]
272
+ end
273
+
274
+ def rev=(r)
275
+ @changed_body[:_rev] = r
276
+ end
277
+
278
+ def to_h
279
+ @body.delete_if{|_,v| v.nil?}
280
+ end
281
+
282
+ attr_accessor :ignore_revs, :wait_for_sync
283
+
284
+ attr_reader :collection, :graph, :database, :server, :body
285
+
286
+ # todo body= -> replace_body, update_body
287
+ def body=(doc)
288
+ @changed_body = _body_from_arg(doc)
289
+ #set_up_from_or_to("from", result[:_from])
290
+ #set_up_from_or_to("to", result[:_to])
291
+ end
292
+
293
+ def method_missing(name, *args, &block)
294
+ name_s = name.to_s
295
+ set_attr = false
296
+ have_attr = false
297
+ attribute_name_s = name_s.end_with?('=') ? (set_attr = true; name_s.chop) : name_s
298
+ attribute_name_y = attribute_name_s.start_with?('attribute_') ? (have_attr = true; attribute_name_s[9..-1].to_sym) : attribute_name_s.to_sym
299
+ if set_attr
300
+ return @changed_body[attribute_name_y] = args[0]
301
+ elsif @changed_body.key?(attribute_name_y)
302
+ return @changed_body[attribute_name_y]
303
+ elsif @body.key?(attribute_name_y)
304
+ return @body[attribute_name_y]
305
+ elsif have_attr
306
+ return nil
307
+ end
308
+ super(name, *args, &block)
309
+ end
310
+
311
+ request_method :reload do
312
+ headers = nil
313
+ headers = { "If-Match": @body[:_rev] } if !@ignore_revs && @body.key?(:_rev)
314
+ { get: "_api/document/#{@collection.name}/#{@body[:_key]}", headers: headers,
315
+ block: ->(result) do
316
+ @body = _body_from_arg(result)
317
+ @changed_body = {}
318
+ self
319
+ end
320
+ }
321
+ end
322
+ alias refresh reload
323
+ alias retrieve reload
324
+ alias revert reload
325
+ alias batch_refresh batch_reload
326
+ alias batch_retrieve batch_reload
327
+ alias batch_revert batch_reload
328
+
329
+ request_method :same_revision? do
330
+ headers = { "If-Match": @body[:_rev] }
331
+ { head: "_api/document/#{@collection.name}/#{@body[:_key]}", headers: headers, block: ->(result) { result.response_code == 200 }}
332
+ end
333
+
334
+ request_method :create do
335
+ query = { returnNew: true }
336
+ query[:waitForSync] = @wait_for_sync unless @wait_for_sync.nil?
337
+ @body = @body.merge(@changed_body)
338
+ @changed_body = {}
339
+ { post: "_api/document/#{@collection.name}", body: @body, query: query,
340
+ block: ->(result) do
341
+ @body.merge!(result[:new])
342
+ self
343
+ end
344
+ }
345
+ end
346
+
347
+ request_method :replace do
348
+ query = { returnNew: true, ignoreRevs: @ignore_revs }
349
+ query[:waitForSync] = @wait_for_sync unless @wait_for_sync.nil?
350
+ headers = nil
351
+ body = @changed_body
352
+ body[:_id] = @body[:_id]
353
+ body[:_key] = @body[:_key]
354
+ body[:_rev] = @body[:_rev]
355
+ @body = body
356
+ @changed_body = {}
357
+ headers = { "If-Match": @body[:_rev] } if !@ignore_revs && @body.key?(:_rev)
358
+ { put: "_api/document/#{@collection.name}/#{@body[:_key]}", body: @body, query: query, headers: headers,
359
+ block: ->(result) do
360
+ @body.merge!(result[:new])
361
+ self
362
+ end
363
+ }
364
+ end
365
+
366
+ request_method :save do
367
+ query = { returnNew: true, ignoreRevs: @ignore_revs }
368
+ query[:waitForSync] = @wait_for_sync unless @wait_for_sync.nil?
369
+ headers = nil
370
+ headers = { "If-Match": @body[:_rev] } if !@ignore_revs && @body.key?(:_rev)
371
+ changed_body = @changed_body
372
+ @changed_body = {}
373
+ { patch: "_api/document/#{@collection.name}/#{@body[:_key]}", body: changed_body, query: query, headers: headers,
374
+ block: ->(result) do
375
+ @body.merge!(result[:new])
376
+ self
377
+ end
378
+ }
379
+ end
380
+ alias update save
381
+ alias batch_update batch_save
382
+
383
+ request_method :drop do
384
+ query = { waitForSync: @wait_for_sync }
385
+ headers = nil
386
+ headers = { "If-Match": @body[:_rev] } if !@ignore_revs && @body.key?(:_rev)
387
+ { delete: "_api/document/#{@collection.name}/#{@body[:_key]}", query: query, headers: headers, block: ->(_) { nil }}
388
+ end
389
+ alias delete drop
390
+ alias destroy drop
391
+ alias batch_delete batch_drop
392
+ alias batch_destroy batch_drop
393
+
394
+ # === EDGE ===
395
+
396
+ def edges(collection:, direction: nil)
397
+ satisfy_class?(collection, [Arango::Collection, String])
398
+ collection = collection.is_a?(Arango::Collection) ? collection.name : collection
399
+ query = {
400
+ vertex: @body[:_id],
401
+ direction: direction
402
+ }
403
+ result = @database.request("GET", "_api/edges/#{collection}", query: query)
404
+ return result if return_directly?(result)
405
+ result[:edges].map do |edge|
406
+ collection_name, key = edge[:_id].split("/")
407
+ collection = Arango::Collection.new(collection_name, database: @database, type: :edge)
408
+ Arango::Document.new(edge, collection: collection)
409
+ end
410
+ end
411
+
412
+ def any(collection)
413
+ edges(collection: collection)
414
+ end
415
+
416
+ def out(collection)
417
+ edges(collection: collection, direction: "out")
418
+ end
419
+
420
+ def in(collection)
421
+ edges(collection: collection, direction: "in")
422
+ end
423
+
424
+ private
425
+
426
+ def _body_from_arg(arg)
427
+ case arg
428
+ when String then { _key: arg }
429
+ when Hash
430
+ arg.transform_keys!(&:to_sym)
431
+ arg[:_id] = arg.delete(:id) if arg.key?(:id) && !arg.key?(:_id)
432
+ arg[:_key] = arg.delete(:key) if arg.key?(:key) && !arg.key?(:_key)
433
+ arg[:_rev] = arg.delete(:rev) if arg.key?(:rev) && !arg.key?(:_rev)
434
+ arg.delete_if{|_,v| v.nil?}
435
+ arg
436
+ when Arango::Document then arg.to_h
437
+ when Arango::Result then arg.to_h
438
+ else
439
+ raise "Unknown arg type, must be String, Hash, Arango::Result or Arango::Document"
440
+ end
441
+ end
442
+ end
443
+ end