google-cloud-datastore 0.20.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.
@@ -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