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,222 @@
|
|
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 "delegate"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Datastore
|
21
|
+
class Dataset
|
22
|
+
##
|
23
|
+
# LookupResults is a special case Array with additional values.
|
24
|
+
# A LookupResults object is returned from Dataset#find_all and
|
25
|
+
# contains the entities as well as the Keys that were deferred from
|
26
|
+
# the results and the Entities that were missing in the dataset.
|
27
|
+
#
|
28
|
+
# Please be cautious when treating the QueryResults as an Array.
|
29
|
+
# Many common Array methods will return a new Array instance.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# tasks = datastore.find_all task_key1, task_key2, task_key3
|
33
|
+
# tasks.size #=> 3
|
34
|
+
# tasks.deferred #=> []
|
35
|
+
# tasks.missing #=> []
|
36
|
+
#
|
37
|
+
# @example Caution, many Array methods will return a new Array instance:
|
38
|
+
# tasks = datastore.find_all task_key1, task_key2, task_key3
|
39
|
+
# tasks.size #=> 3
|
40
|
+
# tasks.deferred #=> []
|
41
|
+
# tasks.missing #=> []
|
42
|
+
# descriptions = tasks.map { |task| task["description"] }
|
43
|
+
# descriptions.size #=> 3
|
44
|
+
# descriptions.deferred #=> NoMethodError
|
45
|
+
# descriptions.missing #=> NoMethodError
|
46
|
+
#
|
47
|
+
class LookupResults < DelegateClass(::Array)
|
48
|
+
##
|
49
|
+
# Keys that were not looked up due to resource constraints.
|
50
|
+
attr_accessor :deferred
|
51
|
+
|
52
|
+
##
|
53
|
+
# Entities not found, with only the key populated.
|
54
|
+
attr_accessor :missing
|
55
|
+
|
56
|
+
##
|
57
|
+
# @private Create a new LookupResults with an array of values.
|
58
|
+
def initialize arr = []
|
59
|
+
super arr
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Whether there are more results available.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# gcloud = Google::Cloud.new
|
69
|
+
# datastore = gcloud.datastore
|
70
|
+
#
|
71
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
72
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
73
|
+
# tasks = datastore.find_all task_key1, task_key2
|
74
|
+
# if tasks.next?
|
75
|
+
# next_tasks = tasks.next
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def next?
|
79
|
+
Array(@deferred).any?
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Retrieve the next page of results.
|
84
|
+
#
|
85
|
+
# @return [LookupResults]
|
86
|
+
#
|
87
|
+
# @example
|
88
|
+
# gcloud = Google::Cloud.new
|
89
|
+
# datastore = gcloud.datastore
|
90
|
+
#
|
91
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
92
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
93
|
+
# tasks = datastore.find_all task_key1, task_key2
|
94
|
+
# if tasks.next?
|
95
|
+
# next_tasks = tasks.next
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
def next
|
99
|
+
return nil unless next?
|
100
|
+
ensure_service!
|
101
|
+
lookup_res = @service.lookup(
|
102
|
+
*Array(@deferred).flatten.map(&:to_grpc),
|
103
|
+
consistency: @consistency, transaction: @transaction)
|
104
|
+
self.class.from_grpc lookup_res, @service, @consistency
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Retrieves all lookup results by repeatedly loading {#next} until
|
109
|
+
# {#next?} returns `false`. Calls the given block once for each
|
110
|
+
# result, which is passed as the parameter.
|
111
|
+
#
|
112
|
+
# An Enumerator is returned if no block is given.
|
113
|
+
#
|
114
|
+
# This method may make several API calls until all lookup results are
|
115
|
+
# retrieved. Be sure to use as narrow a search criteria as possible.
|
116
|
+
# Please use with caution.
|
117
|
+
#
|
118
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
119
|
+
# make to load all lookup results. Default is no limit.
|
120
|
+
# @yield [result] The block for accessing each lookup result.
|
121
|
+
# @yieldparam [Entity] result The lookup result object.
|
122
|
+
#
|
123
|
+
# @return [Enumerator]
|
124
|
+
#
|
125
|
+
# @example Iterating each result by passing a block:
|
126
|
+
# gcloud = Google::Cloud.new
|
127
|
+
# datastore = gcloud.datastore
|
128
|
+
#
|
129
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
130
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
131
|
+
# tasks = datastore.find_all task_key1, task_key2
|
132
|
+
# tasks.all do |task|
|
133
|
+
# puts "Task #{task.key.id} (#cursor)"
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# @example Using the enumerator by not passing a block:
|
137
|
+
# gcloud = Google::Cloud.new
|
138
|
+
# datastore = gcloud.datastore
|
139
|
+
#
|
140
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
141
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
142
|
+
# tasks = datastore.find_all task_key1, task_key2
|
143
|
+
# all_keys = tasks.all.map(&:key).each do |task|
|
144
|
+
# task.key
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# @example Limit the number of API calls made:
|
148
|
+
# gcloud = Google::Cloud.new
|
149
|
+
# datastore = gcloud.datastore
|
150
|
+
#
|
151
|
+
# task_key1 = datastore.key "Task", "sampleTask1"
|
152
|
+
# task_key2 = datastore.key "Task", "sampleTask2"
|
153
|
+
# tasks = datastore.find_all task_key1, task_key2
|
154
|
+
# tasks.all(request_limit: 10) do |task|
|
155
|
+
# puts "Task #{task.key.id} (#cursor)"
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
def all request_limit: nil
|
159
|
+
request_limit = request_limit.to_i if request_limit
|
160
|
+
unless block_given?
|
161
|
+
return enum_for(:all, request_limit: request_limit)
|
162
|
+
end
|
163
|
+
results = self
|
164
|
+
loop do
|
165
|
+
results.each { |r| yield r }
|
166
|
+
if request_limit
|
167
|
+
request_limit -= 1
|
168
|
+
break if request_limit < 0
|
169
|
+
end
|
170
|
+
break unless results.next?
|
171
|
+
results = results.next
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# @private New Dataset::LookupResults from a
|
177
|
+
# Google::Dataset::V1::LookupResponse object.
|
178
|
+
def self.from_grpc lookup_res, service, consistency = nil, tx = nil
|
179
|
+
entities = to_gcloud_entities lookup_res.found
|
180
|
+
deferred = to_gcloud_keys lookup_res.deferred
|
181
|
+
missing = to_gcloud_entities lookup_res.missing
|
182
|
+
new(entities).tap do |lr|
|
183
|
+
lr.instance_variable_set :@service, service
|
184
|
+
lr.instance_variable_set :@consistency, consistency
|
185
|
+
lr.instance_variable_set :@transaction, tx
|
186
|
+
lr.instance_variable_set :@deferred, deferred
|
187
|
+
lr.instance_variable_set :@missing, missing
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
|
193
|
+
##
|
194
|
+
# @private Raise an error unless an active connection to the service
|
195
|
+
# is available.
|
196
|
+
def ensure_service!
|
197
|
+
msg = "Must have active connection to datastore service to get next"
|
198
|
+
fail msg if @service.nil?
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Convenience method to convert GRPC entities to google-cloud
|
203
|
+
# entities.
|
204
|
+
def self.to_gcloud_entities grpc_entity_results
|
205
|
+
# Entities are nested in an object.
|
206
|
+
Array(grpc_entity_results).map do |result|
|
207
|
+
# TODO: Make this return an EntityResult with cursor...
|
208
|
+
Entity.from_grpc result.entity
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
##
|
213
|
+
# Convenience method to convert GRPC keys to google-cloud keys.
|
214
|
+
def self.to_gcloud_keys grpc_keys
|
215
|
+
# Keys are not nested in an object like entities are.
|
216
|
+
Array(grpc_keys).map { |key| Key.from_grpc key }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,389 @@
|
|
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 "delegate"
|
17
|
+
|
18
|
+
module Google
|
19
|
+
module Cloud
|
20
|
+
module Datastore
|
21
|
+
class Dataset
|
22
|
+
##
|
23
|
+
# QueryResults is a special case Array with additional values.
|
24
|
+
# A QueryResults object is returned from Dataset#run and contains
|
25
|
+
# the Entities from the query as well as the query's cursor and
|
26
|
+
# more_results value.
|
27
|
+
#
|
28
|
+
# Please be cautious when treating the QueryResults as an Array.
|
29
|
+
# Many common Array methods will return a new Array instance.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# require "google/cloud"
|
33
|
+
#
|
34
|
+
# gcloud = Google::Cloud.new
|
35
|
+
# datastore = gcloud.datastore
|
36
|
+
#
|
37
|
+
# query = datastore.query("Task")
|
38
|
+
# tasks = datastore.run query
|
39
|
+
#
|
40
|
+
# tasks.size #=> 3
|
41
|
+
# tasks.cursor #=> Cursor(c3VwZXJhd2Vzb21lIQ)
|
42
|
+
#
|
43
|
+
# @example Caution, many Array methods will return a new Array instance:
|
44
|
+
# require "google/cloud"
|
45
|
+
#
|
46
|
+
# gcloud = Google::Cloud.new
|
47
|
+
# datastore = gcloud.datastore
|
48
|
+
#
|
49
|
+
# query = datastore.query("Task")
|
50
|
+
# tasks = datastore.run query
|
51
|
+
#
|
52
|
+
# tasks.size #=> 3
|
53
|
+
# tasks.end_cursor #=> Cursor(c3VwZXJhd2Vzb21lIQ)
|
54
|
+
# descriptions = tasks.map { |task| task["description"] }
|
55
|
+
# descriptions.size #=> 3
|
56
|
+
# descriptions.cursor #=> NoMethodError
|
57
|
+
#
|
58
|
+
class QueryResults < DelegateClass(::Array)
|
59
|
+
##
|
60
|
+
# The end_cursor of the QueryResults.
|
61
|
+
#
|
62
|
+
# @return [Google::Cloud::Datastore::Cursor]
|
63
|
+
attr_reader :end_cursor
|
64
|
+
alias_method :cursor, :end_cursor
|
65
|
+
|
66
|
+
##
|
67
|
+
# The state of the query after the current batch.
|
68
|
+
#
|
69
|
+
# Expected values are:
|
70
|
+
#
|
71
|
+
# * `:NOT_FINISHED`
|
72
|
+
# * `:MORE_RESULTS_AFTER_LIMIT`
|
73
|
+
# * `:MORE_RESULTS_AFTER_CURSOR`
|
74
|
+
# * `:NO_MORE_RESULTS`
|
75
|
+
attr_reader :more_results
|
76
|
+
|
77
|
+
##
|
78
|
+
# @private
|
79
|
+
attr_accessor :service, :namespace, :cursors, :query
|
80
|
+
|
81
|
+
##
|
82
|
+
# @private
|
83
|
+
attr_writer :end_cursor, :more_results
|
84
|
+
|
85
|
+
##
|
86
|
+
# Convenience method for determining if the `more_results` value
|
87
|
+
# is `:NOT_FINISHED`
|
88
|
+
def not_finished?
|
89
|
+
more_results == :NOT_FINISHED
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Convenience method for determining if the `more_results` value
|
94
|
+
# is `:MORE_RESULTS_AFTER_LIMIT`
|
95
|
+
def more_after_limit?
|
96
|
+
more_results == :MORE_RESULTS_AFTER_LIMIT
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Convenience method for determining if the `more_results` value
|
101
|
+
# is `:MORE_RESULTS_AFTER_CURSOR`
|
102
|
+
def more_after_cursor?
|
103
|
+
more_results == :MORE_RESULTS_AFTER_CURSOR
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Convenience method for determining if the `more_results` value
|
108
|
+
# is `:NO_MORE_RESULTS`
|
109
|
+
def no_more?
|
110
|
+
more_results == :NO_MORE_RESULTS
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# @private Create a new QueryResults with an array of values.
|
115
|
+
def initialize arr = []
|
116
|
+
super arr
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Whether there are more results available.
|
121
|
+
#
|
122
|
+
# @return [Boolean]
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# require "google/cloud"
|
126
|
+
#
|
127
|
+
# gcloud = Google::Cloud.new
|
128
|
+
# datastore = gcloud.datastore
|
129
|
+
# query = datastore.query "Task"
|
130
|
+
# tasks = datastore.run query
|
131
|
+
#
|
132
|
+
# if tasks.next?
|
133
|
+
# next_tasks = tasks.next
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
def next?
|
137
|
+
not_finished?
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Retrieve the next page of results.
|
142
|
+
#
|
143
|
+
# @return [QueryResults]
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# require "google/cloud"
|
147
|
+
#
|
148
|
+
# gcloud = Google::Cloud.new
|
149
|
+
# datastore = gcloud.datastore
|
150
|
+
# query = datastore.query "Task"
|
151
|
+
# tasks = datastore.run query
|
152
|
+
#
|
153
|
+
# if tasks.next?
|
154
|
+
# next_tasks = tasks.next
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
def next
|
158
|
+
return nil unless next?
|
159
|
+
return nil if end_cursor.nil?
|
160
|
+
ensure_service!
|
161
|
+
query.start_cursor = cursor.to_grpc # should always be a Cursor...
|
162
|
+
query_res = service.run_query query, namespace
|
163
|
+
self.class.from_grpc query_res, service, namespace, query
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Retrieve the {Cursor} for the provided result.
|
168
|
+
#
|
169
|
+
# @param [Entity] result The entity object to get a cursor for.
|
170
|
+
#
|
171
|
+
# @return [Cursor]
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# require "google/cloud"
|
175
|
+
#
|
176
|
+
# gcloud = Google::Cloud.new
|
177
|
+
# datastore = gcloud.datastore
|
178
|
+
# query = datastore.query "Task"
|
179
|
+
# tasks = datastore.run query
|
180
|
+
#
|
181
|
+
# first_task = tasks.first
|
182
|
+
# first_cursor = tasks.cursor_for first_task
|
183
|
+
#
|
184
|
+
def cursor_for result
|
185
|
+
cursor_index = index result
|
186
|
+
return nil if cursor_index.nil?
|
187
|
+
cursors[cursor_index]
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# Calls the given block once for each result and cursor combination,
|
192
|
+
# which are passed as parameters.
|
193
|
+
#
|
194
|
+
# An Enumerator is returned if no block is given.
|
195
|
+
#
|
196
|
+
# @yield [result, cursor] The block for accessing each query result
|
197
|
+
# and cursor.
|
198
|
+
# @yieldparam [Entity] result The query result object.
|
199
|
+
# @yieldparam [Cursor] cursor The cursor object.
|
200
|
+
#
|
201
|
+
# @return [Enumerator]
|
202
|
+
#
|
203
|
+
# @example
|
204
|
+
# require "google/cloud"
|
205
|
+
#
|
206
|
+
# gcloud = Google::Cloud.new
|
207
|
+
# datastore = gcloud.datastore
|
208
|
+
# query = datastore.query "Task"
|
209
|
+
# tasks = datastore.run query
|
210
|
+
# tasks.each_with_cursor do |task, cursor|
|
211
|
+
# puts "Task #{task.key.id} (#cursor)"
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
def each_with_cursor
|
215
|
+
return enum_for(:each_with_cursor) unless block_given?
|
216
|
+
zip(cursors).each { |r, c| yield [r, c] }
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Retrieves all query results by repeatedly loading {#next} until
|
221
|
+
# {#next?} returns `false`. Calls the given block once for each query
|
222
|
+
# result, which is passed as the parameter.
|
223
|
+
#
|
224
|
+
# An Enumerator is returned if no block is given.
|
225
|
+
#
|
226
|
+
# This method may make several API calls until all query results are
|
227
|
+
# retrieved. Be sure to use as narrow a search criteria as possible.
|
228
|
+
# Please use with caution.
|
229
|
+
#
|
230
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
231
|
+
# make to load all query results. Default is no limit.
|
232
|
+
# @yield [result] The block for accessing each query result.
|
233
|
+
# @yieldparam [Entity] result The query result object.
|
234
|
+
#
|
235
|
+
# @return [Enumerator]
|
236
|
+
#
|
237
|
+
# @example Iterating each query result by passing a block:
|
238
|
+
# require "google/cloud"
|
239
|
+
#
|
240
|
+
# gcloud = Google::Cloud.new
|
241
|
+
# datastore = gcloud.datastore
|
242
|
+
# query = datastore.query "Task"
|
243
|
+
# tasks = datastore.run query
|
244
|
+
# tasks.all do |task|
|
245
|
+
# puts "Task #{task.key.id} (#cursor)"
|
246
|
+
# end
|
247
|
+
#
|
248
|
+
# @example Using the enumerator by not passing a block:
|
249
|
+
# require "google/cloud"
|
250
|
+
#
|
251
|
+
# gcloud = Google::Cloud.new
|
252
|
+
# datastore = gcloud.datastore
|
253
|
+
# query = datastore.query "Task"
|
254
|
+
# tasks = datastore.run query
|
255
|
+
# tasks.all.map(&:key).each do |key|
|
256
|
+
# puts "Key #{key.id}"
|
257
|
+
# end
|
258
|
+
#
|
259
|
+
# @example Limit the number of API calls made:
|
260
|
+
# require "google/cloud"
|
261
|
+
#
|
262
|
+
# gcloud = Google::Cloud.new
|
263
|
+
# datastore = gcloud.datastore
|
264
|
+
# query = datastore.query "Task"
|
265
|
+
# tasks = datastore.run query
|
266
|
+
# tasks.all(request_limit: 10) do |task|
|
267
|
+
# puts "Task #{task.key.id} (#cursor)"
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
def all request_limit: nil
|
271
|
+
request_limit = request_limit.to_i if request_limit
|
272
|
+
unless block_given?
|
273
|
+
return enum_for(:all, request_limit: request_limit)
|
274
|
+
end
|
275
|
+
results = self
|
276
|
+
loop do
|
277
|
+
results.each { |r| yield r }
|
278
|
+
if request_limit
|
279
|
+
request_limit -= 1
|
280
|
+
break if request_limit < 0
|
281
|
+
end
|
282
|
+
break unless results.next?
|
283
|
+
results = results.next
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
##
|
288
|
+
# Retrieves all query results and cursors by repeatedly loading
|
289
|
+
# {#next} until {#next?} returns `false`. Calls the given block once
|
290
|
+
# for each result and cursor combination, which are passed as
|
291
|
+
# parameters.
|
292
|
+
#
|
293
|
+
# An Enumerator is returned if no block is given.
|
294
|
+
#
|
295
|
+
# This method may make several API calls until all query results are
|
296
|
+
# retrieved. Be sure to use as narrow a search criteria as possible.
|
297
|
+
# Please use with caution.
|
298
|
+
#
|
299
|
+
# @param [Integer] request_limit The upper limit of API requests to
|
300
|
+
# make to load all tables. Default is no limit.
|
301
|
+
# @yield [result, cursor] The block for accessing each query result
|
302
|
+
# and cursor.
|
303
|
+
# @yieldparam [Entity] result The query result object.
|
304
|
+
# @yieldparam [Cursor] cursor The cursor object.
|
305
|
+
#
|
306
|
+
# @return [Enumerator]
|
307
|
+
#
|
308
|
+
# @example Iterating all results and cursors by passing a block:
|
309
|
+
# require "google/cloud"
|
310
|
+
#
|
311
|
+
# gcloud = Google::Cloud.new
|
312
|
+
# datastore = gcloud.datastore
|
313
|
+
# query = datastore.query "Task"
|
314
|
+
# tasks = datastore.run query
|
315
|
+
# tasks.all_with_cursor do |task, cursor|
|
316
|
+
# puts "Task #{task.key.id} (#cursor)"
|
317
|
+
# end
|
318
|
+
#
|
319
|
+
# @example Using the enumerator by not passing a block:
|
320
|
+
# require "google/cloud"
|
321
|
+
#
|
322
|
+
# gcloud = Google::Cloud.new
|
323
|
+
# datastore = gcloud.datastore
|
324
|
+
# query = datastore.query "Task"
|
325
|
+
# tasks = datastore.run query
|
326
|
+
# tasks.all_with_cursor.count #=> number of result/cursor pairs
|
327
|
+
#
|
328
|
+
# @example Limit the number of API calls made:
|
329
|
+
# require "google/cloud"
|
330
|
+
#
|
331
|
+
# gcloud = Google::Cloud.new
|
332
|
+
# datastore = gcloud.datastore
|
333
|
+
# query = datastore.query "Task"
|
334
|
+
# tasks = datastore.run query
|
335
|
+
# tasks.all_with_cursor(request_limit: 10) do |task, cursor|
|
336
|
+
# puts "Task #{task.key.id} (#cursor)"
|
337
|
+
# end
|
338
|
+
#
|
339
|
+
def all_with_cursor request_limit: nil
|
340
|
+
request_limit = request_limit.to_i if request_limit
|
341
|
+
unless block_given?
|
342
|
+
return enum_for(:all_with_cursor, request_limit: request_limit)
|
343
|
+
end
|
344
|
+
results = self
|
345
|
+
|
346
|
+
loop do
|
347
|
+
results.zip(results.cursors).each { |r, c| yield r, c }
|
348
|
+
if request_limit
|
349
|
+
request_limit -= 1
|
350
|
+
break if request_limit < 0
|
351
|
+
end
|
352
|
+
break unless results.next?
|
353
|
+
results = results.next
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
##
|
358
|
+
# @private New Dataset::QueryResults from a
|
359
|
+
# Google::Dataset::V1::RunQueryResponse object.
|
360
|
+
def self.from_grpc query_res, service, namespace, query
|
361
|
+
r, c = Array(query_res.batch.entity_results).map do |result|
|
362
|
+
[Entity.from_grpc(result.entity), Cursor.from_grpc(result.cursor)]
|
363
|
+
end.transpose
|
364
|
+
r ||= []
|
365
|
+
c ||= []
|
366
|
+
new(r).tap do |qr|
|
367
|
+
qr.cursors = c
|
368
|
+
qr.end_cursor = Cursor.from_grpc query_res.batch.end_cursor
|
369
|
+
qr.more_results = query_res.batch.more_results
|
370
|
+
qr.service = service
|
371
|
+
qr.namespace = namespace
|
372
|
+
qr.query = query_res.query || query
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
protected
|
377
|
+
|
378
|
+
##
|
379
|
+
# @private Raise an error unless an active connection to the service
|
380
|
+
# is available.
|
381
|
+
def ensure_service!
|
382
|
+
msg = "Must have active connection to datastore service to get next"
|
383
|
+
fail msg if @service.nil? || @query.nil?
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|