google-cloud-firestore 2.7.2 → 2.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/AUTHENTICATION.md +8 -26
- data/CHANGELOG.md +69 -0
- data/LOGGING.md +1 -1
- data/lib/google/cloud/firestore/aggregate_query.rb +285 -0
- data/lib/google/cloud/firestore/aggregate_query_snapshot.rb +145 -0
- data/lib/google/cloud/firestore/bulk_commit_batch.rb +73 -0
- data/lib/google/cloud/firestore/bulk_writer.rb +558 -0
- data/lib/google/cloud/firestore/bulk_writer_exception.rb +40 -0
- data/lib/google/cloud/firestore/bulk_writer_operation.rb +126 -0
- data/lib/google/cloud/firestore/bulk_writer_scheduler.rb +164 -0
- data/lib/google/cloud/firestore/client.rb +161 -10
- data/lib/google/cloud/firestore/collection_group.rb +20 -4
- data/lib/google/cloud/firestore/collection_reference.rb +17 -2
- data/lib/google/cloud/firestore/collection_reference_list.rb +4 -3
- data/lib/google/cloud/firestore/convert.rb +6 -7
- data/lib/google/cloud/firestore/document_reference/list.rb +5 -3
- data/lib/google/cloud/firestore/document_reference.rb +20 -3
- data/lib/google/cloud/firestore/document_snapshot.rb +1 -1
- data/lib/google/cloud/firestore/errors.rb +60 -0
- data/lib/google/cloud/firestore/filter.rb +326 -0
- data/lib/google/cloud/firestore/promise/future.rb +97 -0
- data/lib/google/cloud/firestore/query.rb +112 -89
- data/lib/google/cloud/firestore/rate_limiter.rb +80 -0
- data/lib/google/cloud/firestore/service.rb +74 -23
- data/lib/google/cloud/firestore/transaction.rb +57 -4
- data/lib/google/cloud/firestore/version.rb +1 -1
- data/lib/google/cloud/firestore.rb +17 -7
- data/lib/google-cloud-firestore.rb +45 -8
- metadata +17 -146
@@ -17,6 +17,8 @@ require "google/cloud/firestore/v1"
|
|
17
17
|
require "google/cloud/firestore/document_snapshot"
|
18
18
|
require "google/cloud/firestore/query_listener"
|
19
19
|
require "google/cloud/firestore/convert"
|
20
|
+
require "google/cloud/firestore/aggregate_query"
|
21
|
+
require "google/cloud/firestore/filter"
|
20
22
|
require "json"
|
21
23
|
|
22
24
|
module Google
|
@@ -209,29 +211,48 @@ module Google
|
|
209
211
|
end
|
210
212
|
|
211
213
|
##
|
212
|
-
#
|
213
|
-
#
|
214
|
-
# @
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
214
|
+
# Adds filter to the where clause
|
215
|
+
#
|
216
|
+
# @overload where(filter)
|
217
|
+
# Pass Firestore::Filter to `where` via field_or_filter argument.
|
218
|
+
#
|
219
|
+
# @param filter [::Google::Cloud::Firestore::Filter]
|
220
|
+
#
|
221
|
+
# @overload where(field, operator, value)
|
222
|
+
# Pass arguments to `where` via positional arguments.
|
223
|
+
#
|
224
|
+
# @param field [FieldPath, String, Symbol] A field path to filter
|
225
|
+
# results with.
|
226
|
+
# If a {FieldPath} object is not provided then the field will be
|
227
|
+
# treated as a dotted string, meaning the string represents individual
|
228
|
+
# fields joined by ".". Fields containing `~`, `*`, `/`, `[`, `]`, and
|
229
|
+
# `.` cannot be in a dotted string, and should provided using a
|
230
|
+
# {FieldPath} object instead.
|
231
|
+
#
|
232
|
+
# @param operator [String, Symbol] The operation to compare the field
|
233
|
+
# to. Acceptable values include:
|
234
|
+
# * less than: `<`, `lt`
|
235
|
+
# * less than or equal: `<=`, `lte`
|
236
|
+
# * greater than: `>`, `gt`
|
237
|
+
# * greater than or equal: `>=`, `gte`
|
238
|
+
# * equal: `=`, `==`, `eq`, `eql`, `is`
|
239
|
+
# * not equal: `!=`
|
240
|
+
# * in: `in`
|
241
|
+
# * not in: `not-in`, `not_in`
|
242
|
+
# * array contains: `array-contains`, `array_contains`
|
243
|
+
#
|
244
|
+
# @param value [Object] The value to compare the property to. Defaults to nil.
|
245
|
+
# Possible values are:
|
246
|
+
# * Integer
|
247
|
+
# * Float/BigDecimal
|
248
|
+
# * String
|
249
|
+
# * Boolean
|
250
|
+
# * Array
|
251
|
+
# * Date/Time
|
252
|
+
# * StringIO
|
253
|
+
# * Google::Cloud::Datastore::Key
|
254
|
+
# * Google::Cloud::Datastore::Entity
|
255
|
+
# * nil
|
235
256
|
#
|
236
257
|
# @return [Query] New query with `where` called on it.
|
237
258
|
#
|
@@ -250,7 +271,25 @@ module Google
|
|
250
271
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
251
272
|
# end
|
252
273
|
#
|
253
|
-
|
274
|
+
# @example
|
275
|
+
# require "google/cloud/firestore"
|
276
|
+
#
|
277
|
+
# firestore = Google::Cloud::Firestore.new
|
278
|
+
#
|
279
|
+
# # Get a collection reference
|
280
|
+
# cities_col = firestore.col "cities"
|
281
|
+
#
|
282
|
+
# # Create a filter
|
283
|
+
# filter = Filter.create(:population, :>=, 1000000)
|
284
|
+
#
|
285
|
+
# # Add filter to where clause
|
286
|
+
# query = query.where filter
|
287
|
+
#
|
288
|
+
# query.get do |city|
|
289
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
290
|
+
# end
|
291
|
+
#
|
292
|
+
def where filter_or_field = nil, operator = nil, value = nil
|
254
293
|
if query_has_cursors?
|
255
294
|
raise "cannot call where after calling " \
|
256
295
|
"start_at, start_after, end_before, or end_at"
|
@@ -259,10 +298,12 @@ module Google
|
|
259
298
|
new_query = @query.dup
|
260
299
|
new_query ||= StructuredQuery.new
|
261
300
|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
301
|
+
if filter_or_field.is_a? Google::Cloud::Firestore::Filter
|
302
|
+
new_query.where = filter_or_field.filter
|
303
|
+
else
|
304
|
+
new_filter = Google::Cloud::Firestore::Filter.new filter_or_field, operator, value
|
305
|
+
add_filters_to_query new_query, new_filter.filter
|
306
|
+
end
|
266
307
|
|
267
308
|
Query.start new_query, parent_path, client, limit_type: limit_type
|
268
309
|
end
|
@@ -903,6 +944,9 @@ module Google
|
|
903
944
|
##
|
904
945
|
# Retrieves document snapshots for the query.
|
905
946
|
#
|
947
|
+
# @param [Time] read_time Reads documents as they were at the given time.
|
948
|
+
# This may not be older than 270 seconds. Optional
|
949
|
+
#
|
906
950
|
# @yield [documents] The block for accessing the document snapshots.
|
907
951
|
# @yieldparam [DocumentSnapshot] document A document snapshot.
|
908
952
|
#
|
@@ -923,12 +967,29 @@ module Google
|
|
923
967
|
# puts "#{city.document_id} has #{city[:population]} residents."
|
924
968
|
# end
|
925
969
|
#
|
926
|
-
|
970
|
+
# @example Get query with read time
|
971
|
+
# require "google/cloud/firestore"
|
972
|
+
#
|
973
|
+
# firestore = Google::Cloud::Firestore.new
|
974
|
+
#
|
975
|
+
# # Get a collection reference
|
976
|
+
# cities_col = firestore.col "cities"
|
977
|
+
#
|
978
|
+
# # Create a query
|
979
|
+
# query = cities_col.select(:population)
|
980
|
+
#
|
981
|
+
# read_time = Time.now
|
982
|
+
#
|
983
|
+
# query.get(read_time: read_time) do |city|
|
984
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
985
|
+
# end
|
986
|
+
#
|
987
|
+
def get read_time: nil
|
927
988
|
ensure_service!
|
928
989
|
|
929
|
-
return enum_for :get unless block_given?
|
990
|
+
return enum_for :get, read_time: read_time unless block_given?
|
930
991
|
|
931
|
-
results = service.run_query parent_path, @query
|
992
|
+
results = service.run_query parent_path, @query, read_time: read_time
|
932
993
|
|
933
994
|
# Reverse the results for Query#limit_to_last queries since that method reversed the order_by directions.
|
934
995
|
results = results.to_a.reverse if limit_type == :last
|
@@ -940,6 +1001,26 @@ module Google
|
|
940
1001
|
end
|
941
1002
|
alias run get
|
942
1003
|
|
1004
|
+
##
|
1005
|
+
# Creates an AggregateQuery object for the query.
|
1006
|
+
#
|
1007
|
+
# @return [AggregateQuery] New empty aggregate query.
|
1008
|
+
#
|
1009
|
+
# @example
|
1010
|
+
# require "google/cloud/firestore"
|
1011
|
+
#
|
1012
|
+
# firestore = Google::Cloud::Firestore.new
|
1013
|
+
#
|
1014
|
+
# # Get a collection reference
|
1015
|
+
# query = firestore.col "cities"
|
1016
|
+
#
|
1017
|
+
# # Create an aggregate query
|
1018
|
+
# aggregate_query = query.aggregate_query
|
1019
|
+
#
|
1020
|
+
def aggregate_query
|
1021
|
+
AggregateQuery.new self, parent_path, client
|
1022
|
+
end
|
1023
|
+
|
943
1024
|
##
|
944
1025
|
# Listen to this query for changes.
|
945
1026
|
#
|
@@ -1046,34 +1127,6 @@ module Google
|
|
1046
1127
|
# @private
|
1047
1128
|
StructuredQuery = Google::Cloud::Firestore::V1::StructuredQuery
|
1048
1129
|
|
1049
|
-
##
|
1050
|
-
# @private
|
1051
|
-
FILTER_OPS = {
|
1052
|
-
"<" => :LESS_THAN,
|
1053
|
-
"lt" => :LESS_THAN,
|
1054
|
-
"<=" => :LESS_THAN_OR_EQUAL,
|
1055
|
-
"lte" => :LESS_THAN_OR_EQUAL,
|
1056
|
-
">" => :GREATER_THAN,
|
1057
|
-
"gt" => :GREATER_THAN,
|
1058
|
-
">=" => :GREATER_THAN_OR_EQUAL,
|
1059
|
-
"gte" => :GREATER_THAN_OR_EQUAL,
|
1060
|
-
"=" => :EQUAL,
|
1061
|
-
"==" => :EQUAL,
|
1062
|
-
"eq" => :EQUAL,
|
1063
|
-
"eql" => :EQUAL,
|
1064
|
-
"is" => :EQUAL,
|
1065
|
-
"!=" => :NOT_EQUAL,
|
1066
|
-
"array_contains" => :ARRAY_CONTAINS,
|
1067
|
-
"array-contains" => :ARRAY_CONTAINS,
|
1068
|
-
"include" => :ARRAY_CONTAINS,
|
1069
|
-
"include?" => :ARRAY_CONTAINS,
|
1070
|
-
"has" => :ARRAY_CONTAINS,
|
1071
|
-
"in" => :IN,
|
1072
|
-
"not_in" => :NOT_IN,
|
1073
|
-
"not-in" => :NOT_IN,
|
1074
|
-
"array_contains_any" => :ARRAY_CONTAINS_ANY,
|
1075
|
-
"array-contains-any" => :ARRAY_CONTAINS_ANY
|
1076
|
-
}.freeze
|
1077
1130
|
##
|
1078
1131
|
# @private
|
1079
1132
|
INEQUALITY_FILTERS = [
|
@@ -1097,36 +1150,6 @@ module Google
|
|
1097
1150
|
value_nil?(value) || value_nan?(value)
|
1098
1151
|
end
|
1099
1152
|
|
1100
|
-
def filter name, op_key, value
|
1101
|
-
field = StructuredQuery::FieldReference.new field_path: name.to_s
|
1102
|
-
operator = FILTER_OPS[op_key.to_s.downcase]
|
1103
|
-
raise ArgumentError, "unknown operator #{op_key}" if operator.nil?
|
1104
|
-
|
1105
|
-
if value_unary? value
|
1106
|
-
operator = case operator
|
1107
|
-
when :EQUAL
|
1108
|
-
value_nan?(value) ? :IS_NAN : :IS_NULL
|
1109
|
-
when :NOT_EQUAL
|
1110
|
-
value_nan?(value) ? :IS_NOT_NAN : :IS_NOT_NULL
|
1111
|
-
else
|
1112
|
-
raise ArgumentError, "can only perform '==' and '!=' comparisons on #{value} values"
|
1113
|
-
end
|
1114
|
-
|
1115
|
-
return StructuredQuery::Filter.new(
|
1116
|
-
unary_filter: StructuredQuery::UnaryFilter.new(
|
1117
|
-
field: field, op: operator
|
1118
|
-
)
|
1119
|
-
)
|
1120
|
-
end
|
1121
|
-
|
1122
|
-
value = Convert.raw_to_value value
|
1123
|
-
StructuredQuery::Filter.new(
|
1124
|
-
field_filter: StructuredQuery::FieldFilter.new(
|
1125
|
-
field: field, op: operator, value: value
|
1126
|
-
)
|
1127
|
-
)
|
1128
|
-
end
|
1129
|
-
|
1130
1153
|
def composite_filter
|
1131
1154
|
StructuredQuery::Filter.new(
|
1132
1155
|
composite_filter: StructuredQuery::CompositeFilter.new(op: :AND)
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Copyright 2023 Google LLC
|
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
|
+
# https://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
|
+
module Google
|
17
|
+
module Cloud
|
18
|
+
module Firestore
|
19
|
+
##
|
20
|
+
# @private Implements 5/5/5 ramp-up via Token Bucket algorithm.
|
21
|
+
#
|
22
|
+
# 5/5/5 is a ramp up strategy that starts with a budget of 500 operations per
|
23
|
+
# second. Additionally, every 5 minutes, the maximum budget can increase by
|
24
|
+
# 50%. Thus, at 5:01 into a long bulk-writing process, the maximum budget
|
25
|
+
# becomes 750 operations per second. At 10:01, the budget becomes 1,125
|
26
|
+
# operations per second.
|
27
|
+
#
|
28
|
+
class RateLimiter
|
29
|
+
DEFAULT_STARTING_MAXIMUM_OPS_PER_SECOND = 500.0
|
30
|
+
DEFAULT_PHASE_LENGTH = 300.0
|
31
|
+
|
32
|
+
attr_reader :bandwidth
|
33
|
+
|
34
|
+
##
|
35
|
+
# Initialize the object
|
36
|
+
def initialize starting_ops: nil, phase_length: nil
|
37
|
+
@start_time = time
|
38
|
+
@last_fetched = time
|
39
|
+
@bandwidth = (starting_ops || DEFAULT_STARTING_MAXIMUM_OPS_PER_SECOND).to_f
|
40
|
+
@phase_length = phase_length || DEFAULT_PHASE_LENGTH
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Wait till the number of tokens is available
|
45
|
+
# Assumes that the bandwidth is distributed evenly across the entire second.
|
46
|
+
#
|
47
|
+
# Example - If the limit is 500 qps, then it has been further broken down to 2e+6 nsec
|
48
|
+
# per query
|
49
|
+
#
|
50
|
+
# @return [nil]
|
51
|
+
def wait_for_tokens size
|
52
|
+
available_time = @last_fetched + (size / @bandwidth)
|
53
|
+
waiting_time = [0, available_time - time].max
|
54
|
+
sleep waiting_time
|
55
|
+
@last_fetched = time
|
56
|
+
increase_bandwidth
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
##
|
62
|
+
# Returns time elapsed since epoch.
|
63
|
+
#
|
64
|
+
# @return [Float] Float denoting time elapsed since epoch
|
65
|
+
def time
|
66
|
+
Time.now.to_f
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Increase the bandwidth as per 555 rule
|
71
|
+
#
|
72
|
+
# @return [nil]
|
73
|
+
def increase_bandwidth
|
74
|
+
intervals = (time - @start_time) / @phase_length
|
75
|
+
@bandwidth = (DEFAULT_STARTING_MAXIMUM_OPS_PER_SECOND * (1.5**intervals.floor)).to_f
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -30,29 +30,34 @@ module Google
|
|
30
30
|
attr_accessor :credentials
|
31
31
|
attr_accessor :timeout
|
32
32
|
attr_accessor :host
|
33
|
+
attr_accessor :database
|
33
34
|
|
34
35
|
##
|
35
36
|
# Creates a new Service instance.
|
36
|
-
def initialize project, credentials, host: nil, timeout: nil
|
37
|
+
def initialize project, credentials, host: nil, timeout: nil, database: nil, transport: :grpc
|
37
38
|
@project = project
|
38
39
|
@credentials = credentials
|
39
40
|
@host = host
|
40
41
|
@timeout = timeout
|
42
|
+
@database = database
|
43
|
+
@transport = transport
|
41
44
|
end
|
42
45
|
|
43
46
|
def firestore
|
44
|
-
@firestore ||=
|
45
|
-
V1::Firestore::Client
|
47
|
+
@firestore ||= begin
|
48
|
+
client_class = @transport == :rest ? V1::Firestore::Rest::Client : V1::Firestore::Client
|
49
|
+
client_class.new do |config|
|
46
50
|
config.credentials = credentials if credentials
|
47
51
|
config.timeout = timeout if timeout
|
48
52
|
config.endpoint = host if host
|
49
53
|
config.lib_name = "gccl"
|
50
54
|
config.lib_version = Google::Cloud::Firestore::VERSION
|
51
|
-
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}/databases
|
55
|
+
config.metadata = { "google-cloud-resource-prefix": "projects/#{@project}/databases/#{@database}" }
|
52
56
|
end
|
57
|
+
end
|
53
58
|
end
|
54
59
|
|
55
|
-
def get_documents document_paths, mask: nil, transaction: nil
|
60
|
+
def get_documents document_paths, mask: nil, transaction: nil, read_time: nil
|
56
61
|
batch_get_req = {
|
57
62
|
database: database_path,
|
58
63
|
documents: document_paths,
|
@@ -63,7 +68,9 @@ module Google
|
|
63
68
|
elsif transaction
|
64
69
|
batch_get_req[:new_transaction] = transaction
|
65
70
|
end
|
66
|
-
|
71
|
+
if read_time
|
72
|
+
batch_get_req[:read_time] = read_time_to_timestamp(read_time)
|
73
|
+
end
|
67
74
|
firestore.batch_get_documents batch_get_req, call_options(parent: database_path)
|
68
75
|
end
|
69
76
|
|
@@ -74,23 +81,25 @@ module Google
|
|
74
81
|
# the showMissing flag to true to support full document traversal. If
|
75
82
|
# there are too many documents, recommendation will be not to call this
|
76
83
|
# method.
|
77
|
-
def list_documents parent, collection_id, token: nil, max: nil
|
84
|
+
def list_documents parent, collection_id, token: nil, max: nil, read_time: nil
|
78
85
|
mask = { field_paths: [] }
|
79
|
-
paged_enum = firestore.list_documents parent:
|
86
|
+
paged_enum = firestore.list_documents parent: parent,
|
80
87
|
collection_id: collection_id,
|
81
|
-
page_size:
|
82
|
-
page_token:
|
83
|
-
mask:
|
84
|
-
show_missing:
|
88
|
+
page_size: max,
|
89
|
+
page_token: token,
|
90
|
+
mask: mask,
|
91
|
+
show_missing: true,
|
92
|
+
read_time: read_time_to_timestamp(read_time)
|
85
93
|
paged_enum.response
|
86
94
|
end
|
87
95
|
|
88
|
-
def list_collections parent, token: nil, max: nil
|
96
|
+
def list_collections parent, token: nil, max: nil, read_time: nil
|
89
97
|
firestore.list_collection_ids(
|
90
98
|
{
|
91
|
-
parent:
|
92
|
-
page_size:
|
93
|
-
page_token: token
|
99
|
+
parent: parent,
|
100
|
+
page_size: max,
|
101
|
+
page_token: token,
|
102
|
+
read_time: read_time_to_timestamp(read_time)
|
94
103
|
},
|
95
104
|
call_options(parent: database_path)
|
96
105
|
)
|
@@ -98,19 +107,20 @@ module Google
|
|
98
107
|
|
99
108
|
##
|
100
109
|
# Returns Google::Cloud::Firestore::V1::PartitionQueryResponse
|
101
|
-
def partition_query parent, query_grpc, partition_count, token: nil, max: nil
|
110
|
+
def partition_query parent, query_grpc, partition_count, token: nil, max: nil, read_time: nil
|
102
111
|
request = Google::Cloud::Firestore::V1::PartitionQueryRequest.new(
|
103
112
|
parent: parent,
|
104
113
|
structured_query: query_grpc,
|
105
114
|
partition_count: partition_count,
|
106
115
|
page_token: token,
|
107
|
-
page_size: max
|
116
|
+
page_size: max,
|
117
|
+
read_time: read_time_to_timestamp(read_time)
|
108
118
|
)
|
109
119
|
paged_enum = firestore.partition_query request
|
110
120
|
paged_enum.response
|
111
121
|
end
|
112
122
|
|
113
|
-
def run_query path, query_grpc, transaction: nil
|
123
|
+
def run_query path, query_grpc, transaction: nil, read_time: nil
|
114
124
|
run_query_req = {
|
115
125
|
parent: path,
|
116
126
|
structured_query: query_grpc
|
@@ -120,10 +130,28 @@ module Google
|
|
120
130
|
elsif transaction
|
121
131
|
run_query_req[:new_transaction] = transaction
|
122
132
|
end
|
133
|
+
if read_time
|
134
|
+
run_query_req[:read_time] = read_time_to_timestamp(read_time)
|
135
|
+
end
|
123
136
|
|
124
137
|
firestore.run_query run_query_req, call_options(parent: database_path)
|
125
138
|
end
|
126
139
|
|
140
|
+
##
|
141
|
+
# Returns Google::Cloud::Firestore::V1::RunAggregationQueryResponse
|
142
|
+
def run_aggregate_query parent, structured_aggregation_query, transaction: nil
|
143
|
+
request = Google::Cloud::Firestore::V1::RunAggregationQueryRequest.new(
|
144
|
+
parent: parent,
|
145
|
+
structured_aggregation_query: structured_aggregation_query
|
146
|
+
)
|
147
|
+
if transaction.is_a? String
|
148
|
+
request.transaction = transaction
|
149
|
+
elsif transaction
|
150
|
+
request.new_transaction = transaction
|
151
|
+
end
|
152
|
+
firestore.run_aggregation_query request
|
153
|
+
end
|
154
|
+
|
127
155
|
def listen enum
|
128
156
|
firestore.listen enum, call_options(parent: database_path)
|
129
157
|
end
|
@@ -158,18 +186,41 @@ module Google
|
|
158
186
|
)
|
159
187
|
end
|
160
188
|
|
161
|
-
|
189
|
+
##
|
190
|
+
# Makes the BatchWrite API call. Contains the list of write operations to be processed.
|
191
|
+
#
|
192
|
+
# @return [::Google::Cloud::Firestore::V1::BatchWriteResponse]
|
193
|
+
def batch_write writes
|
194
|
+
batch_write_req = {
|
195
|
+
database: database_path,
|
196
|
+
writes: writes
|
197
|
+
}
|
198
|
+
firestore.batch_write batch_write_req, call_options(parent: database_path)
|
199
|
+
end
|
200
|
+
|
201
|
+
def database_path project_id: project, database_id: database
|
162
202
|
# Originally used V1::FirestoreClient.database_root_path until it was removed in #5405.
|
163
203
|
"projects/#{project_id}/databases/#{database_id}"
|
164
204
|
end
|
165
205
|
|
166
|
-
def documents_path project_id: project, database_id:
|
206
|
+
def documents_path project_id: project, database_id: database
|
167
207
|
# Originally used V1::FirestoreClient.document_root_path until it was removed in #5405.
|
168
208
|
"projects/#{project_id}/databases/#{database_id}/documents"
|
169
209
|
end
|
170
210
|
|
171
211
|
def inspect
|
172
|
-
"#{self.class}(#{@project})"
|
212
|
+
"#{self.class}(#{@project})(#{@database})"
|
213
|
+
end
|
214
|
+
|
215
|
+
def read_time_to_timestamp read_time
|
216
|
+
return nil if read_time.nil?
|
217
|
+
|
218
|
+
raise TypeError, "read_time is expected to be a Time object" unless read_time.is_a? Time
|
219
|
+
|
220
|
+
Google::Protobuf::Timestamp.new(
|
221
|
+
seconds: read_time.to_i,
|
222
|
+
nanos: read_time.usec * 1000
|
223
|
+
)
|
173
224
|
end
|
174
225
|
|
175
226
|
protected
|
@@ -183,7 +234,7 @@ module Google
|
|
183
234
|
Gapic::CallOptions.new(**{
|
184
235
|
metadata: default_headers(parent),
|
185
236
|
page_token: token
|
186
|
-
}.
|
237
|
+
}.compact)
|
187
238
|
end
|
188
239
|
|
189
240
|
def document_mask mask
|
@@ -269,6 +269,51 @@ module Google
|
|
269
269
|
end
|
270
270
|
alias run get
|
271
271
|
|
272
|
+
##
|
273
|
+
# Retrieves aggregate query snapshots for the given value. Valid values can be
|
274
|
+
# a string representing either a document or a collection of documents,
|
275
|
+
# a document reference object, a collection reference object, or a query
|
276
|
+
# to be run.
|
277
|
+
#
|
278
|
+
# @param [AggregateQuery] aggregate_query
|
279
|
+
# An AggregateQuery object
|
280
|
+
#
|
281
|
+
# @yield [documents] The block for accessing the aggregate query snapshot.
|
282
|
+
# @yieldparam [AggregateQuerySnapshot] aggregate_snapshot An aggregate query snapshot.
|
283
|
+
#
|
284
|
+
# @example
|
285
|
+
# require "google/cloud/firestore"
|
286
|
+
#
|
287
|
+
# firestore = Google::Cloud::Firestore.new
|
288
|
+
#
|
289
|
+
# query = firestore.col "cities"
|
290
|
+
#
|
291
|
+
# # Create an aggregate query
|
292
|
+
# aq = query.aggregate_query
|
293
|
+
# .add_count
|
294
|
+
#
|
295
|
+
# firestore.transaction do |tx|
|
296
|
+
# tx.get_aggregate aq do |aggregate_snapshot|
|
297
|
+
# puts aggregate_snapshot.get
|
298
|
+
# end
|
299
|
+
# end
|
300
|
+
#
|
301
|
+
def get_aggregate aggregate_query
|
302
|
+
ensure_not_closed!
|
303
|
+
ensure_service!
|
304
|
+
|
305
|
+
return enum_for :get_aggregate, aggregate_query unless block_given?
|
306
|
+
|
307
|
+
results = service.run_aggregate_query aggregate_query.parent_path,
|
308
|
+
aggregate_query.to_grpc,
|
309
|
+
transaction: transaction_or_create
|
310
|
+
results.each do |result|
|
311
|
+
extract_transaction_from_result! result
|
312
|
+
next if result.result.nil?
|
313
|
+
yield AggregateQuerySnapshot.from_run_aggregate_query_response result
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
272
317
|
# @!endgroup
|
273
318
|
|
274
319
|
# @!group Modifications
|
@@ -643,10 +688,12 @@ module Google
|
|
643
688
|
|
644
689
|
##
|
645
690
|
# @private New Transaction reference object from a path.
|
646
|
-
def self.from_client client, previous_transaction: nil
|
691
|
+
def self.from_client client, previous_transaction: nil, read_time: nil, read_only: nil
|
647
692
|
new.tap do |s|
|
648
693
|
s.instance_variable_set :@client, client
|
649
694
|
s.instance_variable_set :@previous_transaction, previous_transaction
|
695
|
+
s.instance_variable_set :@read_time, read_time
|
696
|
+
s.instance_variable_set :@read_only, read_only
|
650
697
|
end
|
651
698
|
end
|
652
699
|
|
@@ -699,6 +746,10 @@ module Google
|
|
699
746
|
##
|
700
747
|
# @private
|
701
748
|
def transaction_opt
|
749
|
+
read_only = \
|
750
|
+
Google::Cloud::Firestore::V1::TransactionOptions::ReadOnly.new \
|
751
|
+
read_time: service.read_time_to_timestamp(@read_time)
|
752
|
+
|
702
753
|
read_write = \
|
703
754
|
Google::Cloud::Firestore::V1::TransactionOptions::ReadWrite.new
|
704
755
|
|
@@ -707,9 +758,11 @@ module Google
|
|
707
758
|
@previous_transaction = nil
|
708
759
|
end
|
709
760
|
|
710
|
-
|
711
|
-
|
712
|
-
|
761
|
+
if @read_only
|
762
|
+
Google::Cloud::Firestore::V1::TransactionOptions.new read_only: read_only
|
763
|
+
else
|
764
|
+
Google::Cloud::Firestore::V1::TransactionOptions.new read_write: read_write
|
765
|
+
end
|
713
766
|
end
|
714
767
|
|
715
768
|
##
|