arango-driver 3.5.0.alpha0

Sign up to get free protection for your applications and to get access to all the features.
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