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.
- checksums.yaml +7 -0
- data/lib/google-cloud-datastore.rb +141 -0
- data/lib/google/cloud/datastore.rb +532 -0
- data/lib/google/cloud/datastore/commit.rb +150 -0
- data/lib/google/cloud/datastore/credentials.rb +38 -0
- data/lib/google/cloud/datastore/cursor.rb +79 -0
- data/lib/google/cloud/datastore/dataset.rb +667 -0
- data/lib/google/cloud/datastore/dataset/lookup_results.rb +222 -0
- data/lib/google/cloud/datastore/dataset/query_results.rb +389 -0
- data/lib/google/cloud/datastore/entity.rb +454 -0
- data/lib/google/cloud/datastore/errors.rb +43 -0
- data/lib/google/cloud/datastore/gql_query.rb +216 -0
- data/lib/google/cloud/datastore/grpc_utils.rb +140 -0
- data/lib/google/cloud/datastore/key.rb +289 -0
- data/lib/google/cloud/datastore/properties.rb +133 -0
- data/lib/google/cloud/datastore/query.rb +351 -0
- data/lib/google/cloud/datastore/service.rb +171 -0
- data/lib/google/cloud/datastore/transaction.rb +365 -0
- data/lib/google/cloud/datastore/version.rb +22 -0
- data/lib/google/datastore/v1/datastore_pb.rb +120 -0
- data/lib/google/datastore/v1/datastore_services_pb.rb +61 -0
- data/lib/google/datastore/v1/entity_pb.rb +63 -0
- data/lib/google/datastore/v1/query_pb.rb +131 -0
- metadata +236 -0
@@ -0,0 +1,454 @@
|
|
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/datastore/key"
|
17
|
+
require "google/cloud/datastore/properties"
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module Datastore
|
22
|
+
##
|
23
|
+
# # Entity
|
24
|
+
#
|
25
|
+
# Entity represents a Datastore record.
|
26
|
+
# Every Entity has a {Key}, and a list of properties.
|
27
|
+
#
|
28
|
+
# Entities in Datastore form a hierarchically structured space similar to
|
29
|
+
# the directory structure of a file system. When you create an entity, you
|
30
|
+
# can optionally designate another entity as its parent; the new entity is
|
31
|
+
# a child of the parent entity.
|
32
|
+
#
|
33
|
+
# @see https://cloud.google.com/datastore/docs/concepts/entities Entities,
|
34
|
+
# Properties, and Keys
|
35
|
+
#
|
36
|
+
# @example Create a new entity using a block:
|
37
|
+
# task = datastore.entity "Task", "sampleTask" do |t|
|
38
|
+
# t["type"] = "Personal"
|
39
|
+
# t["created"] = Time.now
|
40
|
+
# t["done"] = false
|
41
|
+
# t["priority"] = 4
|
42
|
+
# t["percent_complete"] = 10.0
|
43
|
+
# t["description"] = "Learn Cloud Datastore"
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @example Create a new entity belonging to an existing parent entity:
|
47
|
+
# task_key = datastore.key "Task", "sampleTask"
|
48
|
+
# task_key.parent = datastore.key "TaskList", "default"
|
49
|
+
#
|
50
|
+
# task = Google::Cloud::Datastore::Entity.new
|
51
|
+
# task.key = task_key
|
52
|
+
#
|
53
|
+
# task["type"] = "Personal"
|
54
|
+
# task["done"] = false
|
55
|
+
# task["priority"] = 4
|
56
|
+
# task["description"] = "Learn Cloud Datastore"
|
57
|
+
#
|
58
|
+
class Entity
|
59
|
+
##
|
60
|
+
# The Key that identifies the entity.
|
61
|
+
attr_reader :key
|
62
|
+
|
63
|
+
##
|
64
|
+
# Create a new Entity object.
|
65
|
+
def initialize
|
66
|
+
@properties = Properties.new
|
67
|
+
@key = Key.new
|
68
|
+
@_exclude_indexes = {}
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Retrieve a property value by providing the name.
|
73
|
+
#
|
74
|
+
# Property values are converted from the Datastore value type
|
75
|
+
# automatically. Blob properties are returned as StringIO objects.
|
76
|
+
# Location properties are returned as a Hash with `:longitude` and
|
77
|
+
# `:latitude` keys.
|
78
|
+
#
|
79
|
+
# @param [String, Symbol] prop_name The name of the property.
|
80
|
+
#
|
81
|
+
# @return [Object, nil] Returns `nil` if the property doesn't exist
|
82
|
+
#
|
83
|
+
# @example Properties can be retrieved with a string name:
|
84
|
+
# require "google/cloud"
|
85
|
+
#
|
86
|
+
# gcloud = Google::Cloud.new
|
87
|
+
# datastore = gcloud.datastore
|
88
|
+
# task = datastore.find "Task", "sampleTask"
|
89
|
+
# task["description"] #=> "Learn Cloud Datastore"
|
90
|
+
#
|
91
|
+
# @example Or with a symbol name:
|
92
|
+
# require "google/cloud"
|
93
|
+
#
|
94
|
+
# gcloud = Google::Cloud.new
|
95
|
+
# datastore = gcloud.datastore
|
96
|
+
# task = datastore.find "Task", "sampleTask"
|
97
|
+
# task[:description] #=> "Learn Cloud Datastore"
|
98
|
+
#
|
99
|
+
# @example Getting a blob value returns a StringIO object:
|
100
|
+
# require "google/cloud"
|
101
|
+
#
|
102
|
+
# gcloud = Google::Cloud.new
|
103
|
+
# datastore = gcloud.datastore
|
104
|
+
# user = datastore.find "User", "alice"
|
105
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
106
|
+
#
|
107
|
+
# @example Getting a geo point value returns a Hash:
|
108
|
+
# require "google/cloud"
|
109
|
+
#
|
110
|
+
# gcloud = Google::Cloud.new
|
111
|
+
# datastore = gcloud.datastore
|
112
|
+
# user = datastore.find "User", "alice"
|
113
|
+
# user["location"] #=> { longitude: -122.0862462,
|
114
|
+
# # latitude: 37.4220041 }
|
115
|
+
#
|
116
|
+
# @example Getting a blob value returns a StringIO object:
|
117
|
+
# require "google/cloud"
|
118
|
+
#
|
119
|
+
# gcloud = Google::Cloud.new
|
120
|
+
# datastore = gcloud.datastore
|
121
|
+
# user = datastore.find "User", "alice"
|
122
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
123
|
+
#
|
124
|
+
def [] prop_name
|
125
|
+
properties[prop_name]
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Set a property value by name.
|
130
|
+
#
|
131
|
+
# Property values are converted to use the proper Datastore value type
|
132
|
+
# automatically. Use an IO-compatible object (File, StringIO, Tempfile)
|
133
|
+
# to indicate the property value should be stored as a Datastore `blob`.
|
134
|
+
# IO-compatible objects are converted to StringIO objects when they are
|
135
|
+
# set. Use a Hash with `:longitude` and `:latitude` keys to indicate the
|
136
|
+
# property value should be stored as a Geo Point/LatLng.
|
137
|
+
#
|
138
|
+
# @param [String, Symbol] prop_name The name of the property.
|
139
|
+
# @param [Object] prop_value The value of the property.
|
140
|
+
#
|
141
|
+
# @example Properties can be set with a string name:
|
142
|
+
# require "google/cloud"
|
143
|
+
#
|
144
|
+
# gcloud = Google::Cloud.new
|
145
|
+
# datastore = gcloud.datastore
|
146
|
+
# task = datastore.find "Task", "sampleTask"
|
147
|
+
# task["description"] = "Learn Cloud Datastore"
|
148
|
+
# task["tags"] = ["fun", "programming"]
|
149
|
+
#
|
150
|
+
# @example Or with a symbol name:
|
151
|
+
# require "google/cloud"
|
152
|
+
#
|
153
|
+
# gcloud = Google::Cloud.new
|
154
|
+
# datastore = gcloud.datastore
|
155
|
+
# task = datastore.find "Task", "sampleTask"
|
156
|
+
# task[:description] = "Learn Cloud Datastore"
|
157
|
+
# task[:tags] = ["fun", "programming"]
|
158
|
+
#
|
159
|
+
# @example Setting a blob value using an IO:
|
160
|
+
# require "google/cloud"
|
161
|
+
#
|
162
|
+
# gcloud = Google::Cloud.new
|
163
|
+
# datastore = gcloud.datastore
|
164
|
+
# user = datastore.find "User", "alice"
|
165
|
+
# user["avatar"] = File.open "/avatars/alice.png"
|
166
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
167
|
+
#
|
168
|
+
# @example Setting a geo point value using a Hash:
|
169
|
+
# require "google/cloud"
|
170
|
+
#
|
171
|
+
# gcloud = Google::Cloud.new
|
172
|
+
# datastore = gcloud.datastore
|
173
|
+
# user = datastore.find "User", "alice"
|
174
|
+
# user["location"] = { longitude: -122.0862462, latitude: 37.4220041 }
|
175
|
+
#
|
176
|
+
# @example Setting a blob value using an IO:
|
177
|
+
# require "google/cloud"
|
178
|
+
#
|
179
|
+
# gcloud = Google::Cloud.new
|
180
|
+
# datastore = gcloud.datastore
|
181
|
+
# user = datastore.find "User", "alice"
|
182
|
+
# user["avatar"] = File.open "/avatars/alice.png"
|
183
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
184
|
+
#
|
185
|
+
def []= prop_name, prop_value
|
186
|
+
properties[prop_name] = prop_value
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Retrieve properties in a hash-like structure.
|
191
|
+
# Properties can be accessed or set by string or symbol.
|
192
|
+
#
|
193
|
+
# @return [Google::Cloud::Datastore::Properties]
|
194
|
+
#
|
195
|
+
# @example
|
196
|
+
# task.properties[:description] = "Learn Cloud Datastore"
|
197
|
+
# task.properties["description"] #=> "Learn Cloud Datastore"
|
198
|
+
#
|
199
|
+
# task.properties.each do |name, value|
|
200
|
+
# puts "property #{name} has a value of #{value}"
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# @example A property's existence can be determined by calling `exist?`:
|
204
|
+
# task.properties.exist? :description #=> true
|
205
|
+
# task.properties.exist? "description" #=> true
|
206
|
+
# task.properties.exist? :expiration #=> false
|
207
|
+
#
|
208
|
+
# @example A property can be removed from the entity:
|
209
|
+
# task.properties.delete :description
|
210
|
+
# task.save
|
211
|
+
#
|
212
|
+
# @example The properties can be converted to a hash:
|
213
|
+
# prop_hash = task.properties.to_h
|
214
|
+
#
|
215
|
+
attr_reader :properties
|
216
|
+
|
217
|
+
##
|
218
|
+
# Sets the {Google::Cloud::Datastore::Key} that identifies the entity.
|
219
|
+
#
|
220
|
+
# Once the entity is saved, the key is frozen and immutable. Trying to
|
221
|
+
# set a key when immutable will raise a `RuntimeError`.
|
222
|
+
#
|
223
|
+
# @example The key can be set before the entity is saved:
|
224
|
+
# require "google/cloud"
|
225
|
+
#
|
226
|
+
# gcloud = Google::Cloud.new
|
227
|
+
# datastore = gcloud.datastore
|
228
|
+
# task = Google::Cloud::Datastore::Entity.new
|
229
|
+
# task.key = datastore.key "Task"
|
230
|
+
# datastore.save task
|
231
|
+
#
|
232
|
+
# @example Once the entity is saved, the key is frozen and immutable:
|
233
|
+
# require "google/cloud"
|
234
|
+
#
|
235
|
+
# gcloud = Google::Cloud.new
|
236
|
+
# datastore = gcloud.datastore
|
237
|
+
# task = datastore.find "Task", "sampleTask"
|
238
|
+
# task.persisted? #=> true
|
239
|
+
# task.key = datastore.key "Task" #=> RuntimeError
|
240
|
+
# task.key.frozen? #=> true
|
241
|
+
# task.key.id = 9876543221 #=> RuntimeError
|
242
|
+
#
|
243
|
+
def key= new_key
|
244
|
+
fail "This entity's key is immutable." if persisted?
|
245
|
+
@key = new_key
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Indicates if the record is persisted. Default is false.
|
250
|
+
#
|
251
|
+
# @example
|
252
|
+
# require "google/cloud"
|
253
|
+
#
|
254
|
+
# gcloud = Google::Cloud.new
|
255
|
+
# datastore = gcloud.datastore
|
256
|
+
#
|
257
|
+
# task = Google::Cloud::Datastore::Entity.new
|
258
|
+
# task.persisted? #=> false
|
259
|
+
#
|
260
|
+
# task = datastore.find "Task", "sampleTask"
|
261
|
+
# task.persisted? #=> true
|
262
|
+
#
|
263
|
+
def persisted?
|
264
|
+
@key && @key.frozen?
|
265
|
+
end
|
266
|
+
|
267
|
+
##
|
268
|
+
# Indicates if a property is flagged to be excluded from the Datastore
|
269
|
+
# indexes. The default value is `false`. This is another way of saying
|
270
|
+
# that values are indexed by default.
|
271
|
+
#
|
272
|
+
# If the property is multi-valued, each value in the list can be managed
|
273
|
+
# separately for exclusion from indexing. Calling this method for a
|
274
|
+
# multi-valued property will return an array that contains the
|
275
|
+
# `excluded` boolean value for each corresponding value in the property.
|
276
|
+
# For example, if a multi-valued property contains `["a", "b"]`, and
|
277
|
+
# only the value `"b"` is indexed (meaning that `"a"`' is excluded), the
|
278
|
+
# return value for this method will be `[true, false]`.
|
279
|
+
#
|
280
|
+
# @see https://cloud.google.com/datastore/docs/concepts/indexes#Datastore_Unindexed_properties
|
281
|
+
# Unindexed properties
|
282
|
+
#
|
283
|
+
# @example Single property values will return a single flag setting:
|
284
|
+
# task["priority"] = 4
|
285
|
+
# task.exclude_from_indexes? "priority" #=> false
|
286
|
+
#
|
287
|
+
# @example A multi-valued property will return array of flag settings:
|
288
|
+
# task["tags"] = ["fun", "programming"]
|
289
|
+
# task.exclude_from_indexes! "tags", [true, false]
|
290
|
+
#
|
291
|
+
# task.exclude_from_indexes? "tags" #=> [true, false]
|
292
|
+
#
|
293
|
+
def exclude_from_indexes? name
|
294
|
+
value = self[name]
|
295
|
+
flag = @_exclude_indexes[name.to_s]
|
296
|
+
map_exclude_flag_to_value flag, value
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# Sets whether a property should be excluded from the Datastore indexes.
|
301
|
+
# Setting `true` will exclude the property from the indexes. Setting
|
302
|
+
# `false` will include the property on any applicable indexes. The
|
303
|
+
# default value is `false`. This is another way of saying that values
|
304
|
+
# are indexed by default.
|
305
|
+
#
|
306
|
+
# If the property is multi-valued, each value in the list can be managed
|
307
|
+
# separately for exclusion from indexing. When you call this method for
|
308
|
+
# a multi-valued property, you can pass either a single boolean argument
|
309
|
+
# to be applied to all of the values, or an array that contains the
|
310
|
+
# boolean argument for each corresponding value in the property. For
|
311
|
+
# example, if a multi-valued property contains `["a", "b"]`, and only
|
312
|
+
# the value `"b"` should be indexed (meaning that `"a"`' should be
|
313
|
+
# excluded), you should pass the array: `[true, false]`.
|
314
|
+
#
|
315
|
+
# @param [String] name the property name
|
316
|
+
# @param [Boolean, Array<Boolean>, nil] flag whether the value or values
|
317
|
+
# should be excluded from indexing
|
318
|
+
# @yield [value] a block yielding each value of the property
|
319
|
+
# @yieldparam [Object] value a value of the property
|
320
|
+
# @yieldreturn [Boolean] `true` if the value should be excluded from
|
321
|
+
# indexing
|
322
|
+
#
|
323
|
+
# @see https://cloud.google.com/datastore/docs/concepts/indexes#Datastore_Unindexed_properties
|
324
|
+
# Unindexed properties
|
325
|
+
#
|
326
|
+
# @example
|
327
|
+
# entity["priority"] = 4
|
328
|
+
# entity.exclude_from_indexes! "priority", true
|
329
|
+
#
|
330
|
+
# @example Multi-valued properties can be given multiple exclude flags:
|
331
|
+
# entity["tags"] = ["fun", "programming"]
|
332
|
+
# entity.exclude_from_indexes! "tags", [true, false]
|
333
|
+
#
|
334
|
+
# @example Or, a single flag can be applied to all values in a property:
|
335
|
+
# entity["tags"] = ["fun", "programming"]
|
336
|
+
# entity.exclude_from_indexes! "tags", true
|
337
|
+
#
|
338
|
+
# @example Flags can also be set with a block:
|
339
|
+
# entity["priority"] = 4
|
340
|
+
# entity.exclude_from_indexes! "priority" do |priority|
|
341
|
+
# priority > 4
|
342
|
+
# end
|
343
|
+
#
|
344
|
+
def exclude_from_indexes! name, flag = nil, &block
|
345
|
+
name = name.to_s
|
346
|
+
flag = block if block_given?
|
347
|
+
if flag.nil?
|
348
|
+
@_exclude_indexes.delete name
|
349
|
+
else
|
350
|
+
@_exclude_indexes[name] = flag
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
##
|
355
|
+
# The number of bytes the Entity will take to serialize during API
|
356
|
+
# calls.
|
357
|
+
def serialized_size
|
358
|
+
to_grpc.to_proto.length
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# @private Convert the Entity to a Google::Datastore::V1::Entity
|
363
|
+
# object.
|
364
|
+
def to_grpc
|
365
|
+
grpc = Google::Datastore::V1::Entity.new(
|
366
|
+
properties: @properties.to_grpc
|
367
|
+
)
|
368
|
+
grpc.key = @key.to_grpc unless @key.nil?
|
369
|
+
update_properties_indexed! grpc.properties
|
370
|
+
grpc
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# @private Create a new Entity from a Google::Datastore::V1::Key
|
375
|
+
# object.
|
376
|
+
def self.from_grpc grpc
|
377
|
+
entity = Entity.new
|
378
|
+
entity.key = Key.from_grpc grpc.key
|
379
|
+
entity.send :properties=, Properties.from_grpc(grpc.properties)
|
380
|
+
entity.send :update_exclude_indexes!, grpc.properties
|
381
|
+
entity
|
382
|
+
end
|
383
|
+
|
384
|
+
protected
|
385
|
+
|
386
|
+
##
|
387
|
+
# @private Allow friendly objects to set Properties object.
|
388
|
+
attr_writer :properties
|
389
|
+
|
390
|
+
# rubocop:disable all
|
391
|
+
# Disabled rubocop because this is intentionally complex.
|
392
|
+
|
393
|
+
##
|
394
|
+
# @private Map the exclude flag object to value.
|
395
|
+
# The flag object can be a boolean, Proc, or Array.
|
396
|
+
# Procs will be called and passed in the value.
|
397
|
+
# This will return an array of flags for an array value.
|
398
|
+
def map_exclude_flag_to_value flag, value
|
399
|
+
if value.is_a? Array
|
400
|
+
if flag.is_a? Proc
|
401
|
+
value.map { |v| !!flag.call(v) }
|
402
|
+
elsif flag.is_a? Array
|
403
|
+
(flag + Array.new(value.size)).slice(0, value.size).map do |v|
|
404
|
+
!!v
|
405
|
+
end
|
406
|
+
else
|
407
|
+
value.map { |_| !!flag }
|
408
|
+
end
|
409
|
+
else
|
410
|
+
if flag.is_a? Proc
|
411
|
+
!!flag.call(value)
|
412
|
+
elsif flag.is_a? Array
|
413
|
+
!!flag.first
|
414
|
+
else
|
415
|
+
!!flag
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
##
|
421
|
+
# @private Update the exclude data after a new object is created.
|
422
|
+
def update_exclude_indexes! grpc_map
|
423
|
+
@_exclude_indexes = {}
|
424
|
+
grpc_map.each do |name, value|
|
425
|
+
next if value.nil?
|
426
|
+
@_exclude_indexes[name] = value.exclude_from_indexes
|
427
|
+
unless value.array_value.nil?
|
428
|
+
exclude = value.array_value.values.map(&:exclude_from_indexes)
|
429
|
+
@_exclude_indexes[name] = exclude
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
##
|
435
|
+
# @private Update the indexed values before the object is saved.
|
436
|
+
def update_properties_indexed! grpc_map
|
437
|
+
grpc_map.each do |name, value|
|
438
|
+
next if value.nil?
|
439
|
+
excluded = exclude_from_indexes? name
|
440
|
+
if excluded.is_a? Array
|
441
|
+
value.array_value.values.each_with_index do |v, i|
|
442
|
+
v.exclude_from_indexes = excluded[i]
|
443
|
+
end
|
444
|
+
else
|
445
|
+
value.exclude_from_indexes = excluded
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
# rubocop:enable all
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|