arangorb 1.4.1 → 2.0.0

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