arangorb 1.4.1 → 2.0.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/ArangoRB.gemspec +20 -18
  3. data/Gemfile +3 -0
  4. data/README.md +1079 -908
  5. data/lib/AQL.rb +155 -0
  6. data/lib/Batch.rb +97 -0
  7. data/lib/Cache.rb +71 -0
  8. data/lib/Collection.rb +852 -0
  9. data/lib/Database.rb +417 -0
  10. data/lib/Document.rb +346 -0
  11. data/lib/Edge.rb +104 -0
  12. data/lib/Error.rb +125 -0
  13. data/lib/Foxx.rb +277 -0
  14. data/lib/Graph.rb +325 -0
  15. data/lib/Index.rb +126 -0
  16. data/lib/Replication.rb +235 -0
  17. data/lib/Request.rb +143 -0
  18. data/lib/Server.rb +466 -0
  19. data/lib/Task.rb +120 -0
  20. data/lib/Transaction.rb +115 -0
  21. data/lib/Traversal.rb +224 -0
  22. data/lib/User.rb +197 -0
  23. data/lib/Vertex.rb +127 -0
  24. data/lib/View.rb +151 -0
  25. data/lib/arangorb.rb +23 -15
  26. data/lib/helpers/Error.rb +28 -0
  27. data/lib/helpers/Return.rb +53 -0
  28. metadata +64 -45
  29. data/lib/ArangoRB_AQL.rb +0 -181
  30. data/lib/ArangoRB_Cache.rb +0 -174
  31. data/lib/ArangoRB_Col.rb +0 -526
  32. data/lib/ArangoRB_DB.rb +0 -363
  33. data/lib/ArangoRB_Doc.rb +0 -319
  34. data/lib/ArangoRB_Edg.rb +0 -184
  35. data/lib/ArangoRB_Gra.rb +0 -201
  36. data/lib/ArangoRB_Index.rb +0 -135
  37. data/lib/ArangoRB_Replication.rb +0 -261
  38. data/lib/ArangoRB_Ser.rb +0 -446
  39. data/lib/ArangoRB_Task.rb +0 -129
  40. data/lib/ArangoRB_Tra.rb +0 -169
  41. data/lib/ArangoRB_Tran.rb +0 -68
  42. data/lib/ArangoRB_User.rb +0 -157
  43. data/lib/ArangoRB_Ver.rb +0 -162
  44. data/spec/arangoRB_helper.rb +0 -4
  45. data/spec/arangoRestart_helper.rb +0 -14
  46. data/spec/arangorb-1.3.0.gem +0 -0
  47. data/spec/lib/0.1.0/arangoAQL_helper.rb +0 -64
  48. data/spec/lib/0.1.0/arangoC_helper.rb +0 -170
  49. data/spec/lib/0.1.0/arangoDB_helper.rb +0 -119
  50. data/spec/lib/0.1.0/arangoDoc_helper.rb +0 -79
  51. data/spec/lib/0.1.0/arangoE_helper.rb +0 -50
  52. data/spec/lib/0.1.0/arangoG_helper.rb +0 -78
  53. data/spec/lib/0.1.0/arangoS_helper.rb +0 -37
  54. data/spec/lib/0.1.0/arangoT_helper.rb +0 -48
  55. data/spec/lib/0.1.0/arangoV_helper.rb +0 -65
  56. data/spec/lib/1.0.0/arangoC_helper.rb +0 -73
  57. data/spec/lib/1.0.0/arangoDB_helper.rb +0 -48
  58. data/spec/lib/1.0.0/arangoI_helper.rb +0 -43
  59. data/spec/lib/1.0.0/arangoS_helper.rb +0 -192
  60. data/spec/lib/1.0.0/arangoTa_helper.rb +0 -49
  61. data/spec/lib/1.0.0/arangoTr_helper.rb +0 -15
  62. data/spec/lib/1.0.0/arangoU_helper.rb +0 -72
  63. data/spec/lib/1.1.0/arangoRB_helper.rb +0 -144
  64. data/spec/lib/1.1.0/arangoRB_walks_helper.rb +0 -19
  65. data/spec/lib/1.2.0/arangoCache_helper.rb +0 -66
  66. data/spec/lib/1.3.0/arangoHash_helper.rb +0 -30
  67. data/spec/lib/arangoRB_0.1.0_helper.rb +0 -9
  68. data/spec/lib/arangoRB_1.0.0_helper.rb +0 -6
  69. data/spec/lib/arangoRB_1.1.0_helper.rb +0 -2
  70. data/spec/lib/arangoRB_1.2.0_helper.rb +0 -2
  71. data/spec/spec_helper.rb +0 -42
