google-cloud-datastore 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,150 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Google
17
+ module Cloud
18
+ module Datastore
19
+ ##
20
+ # # Commit
21
+ #
22
+ # Object yielded from `commit` methods to allow multiple changes to be
23
+ # made in a single commit.
24
+ #
25
+ # @example
26
+ # gcloud = Google::Cloud.new
27
+ # datastore = gcloud.datastore
28
+ # datastore.commit do |c|
29
+ # c.save task1, task2
30
+ # c.delete entity1, entity2
31
+ # end
32
+ #
33
+ # See {Google::Cloud::Datastore::Dataset#commit} and
34
+ # {Google::Cloud::Datastore::Transaction#commit}.
35
+ class Commit
36
+ ##
37
+ # @private Create a new Commit object.
38
+ def initialize
39
+ @shared_upserts = []
40
+ @shared_inserts = []
41
+ @shared_updates = []
42
+ @shared_deletes = []
43
+ end
44
+
45
+ ##
46
+ # Saves entities to the Datastore.
47
+ #
48
+ # @param [Entity] entities One or more Entity objects to save.
49
+ #
50
+ # @example
51
+ # gcloud = Google::Cloud.new
52
+ # datastore = gcloud.datastore
53
+ # datastore.commit do |c|
54
+ # c.save task1, task2
55
+ # end
56
+ #
57
+ def save *entities
58
+ entities = Array(entities).flatten
59
+ @shared_upserts += entities unless entities.empty?
60
+ # Do not save yet
61
+ entities
62
+ end
63
+ alias_method :upsert, :save
64
+
65
+ ##
66
+ # Inserts entities to the Datastore.
67
+ #
68
+ # @param [Entity] entities One or more Entity objects to insert.
69
+ #
70
+ # @example
71
+ # gcloud = Google::Cloud.new
72
+ # datastore = gcloud.datastore
73
+ # datastore.commit do |c|
74
+ # c.insert task1, task2
75
+ # end
76
+ #
77
+ def insert *entities
78
+ entities = Array(entities).flatten
79
+ @shared_inserts += entities unless entities.empty?
80
+ # Do not insert yet
81
+ entities
82
+ end
83
+
84
+ ##
85
+ # Updates entities to the Datastore.
86
+ #
87
+ # @param [Entity] entities One or more Entity objects to update.
88
+ #
89
+ # @example
90
+ # gcloud = Google::Cloud.new
91
+ # datastore = gcloud.datastore
92
+ # datastore.commit do |c|
93
+ # c.update task1, task2
94
+ # end
95
+ #
96
+ def update *entities
97
+ entities = Array(entities).flatten
98
+ @shared_updates += entities unless entities.empty?
99
+ # Do not update yet
100
+ entities
101
+ end
102
+
103
+ ##
104
+ # Remove entities from the Datastore.
105
+ #
106
+ # @param [Entity, Key] entities_or_keys One or more Entity or Key
107
+ # objects to remove.
108
+ #
109
+ # @example
110
+ # gcloud = Google::Cloud.new
111
+ # datastore = gcloud.datastore
112
+ # datastore.commit do |c|
113
+ # c.delete task1, task2
114
+ # end
115
+ #
116
+ def delete *entities_or_keys
117
+ keys = Array(entities_or_keys).flatten.map do |e_or_k|
118
+ e_or_k.respond_to?(:key) ? e_or_k.key : e_or_k
119
+ end
120
+ @shared_deletes += keys unless keys.empty?
121
+ # Do not delete yet
122
+ true
123
+ end
124
+
125
+ # @private Mutations object to be committed.
126
+ def mutations
127
+ mutations = []
128
+ mutations += @shared_upserts.map do |entity|
129
+ Google::Datastore::V1::Mutation.new upsert: entity.to_grpc
130
+ end
131
+ mutations += @shared_inserts.map do |entity|
132
+ Google::Datastore::V1::Mutation.new insert: entity.to_grpc
133
+ end
134
+ mutations += @shared_updates.map do |entity|
135
+ Google::Datastore::V1::Mutation.new update: entity.to_grpc
136
+ end
137
+ mutations += @shared_deletes.map do |key|
138
+ Google::Datastore::V1::Mutation.new delete: key.to_grpc
139
+ end
140
+ mutations
141
+ end
142
+
143
+ # @private All entities saved in the commit.
144
+ def entities
145
+ @shared_upserts + @shared_inserts + @shared_updates
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright 2014 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/credentials"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Datastore
21
+ ##
22
+ # @private
23
+ #
24
+ # Authentication credentials to Google Cloud.
25
+ # The most common way to create this object is to provide the path
26
+ # to the JSON keyfile downloaded from Google Cloud.
27
+ #
28
+ # @see https://developers.google.com/accounts/docs/application-default-credentials
29
+ class Credentials < Google::Cloud::Credentials
30
+ SCOPE = ["https://www.googleapis.com/auth/datastore"]
31
+ PATH_ENV_VARS = %w(DATASTORE_KEYFILE GOOGLE_CLOUD_KEYFILE
32
+ GCLOUD_KEYFILE)
33
+ JSON_ENV_VARS = %w(DATASTORE_KEYFILE_JSON GOOGLE_CLOUD_KEYFILE_JSON
34
+ GCLOUD_KEYFILE_JSON)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Google
17
+ module Cloud
18
+ module Datastore
19
+ ##
20
+ # # Cursor
21
+ #
22
+ # Cursor is a point in query results. Cursors are returned in
23
+ # QueryResults.
24
+ #
25
+ # @example
26
+ # require "google/cloud"
27
+ #
28
+ # gcloud = Google::Cloud.new
29
+ # datastore = gcloud.datastore
30
+ #
31
+ # query = datastore.query("Task").
32
+ # where("done", "=", false)
33
+ #
34
+ # tasks = datastore.run query
35
+ # tasks.cursor #=> Cursor
36
+ #
37
+ class Cursor
38
+ # Base64 encoded array of bytes
39
+ def initialize cursor
40
+ @cursor = cursor
41
+ end
42
+
43
+ # Base64 encoded array of bytes
44
+ def to_s
45
+ @cursor
46
+ end
47
+
48
+ # @private
49
+ def inspect
50
+ "#{self.class}(#{@cursor})"
51
+ end
52
+
53
+ # @private
54
+ def == other
55
+ return false unless other.is_a? Cursor
56
+ @cursor == other.to_s
57
+ end
58
+
59
+ # @private
60
+ def <=> other
61
+ return -1 unless other.is_a? Cursor
62
+ @cursor <=> other.to_s
63
+ end
64
+
65
+ # @private byte array as a string
66
+ def to_grpc
67
+ Core::GRPCUtils.decode_bytes(@cursor)
68
+ end
69
+
70
+ # @private byte array as a string
71
+ def self.from_grpc grpc
72
+ grpc = String grpc
73
+ return nil if grpc.empty?
74
+ new Core::GRPCUtils.encode_bytes(grpc)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,667 @@
1
+ # Copyright 2014 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/core/gce"
17
+ require "google/cloud/datastore/grpc_utils"
18
+ require "google/cloud/datastore/credentials"
19
+ require "google/cloud/datastore/service"
20
+ require "google/cloud/datastore/commit"
21
+ require "google/cloud/datastore/entity"
22
+ require "google/cloud/datastore/key"
23
+ require "google/cloud/datastore/query"
24
+ require "google/cloud/datastore/gql_query"
25
+ require "google/cloud/datastore/cursor"
26
+ require "google/cloud/datastore/dataset/lookup_results"
27
+ require "google/cloud/datastore/dataset/query_results"
28
+
29
+ module Google
30
+ module Cloud
31
+ module Datastore
32
+ ##
33
+ # # Dataset
34
+ #
35
+ # Dataset is the data saved in a project's Datastore.
36
+ # Dataset is analogous to a database in relational database world.
37
+ #
38
+ # Google::Cloud::Datastore::Dataset is the main object for interacting
39
+ # with Google Datastore. {Google::Cloud::Datastore::Entity} objects are
40
+ # created, read, updated, and deleted by
41
+ # Google::Cloud::Datastore::Dataset.
42
+ #
43
+ # See {Google::Cloud#datastore}
44
+ #
45
+ # @example
46
+ # require "google/cloud"
47
+ #
48
+ # gcloud = Google::Cloud.new
49
+ # datastore = gcloud.datastore
50
+ #
51
+ # query = datastore.query("Task").
52
+ # where("done", "=", false)
53
+ #
54
+ # tasks = datastore.run query
55
+ #
56
+ class Dataset
57
+ ##
58
+ # @private The gRPC Service object.
59
+ attr_accessor :service
60
+
61
+ ##
62
+ # @private Creates a new Dataset instance.
63
+ #
64
+ # See {Google::Cloud#datastore}
65
+ def initialize service
66
+ @service = service
67
+ end
68
+
69
+ ##
70
+ # The Datastore project connected to.
71
+ #
72
+ # @example
73
+ # require "google/cloud"
74
+ #
75
+ # gcloud = Google::Cloud.new "my-todo-project",
76
+ # "/path/to/keyfile.json"
77
+ #
78
+ # datastore = gcloud.datastore
79
+ # datastore.project #=> "my-todo-project"
80
+ #
81
+ def project
82
+ service.project
83
+ end
84
+
85
+ ##
86
+ # @private Default project.
87
+ def self.default_project
88
+ ENV["DATASTORE_DATASET"] ||
89
+ ENV["DATASTORE_PROJECT"] ||
90
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
91
+ ENV["GCLOUD_PROJECT"] ||
92
+ Google::Cloud::Core::GCE.project_id
93
+ end
94
+
95
+ ##
96
+ # Generate IDs for a Key before creating an entity.
97
+ #
98
+ # @param [Key] incomplete_key A Key without `id` or `name` set.
99
+ # @param [String] count The number of new key IDs to create.
100
+ #
101
+ # @return [Array<Google::Cloud::Datastore::Key>]
102
+ #
103
+ # @example
104
+ # task_key = datastore.key "Task"
105
+ # task_keys = datastore.allocate_ids task_key, 5
106
+ #
107
+ def allocate_ids incomplete_key, count = 1
108
+ if incomplete_key.complete?
109
+ fail Google::Cloud::Datastore::KeyError,
110
+ "An incomplete key must be provided."
111
+ end
112
+
113
+ ensure_service!
114
+ incomplete_keys = count.times.map { incomplete_key.to_grpc }
115
+ allocate_res = service.allocate_ids(*incomplete_keys)
116
+ allocate_res.keys.map { |key| Key.from_grpc key }
117
+ end
118
+
119
+ ##
120
+ # Persist one or more entities to the Datastore.
121
+ #
122
+ # @param [Entity] entities One or more entity objects to be saved.
123
+ #
124
+ # @return [Array<Google::Cloud::Datastore::Entity>]
125
+ #
126
+ # @example Insert a new entity:
127
+ # task = datastore.entity "Task" do |t|
128
+ # t["type"] = "Personal"
129
+ # t["done"] = false
130
+ # t["priority"] = 4
131
+ # t["description"] = "Learn Cloud Datastore"
132
+ # end
133
+ # task.key.id #=> nil
134
+ # datastore.save task
135
+ # task.key.id #=> 123456
136
+ #
137
+ # @example Insert multiple new entities in a batch:
138
+ # task1 = datastore.entity "Task" do |t|
139
+ # t["type"] = "Personal"
140
+ # t["done"] = false
141
+ # t["priority"] = 4
142
+ # t["description"] = "Learn Cloud Datastore"
143
+ # end
144
+ #
145
+ # task2 = datastore.entity "Task" do |t|
146
+ # t["type"] = "Personal"
147
+ # t["done"] = false
148
+ # t["priority"] = 5
149
+ # t["description"] = "Integrate Cloud Datastore"
150
+ # end
151
+ #
152
+ # task_key1, task_key2 = datastore.save(task1, task2).map(&:key)
153
+ #
154
+ # @example Update an existing entity:
155
+ # task = datastore.find "Task", "sampleTask"
156
+ # task["priority"] = 5
157
+ # datastore.save task
158
+ #
159
+ def save *entities
160
+ commit { |c| c.save(*entities) }
161
+ end
162
+ alias_method :upsert, :save
163
+
164
+ ##
165
+ # Insert one or more entities to the Datastore. An InvalidArgumentError
166
+ # will raised if the entities cannot be inserted.
167
+ #
168
+ # @param [Entity] entities One or more entity objects to be inserted.
169
+ #
170
+ # @return [Array<Google::Cloud::Datastore::Entity>]
171
+ #
172
+ # @example Insert a new entity:
173
+ # task = datastore.entity "Task" do |t|
174
+ # t["type"] = "Personal"
175
+ # t["done"] = false
176
+ # t["priority"] = 4
177
+ # t["description"] = "Learn Cloud Datastore"
178
+ # end
179
+ # task.key.id #=> nil
180
+ # datastore.insert task
181
+ # task.key.id #=> 123456
182
+ #
183
+ # @example Insert multiple new entities in a batch:
184
+ # task1 = datastore.entity "Task" do |t|
185
+ # t["type"] = "Personal"
186
+ # t["done"] = false
187
+ # t["priority"] = 4
188
+ # t["description"] = "Learn Cloud Datastore"
189
+ # end
190
+ #
191
+ # task2 = datastore.entity "Task" do |t|
192
+ # t["type"] = "Personal"
193
+ # t["done"] = false
194
+ # t["priority"] = 5
195
+ # t["description"] = "Integrate Cloud Datastore"
196
+ # end
197
+ #
198
+ # task_key1, task_key2 = datastore.insert(task1, task2).map(&:key)
199
+ #
200
+ def insert *entities
201
+ commit { |c| c.insert(*entities) }
202
+ end
203
+
204
+ ##
205
+ # Update one or more entities to the Datastore. An InvalidArgumentError
206
+ # will raised if the entities cannot be updated.
207
+ #
208
+ # @param [Entity] entities One or more entity objects to be updated.
209
+ #
210
+ # @return [Array<Google::Cloud::Datastore::Entity>]
211
+ #
212
+ # @example Update an existing entity:
213
+ # task = datastore.find "Task", "sampleTask"
214
+ # task["done"] = true
215
+ # datastore.save task
216
+ #
217
+ # @example update multiple new entities in a batch:
218
+ # query = datastore.query("Task").where("done", "=", false)
219
+ # tasks = datastore.run query
220
+ # tasks.each { |t| t["done"] = true }
221
+ # datastore.update tasks
222
+ #
223
+ def update *entities
224
+ commit { |c| c.update(*entities) }
225
+ end
226
+
227
+ ##
228
+ # Remove entities from the Datastore.
229
+ #
230
+ # @param [Entity, Key] entities_or_keys One or more Entity or Key
231
+ # objects to remove.
232
+ #
233
+ # @return [Boolean] Returns `true` if successful
234
+ #
235
+ # @example
236
+ # gcloud = Google::Cloud.new
237
+ # datastore = gcloud.datastore
238
+ # datastore.delete task1, task2
239
+ #
240
+ def delete *entities_or_keys
241
+ commit { |c| c.delete(*entities_or_keys) }
242
+ true
243
+ end
244
+
245
+ ##
246
+ # Make multiple changes in a single commit.
247
+ #
248
+ # @yield [commit] a block for making changes
249
+ # @yieldparam [Commit] commit The object that changes are made on
250
+ #
251
+ # @return [Array<Google::Cloud::Datastore::Entity>] The entities that
252
+ # were persisted.
253
+ #
254
+ # @example
255
+ # gcloud = Google::Cloud.new
256
+ # datastore = gcloud.datastore
257
+ # datastore.commit do |c|
258
+ # c.save task3, task4
259
+ # c.delete task1, task2
260
+ # end
261
+ #
262
+ def commit
263
+ return unless block_given?
264
+ c = Commit.new
265
+ yield c
266
+
267
+ ensure_service!
268
+ commit_res = service.commit c.mutations
269
+ entities = c.entities
270
+ returned_keys = commit_res.mutation_results.map(&:key)
271
+ returned_keys.each_with_index do |key, index|
272
+ next if entities[index].nil?
273
+ entities[index].key = Key.from_grpc(key) unless key.nil?
274
+ end
275
+ entities.each { |e| e.key.freeze unless e.persisted? }
276
+ entities
277
+ end
278
+
279
+ ##
280
+ # Retrieve an entity by key.
281
+ #
282
+ # @param [Key, String] key_or_kind A Key object or `kind` string value.
283
+ # @param [Integer, String, nil] id_or_name The Key's `id` or `name`
284
+ # value if a `kind` was provided in the first parameter.
285
+ # @param [Symbol] consistency The non-transactional read consistency to
286
+ # use. Cannot be set to `:strong` for global queries. Accepted values
287
+ # are `:eventual` and `:strong`.
288
+ #
289
+ # The default consistency depends on the type of lookup used. See
290
+ # [Eventual Consistency in Google Cloud
291
+ # Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
292
+ # for more information.
293
+ #
294
+ # @return [Google::Cloud::Datastore::Entity, nil]
295
+ #
296
+ # @example Finding an entity with a key:
297
+ # task_key = datastore.key "Task", "sampleTask"
298
+ # task = datastore.find task_key
299
+ #
300
+ # @example Finding an entity with a `kind` and `id`/`name`:
301
+ # task = datastore.find "Task", "sampleTask"
302
+ #
303
+ def find key_or_kind, id_or_name = nil, consistency: nil
304
+ key = key_or_kind
305
+ unless key.is_a? Google::Cloud::Datastore::Key
306
+ key = Key.new key_or_kind, id_or_name
307
+ end
308
+ find_all(key, consistency: consistency).first
309
+ end
310
+ alias_method :get, :find
311
+
312
+ ##
313
+ # Retrieve the entities for the provided keys. The order of results is
314
+ # undefined and has no relation to the order of `keys` arguments.
315
+ #
316
+ # @param [Key] keys One or more Key objects to find records for.
317
+ # @param [Symbol] consistency The non-transactional read consistency to
318
+ # use. Cannot be set to `:strong` for global queries. Accepted values
319
+ # are `:eventual` and `:strong`.
320
+ #
321
+ # The default consistency depends on the type of lookup used. See
322
+ # [Eventual Consistency in Google Cloud
323
+ # Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
324
+ # for more information.
325
+ #
326
+ # @return [Google::Cloud::Datastore::Dataset::LookupResults]
327
+ #
328
+ # @example
329
+ # gcloud = Google::Cloud.new
330
+ # datastore = gcloud.datastore
331
+ #
332
+ # task_key1 = datastore.key "Task", "sampleTask1"
333
+ # task_key2 = datastore.key "Task", "sampleTask2"
334
+ # tasks = datastore.find_all task_key1, task_key2
335
+ #
336
+ def find_all *keys, consistency: nil
337
+ ensure_service!
338
+ check_consistency! consistency
339
+ lookup_res = service.lookup(*Array(keys).flatten.map(&:to_grpc),
340
+ consistency: consistency)
341
+ LookupResults.from_grpc lookup_res, service, consistency
342
+ end
343
+ alias_method :lookup, :find_all
344
+
345
+ ##
346
+ # Retrieve entities specified by a Query.
347
+ #
348
+ # @param [Query, GqlQuery] query The object with the search criteria.
349
+ # @param [String] namespace The namespace the query is to run within.
350
+ # @param [Symbol] consistency The non-transactional read consistency to
351
+ # use. Cannot be set to `:strong` for global queries. Accepted values
352
+ # are `:eventual` and `:strong`.
353
+ #
354
+ # The default consistency depends on the type of query used. See
355
+ # [Eventual Consistency in Google Cloud
356
+ # Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
357
+ # for more information.
358
+ #
359
+ # @return [Google::Cloud::Datastore::Dataset::QueryResults]
360
+ #
361
+ # @example
362
+ # query = datastore.query("Task").
363
+ # where("done", "=", false)
364
+ # tasks = datastore.run query
365
+ #
366
+ # @example Run an ancestor query with eventual consistency:
367
+ # task_list_key = datastore.key "TaskList", "default"
368
+ # query.kind("Task").
369
+ # ancestor(task_list_key)
370
+ #
371
+ # tasks = datastore.run query, consistency: :eventual
372
+ #
373
+ # @example Run the query within a namespace with the `namespace` option:
374
+ # query = datastore.query("Task").
375
+ # where("done", "=", false)
376
+ # tasks = datastore.run query, namespace: "ns~todo-project"
377
+ #
378
+ # @example Run the query with a GQL string.
379
+ # gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
380
+ # done: false
381
+ # tasks = datastore.run gql_query
382
+ #
383
+ # @example Run the GQL query within a namespace with `namespace` option:
384
+ # gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
385
+ # done: false
386
+ # tasks = datastore.run gql_query, namespace: "ns~todo-project"
387
+ #
388
+ def run query, namespace: nil, consistency: nil
389
+ ensure_service!
390
+ unless query.is_a?(Query) || query.is_a?(GqlQuery)
391
+ fail ArgumentError, "Cannot run a #{query.class} object."
392
+ end
393
+ check_consistency! consistency
394
+ query_res = service.run_query query.to_grpc, namespace,
395
+ consistency: consistency
396
+ QueryResults.from_grpc query_res, service, namespace,
397
+ query.to_grpc.dup
398
+ end
399
+ alias_method :run_query, :run
400
+
401
+ ##
402
+ # Creates a Datastore Transaction.
403
+ #
404
+ # @yield [tx] a block yielding a new transaction
405
+ # @yieldparam [Transaction] tx the transaction object
406
+ #
407
+ # @example Runs the given block in a database transaction:
408
+ # require "google/cloud"
409
+ #
410
+ # gcloud = Google::Cloud.new
411
+ # datastore = gcloud.datastore
412
+ #
413
+ # task = datastore.entity "Task", "sampleTask" do |t|
414
+ # t["type"] = "Personal"
415
+ # t["done"] = false
416
+ # t["priority"] = 4
417
+ # t["description"] = "Learn Cloud Datastore"
418
+ # end
419
+ #
420
+ # datastore.transaction do |tx|
421
+ # if tx.find(task.key).nil?
422
+ # tx.save task
423
+ # end
424
+ # end
425
+ #
426
+ # @example If no block is given, a Transaction object is returned:
427
+ # require "google/cloud"
428
+ #
429
+ # gcloud = Google::Cloud.new
430
+ # datastore = gcloud.datastore
431
+ #
432
+ # task = datastore.entity "Task", "sampleTask" do |t|
433
+ # t["type"] = "Personal"
434
+ # t["done"] = false
435
+ # t["priority"] = 4
436
+ # t["description"] = "Learn Cloud Datastore"
437
+ # end
438
+ #
439
+ # tx = datastore.transaction
440
+ # begin
441
+ # if tx.find(task.key).nil?
442
+ # tx.save task
443
+ # end
444
+ # tx.commit
445
+ # rescue
446
+ # tx.rollback
447
+ # end
448
+ #
449
+ def transaction
450
+ tx = Transaction.new service
451
+ return tx unless block_given?
452
+
453
+ begin
454
+ yield tx
455
+ tx.commit
456
+ rescue
457
+ begin
458
+ tx.rollback
459
+ rescue
460
+ raise TransactionError,
461
+ "Transaction failed to commit and rollback."
462
+ end
463
+ raise TransactionError, "Transaction failed to commit."
464
+ end
465
+ end
466
+
467
+ ##
468
+ # Create a new Query instance. This is a convenience method to make the
469
+ # creation of Query objects easier.
470
+ #
471
+ # @param [String] kinds The kind of entities to query. This is optional.
472
+ #
473
+ # @return [Google::Cloud::Datastore::Query]
474
+ #
475
+ # @example
476
+ # query = datastore.query("Task").
477
+ # where("done", "=", false)
478
+ # tasks = datastore.run query
479
+ #
480
+ # @example The previous example is equivalent to:
481
+ # query = Google::Cloud::Datastore::Query.new.
482
+ # kind("Task").
483
+ # where("done", "=", false)
484
+ # tasks = datastore.run query
485
+ #
486
+ def query *kinds
487
+ query = Query.new
488
+ query.kind(*kinds) unless kinds.empty?
489
+ query
490
+ end
491
+
492
+ ##
493
+ # Create a new GqlQuery instance. This is a convenience method to make
494
+ # the creation of GqlQuery objects easier.
495
+ #
496
+ # @param [String] query The GQL query string.
497
+ # @param [Hash] bindings Named bindings for the GQL query string, each
498
+ # key must match regex `[A-Za-z_$][A-Za-z_$0-9]*`, must not match
499
+ # regex `__.*__`, and must not be `""`. The value must be an `Object`
500
+ # that can be stored as an Entity property value, or a `Cursor`.
501
+ #
502
+ # @return [Google::Cloud::Datastore::GqlQuery]
503
+ #
504
+ # @example
505
+ # gql_query = datastore.gql "SELECT * FROM Task WHERE done = @done",
506
+ # done: false
507
+ # tasks = datastore.run gql_query
508
+ #
509
+ # @example The previous example is equivalent to:
510
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
511
+ # gql_query.query_string = "SELECT * FROM Task WHERE done = @done"
512
+ # gql_query.named_bindings = {done: false}
513
+ # tasks = datastore.run gql_query
514
+ #
515
+ def gql query, bindings = {}
516
+ gql = GqlQuery.new
517
+ gql.query_string = query
518
+ gql.named_bindings = bindings unless bindings.empty?
519
+ gql
520
+ end
521
+
522
+ ##
523
+ # Create a new Key instance. This is a convenience method to make the
524
+ # creation of Key objects easier.
525
+ #
526
+ # @param [Array<Array(String,(String|Integer|nil))>] path An optional
527
+ # list of pairs for the key's path. Each pair may include the key's
528
+ # kind (String) and an id (Integer) or name (String). This is
529
+ # optional.
530
+ # @param [String] project The project of the Key. This is optional.
531
+ # @param [String] namespace namespace kind of the Key. This is optional.
532
+ #
533
+ # @return [Google::Cloud::Datastore::Key]
534
+ #
535
+ # @example
536
+ # task_key = datastore.key "Task", "sampleTask"
537
+ #
538
+ # @example The previous example is equivalent to:
539
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
540
+ #
541
+ # @example Create an empty key:
542
+ # key = datastore.key
543
+ #
544
+ # @example Create an incomplete key:
545
+ # key = datastore.key "User"
546
+ #
547
+ # @example Create a key with a parent:
548
+ # key = datastore.key [["TaskList", "default"],
549
+ # ["Task", "sampleTask"]]
550
+ # key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
551
+ #
552
+ # @example Create a key with multi-level ancestry:
553
+ # key = datastore.key([
554
+ # ["User", "alice"],
555
+ # ["TaskList", "default"],
556
+ # ["Task", "sampleTask"]
557
+ # ])
558
+ # key.path #=> [["User", "alice"], ["TaskList", "default"], [ ... ]]
559
+ #
560
+ # @example Create an incomplete key with a parent:
561
+ # key = datastore.key "TaskList", "default", "Task"
562
+ # key.path #=> [["TaskList", "default"], ["Task", nil]]
563
+ #
564
+ # @example Create a key with a project and namespace:
565
+ # key = datastore.key ["TaskList", "default"], ["Task", "sampleTask"],
566
+ # project: "my-todo-project",
567
+ # namespace: "ns~todo-project"
568
+ # key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
569
+ # key.project #=> "my-todo-project",
570
+ # key.namespace #=> "ns~todo-project"
571
+ #
572
+ def key *path, project: nil, namespace: nil
573
+ path = path.flatten.each_slice(2).to_a # group in pairs
574
+ kind, id_or_name = path.pop
575
+ Key.new(kind, id_or_name).tap do |k|
576
+ k.project = project
577
+ k.namespace = namespace
578
+ unless path.empty?
579
+ k.parent = key path, project: project, namespace: namespace
580
+ end
581
+ end
582
+ end
583
+
584
+ ##
585
+ # Create a new empty Entity instance. This is a convenience method to
586
+ # make the creation of Entity objects easier.
587
+ #
588
+ # @param [Key, Array<Array(String,(String|Integer|nil))>] key_or_path An
589
+ # optional list of pairs for the key's path. Each pair may include the
590
+ # key's kind (String) and an id (Integer) or name (String). This is
591
+ # optional.
592
+ # @param [String] project The project of the Key. This is optional.
593
+ # @param [String] namespace namespace kind of the Key. This is optional.
594
+ # @yield [entity] a block yielding a new entity
595
+ # @yieldparam [Entity] entity the newly created entity object
596
+ #
597
+ # @return [Google::Cloud::Datastore::Entity]
598
+ #
599
+ # @example
600
+ # task = datastore.entity
601
+ #
602
+ # @example The previous example is equivalent to:
603
+ # task = Google::Cloud::Datastore::Entity.new
604
+ #
605
+ # @example The key can also be passed in as an object:
606
+ # task_key = datastore.key "Task", "sampleTask"
607
+ # task = datastore.entity task_key
608
+ #
609
+ # @example Or the key values can be passed in as parameters:
610
+ # task = datastore.entity "Task", "sampleTask"
611
+ #
612
+ # @example The previous example is equivalent to:
613
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
614
+ # task = Google::Cloud::Datastore::Entity.new
615
+ # task.key = task_key
616
+ #
617
+ # @example The newly created entity can also be configured using block:
618
+ # task = datastore.entity "Task", "sampleTask" do |t|
619
+ # t["type"] = "Personal"
620
+ # t["done"] = false
621
+ # t["priority"] = 4
622
+ # t["description"] = "Learn Cloud Datastore"
623
+ # end
624
+ #
625
+ # @example The previous example is equivalent to:
626
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
627
+ # task = Google::Cloud::Datastore::Entity.new
628
+ # task.key = task_key
629
+ # task["type"] = "Personal"
630
+ # task["done"] = false
631
+ # task["priority"] = 4
632
+ # task["description"] = "Learn Cloud Datastore"
633
+ #
634
+ def entity *key_or_path, project: nil, namespace: nil
635
+ entity = Entity.new
636
+
637
+ # Set the key
638
+ if key_or_path.flatten.first.is_a? Google::Cloud::Datastore::Key
639
+ entity.key = key_or_path.flatten.first
640
+ else
641
+ entity.key = key key_or_path, project: project, namespace: namespace
642
+ end
643
+
644
+ yield entity if block_given?
645
+
646
+ entity
647
+ end
648
+
649
+ protected
650
+
651
+ ##
652
+ # @private Raise an error unless an active connection to the service is
653
+ # available.
654
+ def ensure_service!
655
+ fail "Must have active connection to service" unless service
656
+ end
657
+
658
+ def check_consistency! consistency
659
+ fail(ArgumentError,
660
+ format("Consistency must be :eventual or :strong, not %s.",
661
+ consistency.inspect)
662
+ ) unless [:eventual, :strong, nil].include? consistency
663
+ end
664
+ end
665
+ end
666
+ end
667
+ end