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