google-cloud-datastore 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,133 @@
|
|
1
|
+
# Copyright 2015 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 "stringio"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Datastore
|
21
|
+
##
|
22
|
+
# # Properties
|
23
|
+
#
|
24
|
+
# Hash-like data structure for Datastore properties.
|
25
|
+
#
|
26
|
+
# See {Entity#properties}
|
27
|
+
class Properties
|
28
|
+
def initialize properties = {}
|
29
|
+
@hash = {}
|
30
|
+
properties.each do |key, value|
|
31
|
+
key = ensure_key_type key
|
32
|
+
value = ensure_value_type value
|
33
|
+
@hash[key] = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def [] key
|
38
|
+
key = ensure_key_type key
|
39
|
+
@hash[key]
|
40
|
+
end
|
41
|
+
alias_method :read, :[]
|
42
|
+
|
43
|
+
def []= key, value
|
44
|
+
key = ensure_key_type key
|
45
|
+
value = ensure_value_type value
|
46
|
+
@hash[key] = value
|
47
|
+
end
|
48
|
+
alias_method :write, :[]=
|
49
|
+
|
50
|
+
def exist? key
|
51
|
+
key = ensure_key_type key
|
52
|
+
@hash.key? key
|
53
|
+
end
|
54
|
+
|
55
|
+
def fetch key, &_block
|
56
|
+
key = ensure_key_type key
|
57
|
+
@hash[key] = yield unless exist? key
|
58
|
+
@hash[key]
|
59
|
+
end
|
60
|
+
|
61
|
+
def each &block
|
62
|
+
@hash.each(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete key, &block
|
66
|
+
key = ensure_key_type key
|
67
|
+
@hash.delete key, &block
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_h
|
71
|
+
@hash.dup
|
72
|
+
end
|
73
|
+
alias_method :to_hash, :to_h
|
74
|
+
|
75
|
+
def to_grpc
|
76
|
+
Hash[@hash.map { |(k, v)| [k.to_s, Core::GRPCUtils.to_value(v)] }]
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.from_grpc grpc_map
|
80
|
+
# For some reason Google::Protobuf::Map#map isn't returning the value.
|
81
|
+
# It returns nil every time. COnvert to Hash to get actual objects.
|
82
|
+
grpc_hash = Core::GRPCUtils.map_to_hash grpc_map
|
83
|
+
grpc_array = grpc_hash.map do |(k, v)|
|
84
|
+
[k.to_s, Core::GRPCUtils.from_value(v)]
|
85
|
+
end
|
86
|
+
new Hash[grpc_array]
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
##
|
92
|
+
# Ensures the key is the proper type,
|
93
|
+
# otherwise a PropertyError is raised.
|
94
|
+
def ensure_key_type key
|
95
|
+
return key.to_str if key.respond_to? :to_str
|
96
|
+
fail "Property key #{key} must be a String."
|
97
|
+
end
|
98
|
+
|
99
|
+
# rubocop:disable all
|
100
|
+
# Disabled rubocop because this needs to match Core::GRPCUtils.to_value
|
101
|
+
|
102
|
+
##
|
103
|
+
# Ensures the value is a type that can be persisted,
|
104
|
+
# otherwise a PropertyError is raised.
|
105
|
+
def ensure_value_type value
|
106
|
+
if Google::Cloud::Datastore::Key === value ||
|
107
|
+
Google::Cloud::Datastore::Entity === value ||
|
108
|
+
NilClass === value ||
|
109
|
+
TrueClass === value ||
|
110
|
+
FalseClass === value ||
|
111
|
+
Float === value ||
|
112
|
+
Integer === value ||
|
113
|
+
String === value ||
|
114
|
+
Array === value
|
115
|
+
return value
|
116
|
+
elsif value.respond_to?(:to_time)
|
117
|
+
return value
|
118
|
+
elsif value.respond_to?(:to_hash) && value.keys.sort == [:latitude, :longitude]
|
119
|
+
return value
|
120
|
+
elsif value.respond_to?(:read) && value.respond_to?(:rewind)
|
121
|
+
# Always convert an IO object to a StringIO when storing.
|
122
|
+
value.rewind
|
123
|
+
return StringIO.new(value.read.force_encoding("ASCII-8BIT"))
|
124
|
+
elsif defined?(BigDecimal) && BigDecimal === value
|
125
|
+
return value
|
126
|
+
end
|
127
|
+
fail PropertyError, "A property of type #{value.class} is not supported."
|
128
|
+
end
|
129
|
+
# rubocop:enable all
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,351 @@
|
|
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/entity"
|
17
|
+
require "google/cloud/datastore/key"
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module Datastore
|
22
|
+
##
|
23
|
+
# # Query
|
24
|
+
#
|
25
|
+
# Represents the search criteria against a Datastore.
|
26
|
+
#
|
27
|
+
# @see https://cloud.google.com/datastore/docs/concepts/queries Datastore
|
28
|
+
# Queries
|
29
|
+
# @see https://cloud.google.com/datastore/docs/concepts/metadataqueries
|
30
|
+
# Datastore Metadata
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# query = Google::Cloud::Datastore::Query.new
|
34
|
+
# query.kind("Task").
|
35
|
+
# where("done", "=", false).
|
36
|
+
# where("priority", ">=", 4).
|
37
|
+
# order("priority", :desc)
|
38
|
+
#
|
39
|
+
# tasks = datastore.run query
|
40
|
+
#
|
41
|
+
class Query
|
42
|
+
##
|
43
|
+
# Returns a new query object.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# query = Google::Cloud::Datastore::Query.new
|
47
|
+
#
|
48
|
+
def initialize
|
49
|
+
@grpc = Google::Datastore::V1::Query.new
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Add the kind of entities to query.
|
54
|
+
#
|
55
|
+
# Special entity kinds such as `__namespace__`, `__kind__`, and
|
56
|
+
# `__property__` can be used for [metadata
|
57
|
+
# queries](https://cloud.google.com/datastore/docs/concepts/metadataqueries).
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# query = Google::Cloud::Datastore::Query.new
|
61
|
+
# query.kind "Task"
|
62
|
+
#
|
63
|
+
# tasks = datastore.run query
|
64
|
+
#
|
65
|
+
def kind *kinds
|
66
|
+
kinds.each do |kind|
|
67
|
+
grpc_kind = Google::Datastore::V1::KindExpression.new(
|
68
|
+
name: kind)
|
69
|
+
@grpc.kind << grpc_kind
|
70
|
+
end
|
71
|
+
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Add a property filter to the query.
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# query = Google::Cloud::Datastore::Query.new
|
80
|
+
# query.kind("Task").
|
81
|
+
# where("done", "=", false)
|
82
|
+
#
|
83
|
+
# tasks = datastore.run query
|
84
|
+
#
|
85
|
+
# @example Add a composite property filter:
|
86
|
+
# query = Google::Cloud::Datastore::Query.new
|
87
|
+
# query.kind("Task").
|
88
|
+
# where("done", "=", false).
|
89
|
+
# where("priority", ">=", 4)
|
90
|
+
#
|
91
|
+
# tasks = datastore.run query
|
92
|
+
#
|
93
|
+
# @example Add an inequality filter on a **single** property only:
|
94
|
+
# query = Google::Cloud::Datastore::Query.new
|
95
|
+
# query.kind("Task").
|
96
|
+
# where("created", ">=", Time.utc(1990, 1, 1)).
|
97
|
+
# where("created", "<", Time.utc(2000, 1, 1))
|
98
|
+
#
|
99
|
+
# tasks = datastore.run query
|
100
|
+
#
|
101
|
+
# @example Add a composite filter on an array property:
|
102
|
+
# query = Google::Cloud::Datastore::Query.new
|
103
|
+
# query.kind("Task").
|
104
|
+
# where("tag", "=", "fun").
|
105
|
+
# where("tag", "=", "programming")
|
106
|
+
#
|
107
|
+
# tasks = datastore.run query
|
108
|
+
#
|
109
|
+
# @example Add an inequality filter on an array property :
|
110
|
+
# query = Google::Cloud::Datastore::Query.new
|
111
|
+
# query.kind("Task").
|
112
|
+
# where("tag", ">", "learn").
|
113
|
+
# where("tag", "<", "math")
|
114
|
+
#
|
115
|
+
# tasks = datastore.run query
|
116
|
+
#
|
117
|
+
# @example Add a key filter using the special property `__key__`:
|
118
|
+
# query = Google::Cloud::Datastore::Query.new
|
119
|
+
# query.kind("Task").
|
120
|
+
# where("__key__", ">", datastore.key("Task", "someTask"))
|
121
|
+
#
|
122
|
+
# tasks = datastore.run query
|
123
|
+
#
|
124
|
+
# @example Add a key filter to a *kindless* query:
|
125
|
+
# last_seen_key = datastore.key "Task", "a"
|
126
|
+
# query = Google::Cloud::Datastore::Query.new
|
127
|
+
# query.where("__key__", ">", last_seen_key)
|
128
|
+
#
|
129
|
+
# tasks = datastore.run query
|
130
|
+
#
|
131
|
+
def where name, operator, value
|
132
|
+
@grpc.filter ||= Google::Datastore::V1::Filter.new(
|
133
|
+
composite_filter: Google::Datastore::V1::CompositeFilter.new(
|
134
|
+
op: :AND
|
135
|
+
)
|
136
|
+
)
|
137
|
+
@grpc.filter.composite_filter.filters << \
|
138
|
+
Google::Datastore::V1::Filter.new(
|
139
|
+
property_filter: Google::Datastore::V1::PropertyFilter.new(
|
140
|
+
property: Google::Datastore::V1::PropertyReference.new(
|
141
|
+
name: name),
|
142
|
+
op: Core::GRPCUtils.to_prop_filter_op(operator),
|
143
|
+
value: Core::GRPCUtils.to_value(value)
|
144
|
+
)
|
145
|
+
)
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
alias_method :filter, :where
|
150
|
+
|
151
|
+
##
|
152
|
+
# Add a filter for entities that inherit from a key.
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# task_list_key = datastore.key "TaskList", "default"
|
156
|
+
#
|
157
|
+
# query = Google::Cloud::Datastore::Query.new
|
158
|
+
# query.kind("Task").
|
159
|
+
# ancestor(task_list_key)
|
160
|
+
#
|
161
|
+
# tasks = datastore.run query
|
162
|
+
#
|
163
|
+
def ancestor parent
|
164
|
+
# Use key if given an entity
|
165
|
+
parent = parent.key if parent.respond_to? :key
|
166
|
+
where "__key__", "~", parent
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Sort the results by a property name.
|
171
|
+
# By default, an ascending sort order will be used.
|
172
|
+
# To sort in descending order, provide a second argument
|
173
|
+
# of a string or symbol that starts with "d".
|
174
|
+
#
|
175
|
+
# @example With ascending sort order:
|
176
|
+
# query = Google::Cloud::Datastore::Query.new
|
177
|
+
# query.kind("Task").
|
178
|
+
# order("created")
|
179
|
+
#
|
180
|
+
# tasks = datastore.run query
|
181
|
+
#
|
182
|
+
# @example With descending sort order:
|
183
|
+
# query = Google::Cloud::Datastore::Query.new
|
184
|
+
# query.kind("Task").
|
185
|
+
# order("created", :desc)
|
186
|
+
#
|
187
|
+
# tasks = datastore.run query
|
188
|
+
#
|
189
|
+
# @example With multiple sort orders:
|
190
|
+
# query = Google::Cloud::Datastore::Query.new
|
191
|
+
# query.kind("Task").
|
192
|
+
# order("priority", :desc).
|
193
|
+
# order("created")
|
194
|
+
#
|
195
|
+
# tasks = datastore.run query
|
196
|
+
#
|
197
|
+
# @example A property used in inequality filter must be ordered first:
|
198
|
+
# query = Google::Cloud::Datastore::Query.new
|
199
|
+
# query.kind("Task").
|
200
|
+
# where("priority", ">", 3).
|
201
|
+
# order("priority").
|
202
|
+
# order("created")
|
203
|
+
#
|
204
|
+
# tasks = datastore.run query
|
205
|
+
#
|
206
|
+
def order name, direction = :asc
|
207
|
+
@grpc.order << Google::Datastore::V1::PropertyOrder.new(
|
208
|
+
property: Google::Datastore::V1::PropertyReference.new(
|
209
|
+
name: name),
|
210
|
+
direction: prop_order_direction(direction)
|
211
|
+
)
|
212
|
+
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Set a limit on the number of results to be returned.
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# query = Google::Cloud::Datastore::Query.new
|
221
|
+
# query.kind("Task").
|
222
|
+
# limit(5)
|
223
|
+
#
|
224
|
+
# tasks = datastore.run query
|
225
|
+
#
|
226
|
+
def limit num
|
227
|
+
@grpc.limit = Google::Protobuf::Int32Value.new(value: num)
|
228
|
+
|
229
|
+
self
|
230
|
+
end
|
231
|
+
|
232
|
+
##
|
233
|
+
# Set an offset for the results to be returned.
|
234
|
+
#
|
235
|
+
# @example
|
236
|
+
# query = Google::Cloud::Datastore::Query.new
|
237
|
+
# query.kind("Task").
|
238
|
+
# limit(5).
|
239
|
+
# offset(10)
|
240
|
+
#
|
241
|
+
# tasks = datastore.run query
|
242
|
+
#
|
243
|
+
def offset num
|
244
|
+
@grpc.offset = num
|
245
|
+
|
246
|
+
self
|
247
|
+
end
|
248
|
+
|
249
|
+
##
|
250
|
+
# Set the cursor to start the results at.
|
251
|
+
#
|
252
|
+
# @example
|
253
|
+
# query = Google::Cloud::Datastore::Query.new
|
254
|
+
# query.kind("Task").
|
255
|
+
# limit(page_size).
|
256
|
+
# start(page_cursor)
|
257
|
+
#
|
258
|
+
# tasks = datastore.run query
|
259
|
+
#
|
260
|
+
def start cursor
|
261
|
+
if cursor.is_a? Cursor
|
262
|
+
@grpc.start_cursor = cursor.to_grpc
|
263
|
+
elsif cursor.is_a? String
|
264
|
+
@grpc.start_cursor = Core::GRPCUtils.decode_bytes cursor
|
265
|
+
else
|
266
|
+
fail ArgumentError, "Can't set a cursor using a #{cursor.class}."
|
267
|
+
end
|
268
|
+
|
269
|
+
self
|
270
|
+
end
|
271
|
+
alias_method :cursor, :start
|
272
|
+
|
273
|
+
##
|
274
|
+
# Retrieve only select properties from the matched entities.
|
275
|
+
#
|
276
|
+
# @example
|
277
|
+
# query = Google::Cloud::Datastore::Query.new
|
278
|
+
# query.kind("Task").
|
279
|
+
# select("priority", "percent_complete")
|
280
|
+
#
|
281
|
+
# priorities = []
|
282
|
+
# percent_completes = []
|
283
|
+
# datastore.run(query).each do |task|
|
284
|
+
# priorities << task["priority"]
|
285
|
+
# percent_completes << task["percent_complete"]
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# @example A keys-only query using the special property `__key__`:
|
289
|
+
# query = Google::Cloud::Datastore::Query.new
|
290
|
+
# query.kind("Task").
|
291
|
+
# select("__key__")
|
292
|
+
#
|
293
|
+
# keys = datastore.run(query).map(&:key)
|
294
|
+
#
|
295
|
+
def select *names
|
296
|
+
names.each do |name|
|
297
|
+
grpc_projection = Google::Datastore::V1::Projection.new(
|
298
|
+
property: Google::Datastore::V1::PropertyReference.new(
|
299
|
+
name: name))
|
300
|
+
@grpc.projection << grpc_projection
|
301
|
+
end
|
302
|
+
|
303
|
+
self
|
304
|
+
end
|
305
|
+
alias_method :projection, :select
|
306
|
+
|
307
|
+
##
|
308
|
+
# Group results by a list of properties.
|
309
|
+
#
|
310
|
+
# @example
|
311
|
+
# query = Google::Cloud::Datastore::Query.new
|
312
|
+
# query.kind("Task").
|
313
|
+
# distinct_on("type", "priority").
|
314
|
+
# order("type").
|
315
|
+
# order("priority")
|
316
|
+
#
|
317
|
+
# tasks = datastore.run query
|
318
|
+
#
|
319
|
+
def group_by *names
|
320
|
+
names.each do |name|
|
321
|
+
grpc_property = Google::Datastore::V1::PropertyReference.new(
|
322
|
+
name: name)
|
323
|
+
@grpc.distinct_on << grpc_property
|
324
|
+
end
|
325
|
+
|
326
|
+
self
|
327
|
+
end
|
328
|
+
alias_method :distinct_on, :group_by
|
329
|
+
|
330
|
+
# @private
|
331
|
+
def to_grpc
|
332
|
+
@grpc
|
333
|
+
end
|
334
|
+
|
335
|
+
protected
|
336
|
+
|
337
|
+
##
|
338
|
+
# @private Get the property order direction for a string.
|
339
|
+
def prop_order_direction direction
|
340
|
+
if direction.to_s.downcase.start_with? "a"
|
341
|
+
:ASCENDING
|
342
|
+
elsif direction.to_s.downcase.start_with? "d"
|
343
|
+
:DESCENDING
|
344
|
+
else
|
345
|
+
:DIRECTION_UNSPECIFIED
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|