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,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