data/lib/AQL.rb ADDED
@@ -0,0 +1,155 @@
1
+ # === AQL ===
2
+
3
+ module Arango
4
+ class AQL
5
+ include Arango::Helper_Error
6
+ include Arango::Helper_Return
7
+ include Arango::Database_Return
8
+
9
+ def initialize(query:, database:, count: nil, batchSize: nil, cache: nil,
10
+ memoryLimit: nil, ttl: nil, bindVars: nil, failOnWarning: nil,
11
+ profile: nil, maxTransactionSize: nil, skipInaccessibleCollections: nil,
12
+ maxWarningCount: nil, intermediateCommitCount: nil,
13
+ satelliteSyncWait: nil, fullCount: nil, intermediateCommitSize: nil,
14
+ optimizer_rules: nil, maxPlans: nil)
15
+ satisfy_class?(query, [String])
16
+ @query = query
17
+ assign_database(database)
18
+
19
+ @count = count
20
+ @batchSize = batchSize
21
+ @cache = cache
22
+ @memoryLimit = memoryLimit
23
+ @ttl = ttl
24
+ @bindVars = bindVars
25
+
26
+ @quantity = nil
27
+ @hasMore = false
28
+ @id = ""
29
+ @result = []
30
+ @options = {}
31
+ # DEFINE
32
+ ["failOnWarning", "profile", "maxTransactionSize",
33
+ "skipInaccessibleCollections", "maxWarningCount", "intermediateCommitCount",
34
+ "satelliteSyncWait", "fullCount", "intermediateCommitSize",
35
+ "optimizer_rules", "maxPlans"].each do |param_name|
36
+ param = eval(param_name)
37
+ set_option(param, param_name)
38
+ define_singleton_method("#{param_name}=") do |value|
39
+ set_option(value, param_name)
40
+ end
41
+ end
42
+ end
43
+
44
+ # === DEFINE ===
45
+
46
+ attr_accessor :count, :query, :batchSize, :ttl, :cache, :options, :bindVars, :quantity
47
+ attr_reader :hasMore, :id, :result, :idCache, :failOnWarning, :profile,
48
+ :maxTransactionSize, :skipInaccessibleCollections, :maxWarningCount,
49
+ :intermediateCommitCount, :satelliteSyncWait, :fullCount, :server, :cached, :extra,
50
+ :intermediateCommitSize, :optimizer_rules, :maxPlans, :database
51
+ alias size batchSize
52
+ alias size= batchSize=
53
+
54
+ def set_option(attrs, name)
55
+ @options ||= {}
56
+ instance_variable_set("@#{name}", attrs)
57
+ unless attrs
58
+ name = "optimizer.rules" if name == "optimizer_rules"
59
+ @options[name] = attrs
60
+ end
61
+ @options.delete_if{|k,v| v.nil?}
62
+ @options = nil if @options.empty?
63
+ end
64
+ private :set_option
65
+
66
+ # === TO HASH ===
67
+
68
+ def to_h
69
+ {
70
+ "query": @query,
71
+ "result": @result,
72
+ "count": @count,
73
+ "quantity": @quantity,
74
+ "ttl": @ttl,
75
+ "cache": @cache,
76
+ "batchSize": @batchSize,
77
+ "bindVars": @bindVars,
78
+ "options": @options,
79
+ "idCache": @idCache,
80
+ "memoryLimit": @memoryLimit,
81
+ "database": @database.name
82
+ }.delete_if{|k,v| v.nil?}
83
+ end
84
+
85
+ # === REQUEST ===
86
+
87
+ def return_aql(result)
88
+ return result if @server.async != false
89
+ @extra = result[:extra]
90
+ @cached = result[:cached]
91
+ @quantity = result[:count]
92
+ @hasMore = result[:hasMore]
93
+ @id = result[:id]
94
+ if(result[:result][0].nil? || !result[:result][0].is_a?(Hash) || !result[:result][0].key?(:_key))
95
+ @result = result[:result]
96
+ else
97
+ @result = result[:result].map do |x|
98
+ collection = Arango::Collection.new(name: x[:_id].split("/")[0], database: @database)
99
+ Arango::Document.new(name: x[:_key], collection: collection, body: x)
100
+ end
101
+ end
102
+ return return_directly?(result) ? result: self
103
+ end
104
+ private :return_aql
105
+
106
+ # === EXECUTE QUERY ===
107
+
108
+ def execute
109
+ body = {
110
+ "query": @query,
111
+ "count": @count,
112
+ "batchSize": @batchSize,
113
+ "ttl": @ttl,
114
+ "cache": @cache,
115
+ "options": @options,
116
+ "bindVars": @bindVars,
117
+ "memoryLimit": @memoryLimit
118
+ }
119
+ result = @database.request("POST", "_api/cursor", body: body)
120
+ return_aql(result)
121
+ end
122
+
123
+ def next
124
+ if @hasMore
125
+ result = @database.request("PUT", "_api/cursor/#{@id}")
126
+ return_aql(result)
127
+ else
128
+ raise Arango::Error.new err::no_other_aql_next, data: {"hasMore": false}
129
+ end
130
+ end
131
+
132
+ def destroy
133
+ @database.request("DELETE", "_api/cursor/#{@id}")
134
+ end
135
+
136
+ def kill
137
+ @database.request("DELETE", "_api/query/#{@id}")
138
+ end
139
+
140
+ # === PROPERTY QUERY ===
141
+
142
+ def explain
143
+ body = {
144
+ "query": @query,
145
+ "options": @options,
146
+ "bindVars": @bindVars
147
+ }
148
+ @database.request("POST", "_api/explain", body: body)
149
+ end
150
+
151
+ def parse
152
+ @database.request("POST", "_api/query", body: {"query": @query})
153
+ end
154
+ end
155
+ end
data/lib/Batch.rb ADDED
@@ -0,0 +1,97 @@
1
+ module Arango
2
+ class Batch
3
+ include Arango::Helper_Error
4
+ include Arango::Helper_Return
5
+ include Arango::Server_Return
6
+
7
+ def initialize(server:, boundary: "XboundaryX", queries: [])
8
+ @id = 1
9
+ assign_server(server)
10
+ assign_queries(queries)
11
+ @headers = {
12
+ "Content-Type": "multipart/form-data",
13
+ "boundary": boundary
14
+ }
15
+ @boundary = boundary
16
+ end
17
+
18
+ # === DEFINE ===
19
+
20
+ attr_reader :server, :boundary, :queries
21
+
22
+ def boundary=(boundary)
23
+ @boundary = boundary
24
+ @headers[:boundary] = boundary
25
+ end
26
+
27
+ def queries=(queries)
28
+ queries = [queries] unless queries.is_a?(Array)
29
+ @queries = {}
30
+ queries.each do |query|
31
+ begin
32
+ query.keys.each do |key|
33
+ query[(key.to_sym rescue key) || key] = query.delete(key)
34
+ end
35
+ rescue
36
+ raise Arango::Error.new(err: :batch_query_not_valid,
37
+ data: {wrong_query: query})
38
+ end
39
+ satisfy_class?(query, [Hash])
40
+ if query[:id].nil?
41
+ query[:id] = @id.to_s
42
+ @id += 1
43
+ end
44
+ @queries[query[:id]] = query
45
+ end
46
+ return @queries
47
+ end
48
+ alias assign_queries queries=
49
+
50
+ # === TO HASH ===
51
+
52
+ def to_h
53
+ {
54
+ "boundary": @boundary,
55
+ "queries": @queries,
56
+ "database": @database.name
57
+ }.delete_if{|k,v| v.nil?}
58
+ end
59
+
60
+ # === QUERY ===
61
+
62
+ def addQuery(id: @id, method:, address:, body: nil)
63
+ id = id.to_s
64
+ @queries[id] = {
65
+ "id": id,
66
+ "method": method,
67
+ "address": address,
68
+ "body": body
69
+ }.delete_if{|k,v| v.nil?}
70
+ @id += 1
71
+ return @queries
72
+ end
73
+ alias modifyQuery addQuery
74
+
75
+ def removeQuery(id:)
76
+ @queries.delete(id)
77
+ return @queries
78
+ end
79
+
80
+ # === EXECUTE ===
81
+
82
+ def execute
83
+ body = ""
84
+ @queries.each do |id, query|
85
+ body += "--#{@boundary}\n"
86
+ body += "Content-Type: application/x-arango-batchpart\n"
87
+ body += "Content-Id: #{query[:id]}\n\n"
88
+ body += "#{query[:method]} "
89
+ body += "#{query[:address]} HTTP/1.1\n"
90
+ body += "\n#{Oj.dump(query[:body])}\n" unless query[:body].nil?
91
+ end
92
+ body += "--#{@boundary}--\n" if @queries.length > 0
93
+ @server.request("POST", "_api/batch", body: body, skip_to_json: true,
94
+ headers: @headers, skip_parsing: true)
95
+ end
96
+ end
97
+ end
data/lib/Cache.rb ADDED
@@ -0,0 +1,71 @@
1
+ module Arango
2
+ class Cache
3
+ def initialize
4
+ @max = {
5
+ database: 10,
6
+ collection: 20,
7
+ document: 200,
8
+ graph: 10,
9
+ index: 20,
10
+ user: 20,
11
+ task: 20,
12
+ view: 20,
13
+ foxx: 20
14
+ }
15
+
16
+ @cache = {
17
+ database: {},
18
+ collection: {},
19
+ document: {},
20
+ graph: {},
21
+ index: {},
22
+ aql: {},
23
+ user: {},
24
+ task: {},
25
+ view: {},
26
+ foxx: {}
27
+ }
28
+ end
29
+
30
+ attr_reader :cache, :max
31
+
32
+ def to_h
33
+ hash = {
34
+ "max": @max,
35
+ "cache": {}
36
+ }
37
+ @cache.each do |key, hash2|
38
+ next if hash2.empty?
39
+ hash[:cache][key] = hash2.keys
40
+ end
41
+ hash
42
+ end
43
+
44
+ def save(type, id, obj)
45
+ while @cache[type].length >= @max[type]
46
+ @cache[type].shift
47
+ end
48
+ @cache[type][id] = obj
49
+ end
50
+
51
+ def destroy(type, id)
52
+ @cache[type].delete(id)
53
+ end
54
+
55
+ def clear
56
+ @cache.each_key{|k| @cache[k] = {}}
57
+ end
58
+
59
+ def updateMax(type:, val:)
60
+ type = type.to_sym rescue type = :error
61
+ unless @max.has_key?(type.to_sym)
62
+ ArangoDB::Error.new :element_in_cache_does_not_exist,
63
+ {wrong_attribute: :type, wrong_value: type}
64
+ end
65
+ while @cache[type].length > val
66
+ @cache[type].shift
67
+ end
68
+ @max[type] = val
69
+ end
70
+ end
71
+ end
data/lib/Collection.rb ADDED
@@ -0,0 +1,852 @@
1
+ # === COLLECTION ===
2
+
3
+ module Arango
4
+ class Collection
5
+ include Arango::Helper_Error
6
+ include Arango::Helper_Return
7
+ include Arango::Database_Return
8
+
9
+ def self.new(*args)
10
+ hash = args[0]
11
+ super unless hash.is_a?(Hash)
12
+ database = hash[:database]
13
+ if database.is_a?(Arango::Database) && database.server.active_cache
14
+ cache_name = "#{database.name}/#{hash[:name]}"
15
+ cached = database.server.cache.cache.dig(:database, cache_name)
16
+ if cached.nil?
17
+ hash[:cache_name] = cache_name
18
+ return super
19
+ else
20
+ body = hash[:body] || {}
21
+ [:type, :isSystem].each{|k| body[k] ||= hash[k]}
22
+ cached.assign_attributes(body)
23
+ return cached
24
+ end
25
+ end
26
+ super
27
+ end
28
+
29
+ def initialize(name:, database:, graph: nil, body: {}, type: :document,
30
+ isSystem: nil, cache_name: nil)
31
+ @name = name
32
+ assign_database(database)
33
+ assign_graph(graph)
34
+ assign_type(type)
35
+ unless cache_name.nil?
36
+ @cache_name = cache_name
37
+ @server.cache.save(:collection, cache_name, self)
38
+ end
39
+ body[:type] ||= type == :document ? 2 : 3
40
+ body[:status] ||= nil
41
+ body[:isSystem] ||= isSystem
42
+ body[:id] ||= nil
43
+ assign_attributes(body)
44
+ end
45
+
46
+ # === DEFINE ===
47
+
48
+ attr_reader :status, :isSystem, :id, :server, :database, :graph, :type,
49
+ :countExport, :hasMoreExport, :idExport, :hasMoreSimple, :idSimple, :body,
50
+ :cache_name
51
+ attr_accessor :name
52
+
53
+ def graph=(graph)
54
+ satisfy_class?(graph, [Arango::Graph, NilClass])
55
+ if !graph.nil? && graph.database.name != @database.name
56
+ raise Arango::Error.new err: :database_graph_no_same_as_collection_database,
57
+ data: {"graph_database_name": graph.database.name,
58
+ "collection_database_name": @database.name}
59
+ end
60
+ @graph = graph
61
+ end
62
+ alias assign_graph graph=
63
+
64
+ def body=(result)
65
+ @body = result
66
+ @name = result[:name] || @name
67
+ @type = assign_type(result[:type])
68
+ @status = reference_status(result[:status])
69
+ @id = result[:id] || @id
70
+ @isSystem = result[:isSystem] || @isSystem
71
+ if @server.active_cache && @cache_name.nil?
72
+ @cache_name = "#{@database.name}/#{@name}"
73
+ @server.cache.save(:database, @cache_name, self)
74
+ end
75
+ end
76
+ alias assign_attributes body=
77
+
78
+ def type=(type)
79
+ type ||= @type
80
+ satisfy_category?(type, ["Document", "Edge", 2, 3, nil, :document, :edge])
81
+ @type = case type
82
+ when 2, "Document", nil
83
+ :document
84
+ when 3, "Edge"
85
+ :edge
86
+ end
87
+ end
88
+ alias assign_type type=
89
+
90
+ def reference_status(number)
91
+ number ||= @number
92
+ return nil if number.nil?
93
+ hash = ["new born collection", "unloaded", "loaded",
94
+ "in the process of being unloaded", "deleted", "loading"]
95
+ return hash[number-1]
96
+ end
97
+ private :reference_status
98
+
99
+ # === TO HASH ===
100
+
101
+ def to_h
102
+ {
103
+ "name": @name,
104
+ "type": @type,
105
+ "status": @status,
106
+ "id": @id,
107
+ "isSystem": @isSystem,
108
+ "body": @body,
109
+ "cache_name": @cache_name,
110
+ "database": @database.name
111
+ }.delete_if{|k,v| v.nil?}
112
+ end
113
+
114
+ # === GET ===
115
+
116
+ def retrieve
117
+ result = @database.request("GET", "_api/collection/#{@name}")
118
+ return_element(result)
119
+ end
120
+
121
+ def properties
122
+ @database.request("GET", "_api/collection/#{@name}/properties")
123
+ end
124
+
125
+ def count
126
+ @database.request("GET", "_api/collection/#{@name}/count", key: :count)
127
+ end
128
+
129
+ def statistics
130
+ @database.request("GET", "_api/collection/#{@name}/figures", key: :figures)
131
+ end
132
+
133
+ def revision
134
+ @database.request("GET", "_api/collection/#{@name}/revision", key: :revision)
135
+ end
136
+
137
+ def checksum(withRevisions: nil, withData: nil)
138
+ query = {
139
+ "withRevisions": withRevisions,
140
+ "withData": withData
141
+ }
142
+ @database.request("GET", "_api/collection/#{@name}/checksum", query: query,
143
+ key: :checksum)
144
+ end
145
+
146
+ # == POST ==
147
+
148
+ def create(journalSize: nil, replicationFactor: nil,
149
+ allowUserKeys: nil, typeKeyGenerator: nil, incrementKeyGenerator: nil,
150
+ offsetKeyGenerator: nil, waitForSync: nil, doCompact: nil,
151
+ isVolatile: nil, shardKeys: nil, numberOfShards: nil,
152
+ isSystem: @isSystem, type: @type, indexBuckets: nil, distributeShardsLike: nil, shardingStrategy: nil)
153
+ satisfy_category?(typeKeyGenerator, [nil, "traditional", "autoincrement"])
154
+ satisfy_category?(type, ["Edge", "Document", 2, 3, nil, :edge, :document])
155
+ satisfy_category?(shardingStrategy, [nil, "community-compat", "enterprise-compat", "enterprise-smart-edge-compat", "hash", "enterprise-hash-smart-edge"])
156
+ keyOptions = {
157
+ "allowUserKeys": allowUserKeys,
158
+ "type": typeKeyGenerator,
159
+ "increment": incrementKeyGenerator,
160
+ "offset": offsetKeyGenerator
161
+ }
162
+ keyOptions.delete_if{|k,v| v.nil?}
163
+ keyOptions = nil if keyOptions.empty?
164
+ type = case type
165
+ when 2, "Document", nil, :document
166
+ 2
167
+ when 3, "Edge", :edge
168
+ 3
169
+ end
170
+ body = {
171
+ "name": @name,
172
+ "type": type,
173
+ "replicationFactor": replicationFactor,
174
+ "journalSize": journalSize,
175
+ "keyOptions": keyOptions,
176
+ "waitForSync": waitForSync,
177
+ "doCompact": doCompact,
178
+ "isVolatile": isVolatile,
179
+ "shardKeys": shardKeys,
180
+ "numberOfShards": numberOfShards,
181
+ "isSystem": isSystem,
182
+ "indexBuckets": indexBuckets,
183
+ "distributeShardsLike": distributeShardsLike,
184
+ "shardingStrategy": shardingStrategy
185
+ }
186
+ body = @body.merge(body)
187
+ result = @database.request("POST", "_api/collection", body: body)
188
+ return_element(result)
189
+ end
190
+
191
+ # === DELETE ===
192
+
193
+ def destroy
194
+ result = @database.request("DELETE", "_api/collection/#{@name}")
195
+ return return_delete(result)
196
+ end
197
+
198
+ def truncate
199
+ result = @database.request("PUT", "_api/collection/#{@name}/truncate")
200
+ return_element(result)
201
+ end
202
+
203
+ # === MODIFY ===
204
+
205
+ def load
206
+ result = @database.request("PUT", "_api/collection/#{@name}/load")
207
+ return_element(result)
208
+ end
209
+
210
+ def unload
211
+ result = @database.request("PUT", "_api/collection/#{@name}/unload")
212
+ return_element(result)
213
+ end
214
+
215
+ def loadIndexesIntoMemory
216
+ @database.request("PUT", "_api/collection/#{@name}/loadIndexesIntoMemory")
217
+ return true
218
+ end
219
+
220
+ def change(waitForSync: nil, journalSize: nil)
221
+ body = {
222
+ "journalSize": journalSize,
223
+ "waitForSync": waitForSync
224
+ }
225
+ result = @database.request("PUT", "_api/collection/#{@name}/properties", body: body)
226
+ return_element(result)
227
+ end
228
+
229
+ def rename(newName:)
230
+ body = { "name": newName }
231
+ result = @database.request("PUT", "_api/collection/#{@name}/rename", body: body)
232
+ return_element(result)
233
+ end
234
+
235
+ def rotate
236
+ @database.request("PUT", "_api/collection/#{@name}/rotate")
237
+ return true
238
+ end
239
+
240
+ # == DOCUMENT ==
241
+
242
+ def [](document_name)
243
+ Arango::Document.new(name: document_name, collection: self)
244
+ end
245
+
246
+ def document(name: nil, body: {}, rev: nil, from: nil, to: nil)
247
+ Arango::Document.new(name: name, collection: self, body: body, rev: rev,
248
+ from: from, to: to)
249
+ end
250
+
251
+ def documents(type: "document") # "path", "id", "key"
252
+ @returnDocument = false
253
+ if type == "document"
254
+ @returnDocument = true
255
+ type = "key"
256
+ end
257
+ satisfy_category?(type, ["path", "id", "key", "document"])
258
+ body = { "type": type, "collection": @name }
259
+ result = @database.request("PUT", "_api/simple/all-keys", body: body)
260
+ @hasMoreSimple = result[:hasMore]
261
+ @idSimple = result[:id]
262
+ return result if return_directly?(result)
263
+ return result[:result] unless @returnDocument
264
+ if @returnDocument
265
+ result[:result].map{|key| Arango::Document.new(name: key, collection: self)}
266
+ end
267
+ end
268
+
269
+ def next
270
+ if @hasMoreSimple
271
+ result = @database.request("PUT", "_api/cursor/#{@idSimple}")
272
+ @hasMoreSimple = result[:hasMore]
273
+ @idSimple = result[:id]
274
+ return result if return_directly?(result)
275
+ return result[:result] unless @returnDocument
276
+ if @returnDocument
277
+ result[:result].map{|key| Arango::Document.new(name: key, collection: self)}
278
+ end
279
+ else
280
+ raise Arango::Error.new err: :no_other_simple_next, data: {"hasMoreSimple": @hasMoreSimple}
281
+ end
282
+ end
283
+
284
+ def return_body(x, type=:document)
285
+ satisfy_class?(x, [Hash, Arango::Document, Arango::Edge, Arango::Vertex])
286
+ body = case x
287
+ when Hash
288
+ x
289
+ when Arango::Edge
290
+ if type == :vertex
291
+ raise Arango::Error.new err: :wrong_type_instead_of_expected_one, data:
292
+ {"expected_value": type, "received_value": x.type, "wrong_object": x}
293
+ end
294
+ x.body
295
+ when Arango::Vertex
296
+ if type == :edge
297
+ raise Arango::Error.new err: :wrong_type_instead_of_expected_one, data:
298
+ {"expected_value": type, "received_value": x.type, "wrong_object": x}
299
+ end
300
+ x.body
301
+ when Arango::Document
302
+ if (type == :vertex && x.collection.type == :edge) ||
303
+ (type == :edge && x.collection.type == :document) ||
304
+ (type == :edge && x.collection.type == :vertex)
305
+ raise Arango::Error.new err: :wrong_type_instead_of_expected_one, data:
306
+ {"expected_value": type, "received_value": x.collection.type, "wrong_object": x}
307
+ end
308
+ x.body
309
+ end
310
+ return body.delete_if{|k,v| v.nil?}
311
+ end
312
+ private :return_body
313
+
314
+ def return_id(x)
315
+ satisfy_class?(x, [String, Arango::Document, Arango::Vertex])
316
+ return x.is_a?(String) ? x : x.id
317
+ end
318
+ private :return_id
319
+
320
+ def createDocuments(document: [], waitForSync: nil, returnNew: nil,
321
+ silent: nil)
322
+ document = [document] unless document.is_a? Array
323
+ document = document.map{|x| return_body(x)}
324
+ query = {
325
+ "waitForSync": waitForSync,
326
+ "returnNew": returnNew,
327
+ "silent": silent
328
+ }
329
+ results = @database.request("POST", "_api/document/#{@name}", body: document,
330
+ query: query)
331
+ return results if return_directly?(results) || silent
332
+ results.map.with_index do |result, index|
333
+ body2 = result.clone
334
+ if returnNew
335
+ body2.delete(:new)
336
+ body2 = body2.merge(result[:new])
337
+ end
338
+ real_body = document[index]
339
+ real_body = real_body.merge(body2)
340
+ Arango::Document.new(name: result[:_key], collection: self, body: real_body)
341
+ end
342
+ end
343
+
344
+ def createEdges(document: {}, from:, to:, waitForSync: nil, returnNew: nil, silent: nil)
345
+ edges = []
346
+ from = [from] unless from.is_a? Array
347
+ to = [to] unless to.is_a? Array
348
+ document = [document] unless document.is_a? Array
349
+ document = document.map{|x| return_body(x, :edge)}
350
+ from = from.map{|x| return_id(x)}
351
+ to = to.map{|x| return_id(x)}
352
+ document.each do |b|
353
+ from.each do |f|
354
+ to.each do |t|
355
+ b[:_from] = f
356
+ b[:_to] = t
357
+ edges << b.clone
358
+ end
359
+ end
360
+ end
361
+ createDocuments(document: edges, waitForSync: waitForSync,
362
+ returnNew: returnNew, silent: silent)
363
+ end
364
+
365
+ def replaceDocuments(document: {}, waitForSync: nil, ignoreRevs: nil,
366
+ returnOld: nil, returnNew: nil)
367
+ document.each{|x| x = x.body if x.is_a?(Arango::Document)}
368
+ query = {
369
+ "waitForSync": waitForSync,
370
+ "returnNew": returnNew,
371
+ "returnOld": returnOld,
372
+ "ignoreRevs": ignoreRevs
373
+ }
374
+ result = @database.request("PUT", "_api/document/#{@name}", body: document,
375
+ query: query)
376
+ return results if return_directly?(result)
377
+ results.map.with_index do |result, index|
378
+ body2 = result.clone
379
+ if returnNew == true
380
+ body2.delete(:new)
381
+ body2 = body2.merge(result[:new])
382
+ end
383
+ real_body = document[index]
384
+ real_body = real_body.merge(body2)
385
+ Arango::Document.new(name: result[:_key], collection: self, body: real_body)
386
+ end
387
+ end
388
+
389
+ def updateDocuments(document: {}, waitForSync: nil, ignoreRevs: nil,
390
+ returnOld: nil, returnNew: nil, keepNull: nil, mergeObjects: nil)
391
+ document.each{|x| x = x.body if x.is_a?(Arango::Document)}
392
+ query = {
393
+ "waitForSync": waitForSync,
394
+ "returnNew": returnNew,
395
+ "returnOld": returnOld,
396
+ "ignoreRevs": ignoreRevs,
397
+ "keepNull": keepNull,
398
+ "mergeObject": mergeObjects
399
+ }
400
+ result = @database.request("PATCH", "_api/document/#{@name}", body: document,
401
+ query: query, keepNull: keepNull)
402
+ return results if return_directly?(result)
403
+ results.map.with_index do |result, index|
404
+ body2 = result.clone
405
+ if returnNew
406
+ body2.delete(:new)
407
+ body2 = body2.merge(result[:new])
408
+ end
409
+ real_body = document[index]
410
+ real_body = real_body.merge(body2)
411
+ Arango::Document.new(name: result[:_key], collection: self,
412
+ body: real_body)
413
+ end
414
+ end
415
+
416
+ def destroyDocuments(document: {}, waitForSync: nil, returnOld: nil,
417
+ ignoreRevs: nil)
418
+ document.each{|x| x = x.body if x.is_a?(Arango::Document)}
419
+ query = {
420
+ "waitForSync": waitForSync,
421
+ "returnOld": returnOld,
422
+ "ignoreRevs": ignoreRevs
423
+ }
424
+ @database.request("DELETE", "_api/document/#{@id}", query: query, body: document)
425
+ end
426
+
427
+ # == SIMPLE ==
428
+
429
+ def generic_document_search(url, body, single=false)
430
+ result = @database.request("PUT", url, body: body)
431
+ @returnDocument = true
432
+ @hasMoreSimple = result[:hasMore]
433
+ @idSimple = result[:id]
434
+ return result if return_directly?(result)
435
+
436
+ if single
437
+ Arango::Document.new(name: result[:document][:_key], collection: self,
438
+ body: result[:document])
439
+ else
440
+ result[:result].map{|x| Arango::Document.new(name: x[:_key], collection: self, body: x)}
441
+ end
442
+ end
443
+ private :generic_document_search
444
+
445
+ def allDocuments(skip: nil, limit: nil, batchSize: nil)
446
+ body = {
447
+ "collection": @name,
448
+ "skip": skip,
449
+ "limit": limit,
450
+ "batchSize": batchSize
451
+ }
452
+ generic_document_search("_api/simple/all", body)
453
+ end
454
+
455
+ def documentsMatch(match:, skip: nil, limit: nil, batchSize: nil)
456
+ body = {
457
+ "collection": @name,
458
+ "example": match,
459
+ "skip": skip,
460
+ "limit": limit,
461
+ "batchSize": batchSize
462
+ }
463
+ generic_document_search("_api/simple/by-example", body)
464
+ end
465
+
466
+ def documentMatch(match:)
467
+ body = {
468
+ "collection": @name,
469
+ "example": match
470
+ }
471
+ generic_document_search("_api/simple/first-example", body, true)
472
+ end
473
+
474
+ def documentByKeys(keys:)
475
+ keys = [keys] unless keys.is_a?(Array)
476
+ keys = keys.map{|x| x.is_a?(Arango::Document) ? x.name : x}
477
+ body = { "collection": @name, "keys": keys }
478
+ result = @database.request("PUT", "_api/simple/lookup-by-keys", body: body)
479
+ return result if return_directly?(result)
480
+ result[:documents].map do |x|
481
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
482
+ end
483
+ end
484
+
485
+ def documentByName(names:)
486
+ documentByKeys(keys: names)
487
+ end
488
+
489
+ def random
490
+ body = { "collection": @name }
491
+ generic_document_search("_api/simple/any", body, true)
492
+ end
493
+
494
+ def removeByKeys(keys:, returnOld: nil, silent: nil, waitForSync: nil)
495
+ options = {
496
+ "returnOld": returnOld,
497
+ "silent": silent,
498
+ "waitForSync": waitForSync
499
+ }
500
+ options.delete_if{|k,v| v.nil?}
501
+ options = nil if options.empty?
502
+ if keys.is_a? Array
503
+ keys = keys.map{|x| x.is_a?(String) ? x : x.key}
504
+ end
505
+ body = { "collection": @name, "keys": keys, "options": options}
506
+ result = @database.request("PUT", "_api/simple/remove-by-keys", body: body)
507
+ return result if return_directly?(result)
508
+ if returnOld == true && silent != true
509
+ result.each do |r|
510
+ Arango::Document.new(name: r[:_key], collection: self, body: r)
511
+ end
512
+ else
513
+ return result
514
+ end
515
+ end
516
+
517
+ def documentByName(names:, returnOld: nil, silent: nil, waitForSync: nil)
518
+ documentByKeys(keys: names, returnOld: returnOld, silent: silent,
519
+ waitForSync: waitForSync)
520
+ end
521
+
522
+ def removeMatch(match:, limit: nil, waitForSync: nil)
523
+ options = {
524
+ "limit": limit,
525
+ "waitForSync": waitForSync
526
+ }
527
+ options.delete_if{|k,v| v.nil?}
528
+ options = nil if options.empty?
529
+ body = {
530
+ "collection": @name,
531
+ "example" => match,
532
+ "options" => options
533
+ }
534
+ @database.request("PUT", "_api/simple/remove-by-example", body: body, key: :deleted)
535
+ end
536
+
537
+ def replaceMatch(match:, newValue:, limit: nil, waitForSync: nil)
538
+ options = {
539
+ "limit": limit,
540
+ "waitForSync": waitForSync
541
+ }
542
+ options.delete_if{|k,v| v.nil?}
543
+ options = nil if options.empty?
544
+ body = {
545
+ "collection": @name,
546
+ "example": match,
547
+ "options": options,
548
+ "newValue": newValue
549
+ }
550
+ @database.request("PUT", "_api/simple/replace-by-example", body: body, key: :replaced)
551
+ end
552
+
553
+ def updateMatch(match:, newValue:, keepNull: nil, mergeObjects: nil,
554
+ limit: nil, waitForSync: nil)
555
+ options = {
556
+ "keepNull": keepNull,
557
+ "mergeObjects": mergeObjects,
558
+ "limit": limit,
559
+ "waitForSync": waitForSync
560
+ }
561
+ options.delete_if{|k,v| v.nil?}
562
+ options = nil if options.empty?
563
+ body = {
564
+ "collection": @name,
565
+ "example": match,
566
+ "options": options,
567
+ "newValue": newValue
568
+ }
569
+ @database.request("PUT", "_api/simple/update-by-example", body: body, key: :updated)
570
+ end
571
+
572
+ # === SIMPLE DEPRECATED ===
573
+
574
+ def range(right:, attribute:, limit: nil, closed: true, skip: nil, left:,
575
+ warning: @server.warning)
576
+ warning_deprecated(warning, "range")
577
+ body = {
578
+ "right": right,
579
+ "attribute": attribute,
580
+ "collection": @name,
581
+ "limit": limit,
582
+ "closed": closed,
583
+ "skip": skip,
584
+ "left": left
585
+ }
586
+ result = @database.request("PUT", "_api/simple/range", body: body)
587
+ return result if return_directly?(result)
588
+ result[:result].map do |x|
589
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
590
+ end
591
+ end
592
+
593
+ def near(distance: nil, longitude:, latitude:, geo: nil, limit: nil,
594
+ skip: nil, warning: @server.warning)
595
+ warning_deprecated(warning, "near")
596
+ body = {
597
+ "distance": distance,
598
+ "longitude": longitude,
599
+ "collection": @name,
600
+ "limit": limit,
601
+ "latitude": latitude,
602
+ "skip": skip,
603
+ "geo": geo
604
+ }
605
+ result = @database.request("PUT", "_api/simple/near", body: body)
606
+ return result if return_directly?(result)
607
+ result[:result].map do |x|
608
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
609
+ end
610
+ end
611
+
612
+ def within(distance: nil, longitude:, latitude:, radius:, geo: nil,
613
+ limit: nil, skip: nil, warning: @server.warning)
614
+ warning_deprecated(warning, "within")
615
+ body = {
616
+ "distance": distance,
617
+ "longitude": longitude,
618
+ "collection": @name,
619
+ "limit": limit,
620
+ "latitude": latitude,
621
+ "skip": skip,
622
+ "geo": geo,
623
+ "radius": radius
624
+ }
625
+ result = @database.request("PUT", "_api/simple/within", body: body)
626
+ return result if return_directly?(result)
627
+ result[:result].map do |x|
628
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
629
+ end
630
+ end
631
+
632
+ def withinRectangle(longitude1:, latitude1:, longitude2:, latitude2:,
633
+ geo: nil, limit: nil, skip: nil, warning: @server.warning)
634
+ warning_deprecated(warning, "withinRectangle")
635
+ body = {
636
+ "longitude1": longitude1,
637
+ "latitude1": latitude1,
638
+ "longitude2": longitude2,
639
+ "latitude2": latitude2,
640
+ "collection": @name,
641
+ "limit": limit,
642
+ "skip": skip,
643
+ "geo": geo,
644
+ "radius": radius
645
+ }
646
+ result = @database.request("PUT", "_api/simple/within-rectangle", body: body)
647
+ return result if return_directly?(result)
648
+ result[:result].map do |x|
649
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
650
+ end
651
+ end
652
+
653
+ def fulltext(index:, attribute:, query:, limit: nil, skip: nil, warning: @server.warning)
654
+ warning_deprecated(warning, "fulltext")
655
+ body = {
656
+ "index": index,
657
+ "attribute": attribute,
658
+ "query": query,
659
+ "limit": limit,
660
+ "skip": skip
661
+ }
662
+ result = @database.request("PUT", "_api/simple/fulltext", body: body)
663
+ return result if return_directly?(result)
664
+ result[:result].map do |x|
665
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
666
+ end
667
+ end
668
+
669
+ # === IMPORT ===
670
+
671
+ def import(attributes:, values:, fromPrefix: nil,
672
+ toPrefix: nil, overwrite: nil, waitForSync: nil,
673
+ onDuplicate: nil, complete: nil, details: nil)
674
+ satisfy_category?(onDuplicate, [nil, "error", "update", "replace", "ignore"])
675
+ satisfy_category?(overwrite, [nil, "yes", "true", true])
676
+ satisfy_category?(complete, [nil, "yes", "true", true])
677
+ satisfy_category?(details, [nil, "yes", "true", true])
678
+ query = {
679
+ "collection": @name,
680
+ "fromPrefix": fromPrefix,
681
+ "toPrefix": toPrefix,
682
+ "overwrite": overwrite,
683
+ "waitForSync": waitForSync,
684
+ "onDuplicate": onDuplicate,
685
+ "complete": complete,
686
+ "details": details
687
+ }
688
+ body = "#{attributes}\n"
689
+ values[0].is_a?(Array) ? values.each{|x| body += "#{x}\n"} : body += "#{values}\n"
690
+ @database.request("POST", "_api/import", query: query,
691
+ body: body, skip_to_json: true)
692
+ end
693
+
694
+ def importJSON(body:, type: "auto", fromPrefix: nil,
695
+ toPrefix: nil, overwrite: nil, waitForSync: nil,
696
+ onDuplicate: nil, complete: nil, details: nil)
697
+ satisfy_category?(type, ["auto", "list", "documents"])
698
+ satisfy_category?(onDuplicate, [nil, "error", "update", "replace", "ignore"])
699
+ satisfy_category?(overwrite, [nil, "yes", "true", true])
700
+ satisfy_category?(complete, [nil, "yes", "true", true])
701
+ satisfy_category?(details, [nil, "yes", "true", true])
702
+ query = {
703
+ "collection": @name,
704
+ "type": type,
705
+ "fromPrefix": fromPrefix,
706
+ "toPrefix": toPrefix,
707
+ "overwrite": overwrite,
708
+ "waitForSync": waitForSync,
709
+ "onDuplicate": onDuplicate,
710
+ "complete": complete,
711
+ "details": details
712
+ }
713
+ @database.request("POST", "_api/import", query: query,
714
+ body: body)
715
+ end
716
+
717
+ # === EXPORT ===
718
+
719
+ def export(count: nil, restrict: nil, batchSize: nil,
720
+ flush: nil, flushWait: nil, limit: nil, ttl: nil)
721
+ query = { "collection": @name }
722
+ body = {
723
+ "count": count,
724
+ "restrict": restrict,
725
+ "batchSize": batchSize,
726
+ "flush": flush,
727
+ "flushWait": flushWait,
728
+ "limit": limit,
729
+ "ttl": ttl
730
+ }
731
+ result = @database.request("POST", "_api/export", body: body, query: query)
732
+ return reuslt if @server.async != false
733
+ @countExport = result[:count]
734
+ @hasMoreExport = result[:hasMore]
735
+ @idExport = result[:id]
736
+ if return_directly?(result) || result[:result][0].nil? || !result[:result][0].is_a?(Hash) || !result[:result][0].key?(:_key)
737
+ return result[:result]
738
+ else
739
+ return result[:result].map do |x|
740
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
741
+ end
742
+ end
743
+ end
744
+
745
+ def exportNext
746
+ unless @hasMoreExport
747
+ raise Arango::Error.new err: :no_other_export_next, data: {"hasMoreExport": @hasMoreExport}
748
+ else
749
+ query = { "collection": @name }
750
+ result = @database.request("PUT", "_api/export/#{@idExport}", query: query)
751
+ return result if @server.async != false
752
+ @countExport = result[:count]
753
+ @hasMoreExport = result[:hasMore]
754
+ @idExport = result[:id]
755
+ if return_directly?(result) || result[:result][0].nil? || !result[:result][0].is_a?(Hash) || !result[:result][0].key?(:_key)
756
+ return result[:result]
757
+ else
758
+ return result[:result].map do |x|
759
+ Arango::Document.new(name: x[:_key], collection: self, body: x)
760
+ end
761
+ end
762
+ end
763
+ end
764
+
765
+ # === INDEXES ===
766
+
767
+ def index(body: {}, id: nil, type: "hash", unique: nil, fields:,
768
+ sparse: nil, geoJson: nil, minLength: nil, deduplicate: nil)
769
+ Arango::Index.new(collection: self, body: body, id: id, type: type,
770
+ unique: unique, fields: fields, sparse: sparse, geoJson: geoJson,
771
+ minLength: minLength, deduplicate: deduplicate)
772
+ end
773
+
774
+ def indexes
775
+ query = { "collection": @name }
776
+ result = @database.request("GET", "_api/index", query: query)
777
+ return result if return_directly?(result)
778
+ result[:indexes].map do |x|
779
+ Arango::Index.new(body: x, id: x[:id], collection: self,
780
+ type: x[:type], unique: x[:unique], fields: x[:fields],
781
+ sparse: x[:sparse])
782
+ end
783
+ end
784
+
785
+ # === REPLICATION ===
786
+
787
+ def data(batchId:, from: nil, to: nil, chunkSize: nil,
788
+ includeSystem: nil, failOnUnknown: nil, ticks: nil, flush: nil)
789
+ query = {
790
+ "collection": @name,
791
+ "batchId": batchId,
792
+ "from": from,
793
+ "to": to,
794
+ "chunkSize": chunkSize,
795
+ "includeSystem": includeSystem,
796
+ "failOnUnknown": failOnUnknown,
797
+ "ticks": ticks,
798
+ "flush": flush
799
+ }
800
+ @database.request("GET", "_api/replication/dump", query: query)
801
+ end
802
+ alias dump data
803
+
804
+ # === USER ACCESS ===
805
+
806
+ def check_user(user)
807
+ user = Arango::User.new(user: user) if user.is_a?(String)
808
+ return user
809
+ end
810
+ private :check_user
811
+
812
+ def addUserAccess(grant:, user:)
813
+ user = check_user(user)
814
+ user.add_collection_access(grant: grant, database: @database.name, collection: @name)
815
+ end
816
+
817
+ def revokeUserAccess(user:)
818
+ user = check_user(user)
819
+ user.clear_collection_access(database: @database.name, collection: @name)
820
+ end
821
+
822
+ def userAccess(user:)
823
+ user = check_user(user)
824
+ user.collection_access(database: @database.name, collection: @name)
825
+ end
826
+
827
+ # === GRAPH ===
828
+
829
+ def vertex(name: nil, body: {}, rev: nil, from: nil, to: nil)
830
+ if @type == :edge
831
+ raise Arango::Error.new err: :is_a_edge_collection, data: {"type": @type}
832
+ end
833
+ if @graph.nil?
834
+ Arango::Document.new(name: name, body: body, rev: rev, collection: self)
835
+ else
836
+ Arango::Vertex.new(name: name, body: body, rev: rev, collection: self)
837
+ end
838
+ end
839
+
840
+ def edge(name: nil, body: {}, rev: nil, from: nil, to: nil)
841
+ if @type == :document
842
+ raise Arango::Error.new err: :is_a_document_collection, data: {"type": @type}
843
+ end
844
+ if @graph.nil?
845
+ Arango::Document.new(name: name, body: body, rev: rev, collection: self)
846
+ else
847
+ Arango::Edge.new(name: name, body: body, rev: rev, from: from, to: to,
848
+ collection: self)
849
+ end
850
+ end
851
+ end
852
+ end