google-cloud-firestore 3.0.0 → 3.1.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 +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +8 -0
- data/lib/google/cloud/firestore/aggregate_query.rb +62 -0
- data/lib/google/cloud/firestore/aggregate_query_explain_result.rb +152 -0
- data/lib/google/cloud/firestore/query.rb +125 -12
- data/lib/google/cloud/firestore/query_explain_result.rb +130 -0
- data/lib/google/cloud/firestore/service.rb +16 -4
- data/lib/google/cloud/firestore/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 791bfbf175ebfc435afa810200d9345d6faa8de259687c1013f31b775cd3c704
|
4
|
+
data.tar.gz: ad58d22b57213e43771e4c22ad17555120e99a63419a9a50c5a7571e89a08d00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f45cf2d0a38b4bfb799842a4feac370d7f7d06b27d3cb9104e49f2fb2d3be76357e8d5c2191ab8a8b313ac77b23aacb8ddd50f9a714bb63a04c022ffa82124b
|
7
|
+
data.tar.gz: 7a4e35af0173d3a4bed5f70fb77d5c27a11bcb143e30f0f75db49059f1c89f85239640ebee68a5c36a149bcefc97961a9a11e8dbd734a77dfe948bcddbb5d311
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 3.1.0 (2025-08-07)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* add AggregateQuery explanation features for RubyFirestore ([#30484](https://github.com/googleapis/google-cloud-ruby/issues/30484))
|
8
|
+
* add Query explanation features for Ruby Firestore ([#29469](https://github.com/googleapis/google-cloud-ruby/issues/29469))
|
9
|
+
* update routing headers to new format ([#30481](https://github.com/googleapis/google-cloud-ruby/issues/30481))
|
10
|
+
|
3
11
|
### 3.0.0 (2025-03-10)
|
4
12
|
|
5
13
|
### ⚠ BREAKING CHANGES
|
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
require "google/cloud/firestore/v1"
|
16
16
|
require "google/cloud/firestore/aggregate_query_snapshot"
|
17
|
+
require "google/cloud/firestore/aggregate_query_explain_result"
|
17
18
|
|
18
19
|
module Google
|
19
20
|
module Cloud
|
@@ -252,6 +253,60 @@ module Google
|
|
252
253
|
end
|
253
254
|
end
|
254
255
|
|
256
|
+
##
|
257
|
+
# Retrieves the query explanation for the aggregate query.
|
258
|
+
# By default, the query is only planned, not executed, returning only metrics from the
|
259
|
+
# planning stages. If `analyze` is set to `true` the query will be planned and executed,
|
260
|
+
# returning the `AggregateQuerySnapshot` alongside both planning and execution stage metrics.
|
261
|
+
#
|
262
|
+
# Unlike the enumerator returned from `AggregateQuery#get`, the `AggregateQueryExplainResult`
|
263
|
+
# caches its snapshot and metrics after the first access.
|
264
|
+
#
|
265
|
+
# @param [Boolean] analyze
|
266
|
+
# Whether to execute the query and return the execution stage metrics
|
267
|
+
# in addition to planning metrics.
|
268
|
+
# If set to `false` the query will be planned only and will return planning
|
269
|
+
# stage metrics without results.
|
270
|
+
# If set to `true` the query will be executed, and will return the query results,
|
271
|
+
# planning stage metrics, and execution stage metrics.
|
272
|
+
# Defaults to `false`.
|
273
|
+
#
|
274
|
+
# @return [AggregateQueryExplainResult]
|
275
|
+
#
|
276
|
+
# @example Getting only the planning stage metrics for the aggregate query
|
277
|
+
# require "google/cloud/firestore"
|
278
|
+
#
|
279
|
+
# firestore = Google::Cloud::Firestore.new
|
280
|
+
# query = firestore.col(:cities).aggregate_query.add_count
|
281
|
+
#
|
282
|
+
# explain_result = query.explain
|
283
|
+
# metrics = explain_result.explain_metrics
|
284
|
+
# puts "Plan summary: #{metrics.plan_summary}" if metrics&.plan_summary
|
285
|
+
#
|
286
|
+
# @example Getting planning and execution stage metrics, as well as aggregate query results
|
287
|
+
# require "google/cloud/firestore"
|
288
|
+
#
|
289
|
+
# firestore = Google::Cloud::Firestore.new
|
290
|
+
# query = firestore.col(:cities).aggregate_query.add_count
|
291
|
+
#
|
292
|
+
# explain_result = query.explain analyze: true
|
293
|
+
# metrics = explain_result.explain_metrics
|
294
|
+
# puts "Plan summary: #{metrics.plan_summary}" if metrics&.plan_summary
|
295
|
+
# puts "Results returned: #{metrics.execution_stats.results_returned}" if metrics&.execution_stats
|
296
|
+
# snapshot = explain_result.snapshot
|
297
|
+
# puts "Count: #{snapshot.get}" if snapshot
|
298
|
+
#
|
299
|
+
def explain analyze: false
|
300
|
+
ensure_service!
|
301
|
+
validate_analyze_option! analyze
|
302
|
+
|
303
|
+
explain_options = ::Google::Cloud::Firestore::V1::ExplainOptions.new analyze: analyze
|
304
|
+
|
305
|
+
responses_enum = service.run_aggregate_query @parent_path, @grpc, explain_options: explain_options
|
306
|
+
|
307
|
+
AggregateQueryExplainResult.new responses_enum
|
308
|
+
end
|
309
|
+
|
255
310
|
##
|
256
311
|
# @private
|
257
312
|
def to_grpc
|
@@ -279,6 +334,13 @@ module Google
|
|
279
334
|
ensure_client!
|
280
335
|
client.service
|
281
336
|
end
|
337
|
+
|
338
|
+
# @private
|
339
|
+
# Validates the analyze option.
|
340
|
+
def validate_analyze_option! analyze_value
|
341
|
+
return if [true, false].include? analyze_value
|
342
|
+
raise ArgumentError, "analyze must be a boolean"
|
343
|
+
end
|
282
344
|
end
|
283
345
|
end
|
284
346
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# Copyright 2025 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
|
+
require "google/cloud/firestore/aggregate_query_snapshot"
|
16
|
+
require "google/cloud/firestore/convert"
|
17
|
+
require "google/cloud/firestore/v1" # For ExplainMetrics and AggregationResult types
|
18
|
+
|
19
|
+
module Google
|
20
|
+
module Cloud
|
21
|
+
module Firestore
|
22
|
+
##
|
23
|
+
# # AggregateQueryExplainResult
|
24
|
+
#
|
25
|
+
# Represents the result of a Firestore aggregate query explanation.
|
26
|
+
# This class provides access to the
|
27
|
+
# {Google::Cloud::Firestore::V1::ExplainMetrics}, which contain details
|
28
|
+
# about the query plan and execution statistics. If the explanation was
|
29
|
+
# run with `analyze: true`, it also provides access to the
|
30
|
+
# {AggregateQuerySnapshot}.
|
31
|
+
#
|
32
|
+
# The metrics and snapshot (if applicable) are fetched and cached upon
|
33
|
+
# the first call to either {#explain_metrics} or {#snapshot}.
|
34
|
+
#
|
35
|
+
# @see AggregateQuery#explain
|
36
|
+
#
|
37
|
+
# @example Getting planning and execution metrics with the snapshot
|
38
|
+
# require "google/cloud/firestore"
|
39
|
+
#
|
40
|
+
# firestore = Google::Cloud::Firestore.new
|
41
|
+
# aggregate_query = firestore.col(:cities).aggregate_query.add_count
|
42
|
+
#
|
43
|
+
# explain_result = aggregate_query.explain analyze: true
|
44
|
+
#
|
45
|
+
# metrics = explain_result.explain_metrics
|
46
|
+
# if metrics
|
47
|
+
# puts "Plan summary: #{metrics.plan_summary&.to_json}"
|
48
|
+
# puts "Execution stats: #{metrics.execution_stats&.to_json}"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# snapshot = explain_result.snapshot
|
52
|
+
# puts "Count: #{snapshot.get}" if snapshot
|
53
|
+
#
|
54
|
+
# @example Getting only planning metrics
|
55
|
+
# require "google/cloud/firestore"
|
56
|
+
#
|
57
|
+
# firestore = Google::Cloud::Firestore.new
|
58
|
+
# aggregate_query = firestore.col(:cities).aggregate_query.add_count
|
59
|
+
#
|
60
|
+
# explain_result = aggregate_query.explain analyze: false # Default
|
61
|
+
#
|
62
|
+
# metrics = explain_result.explain_metrics
|
63
|
+
# puts "Plan summary: #{metrics.plan_summary&.to_json}" if metrics
|
64
|
+
#
|
65
|
+
# # Snapshot will be nil because analyze was false
|
66
|
+
# puts "Snapshot is nil: #{explain_result.snapshot.nil?}"
|
67
|
+
#
|
68
|
+
class AggregateQueryExplainResult
|
69
|
+
# Indicates whether the metrics and snapshot (if applicable) have been
|
70
|
+
# fetched from the server response and cached.
|
71
|
+
# This becomes `true` after the first call to {#explain_metrics} or
|
72
|
+
# {#snapshot}.
|
73
|
+
#
|
74
|
+
# @return [Boolean] `true` if data has been fetched, `false` otherwise.
|
75
|
+
attr_reader :metrics_fetched
|
76
|
+
alias metrics_fetched? metrics_fetched
|
77
|
+
|
78
|
+
##
|
79
|
+
# @private Creates a new AggregateQueryExplainResult.
|
80
|
+
#
|
81
|
+
# @param responses_enum [Enumerable<Google::Cloud::Firestore::V1::RunAggregationQueryResponse>]
|
82
|
+
# The enum of response objects from the gRPC call.
|
83
|
+
#
|
84
|
+
def initialize responses_enum
|
85
|
+
@responses_enum = responses_enum
|
86
|
+
|
87
|
+
@metrics_fetched = false
|
88
|
+
@explain_metrics = nil
|
89
|
+
@snapshot = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# The metrics from planning and potentially execution stages of the
|
93
|
+
# aggregate query.
|
94
|
+
#
|
95
|
+
# Calling this method for the first time will process the server
|
96
|
+
# responses to extract and cache the metrics (and snapshot if
|
97
|
+
# `analyze: true` was used). Subsequent calls return the cached metrics.
|
98
|
+
#
|
99
|
+
# @return [Google::Cloud::Firestore::V1::ExplainMetrics, nil]
|
100
|
+
# The query explanation metrics, or `nil` if no metrics were returned
|
101
|
+
# by the server.
|
102
|
+
#
|
103
|
+
def explain_metrics
|
104
|
+
ensure_fetched!
|
105
|
+
@explain_metrics
|
106
|
+
end
|
107
|
+
|
108
|
+
# The {AggregateQuerySnapshot} containing the aggregation results.
|
109
|
+
#
|
110
|
+
# This is only available if the explanation was run with `analyze: true`.
|
111
|
+
# If `analyze: false` was used, or if the query yielded no results
|
112
|
+
# even with `analyze: true`, this method returns `nil`.
|
113
|
+
#
|
114
|
+
# Calling this method for the first time will process the server
|
115
|
+
# responses to extract and cache the snapshot (and metrics).
|
116
|
+
# Subsequent calls return the cached snapshot.
|
117
|
+
#
|
118
|
+
# @return [AggregateQuerySnapshot, nil]
|
119
|
+
# The aggregate query snapshot if `analyze: true` was used and results
|
120
|
+
# are available, otherwise `nil`.
|
121
|
+
#
|
122
|
+
def snapshot
|
123
|
+
ensure_fetched!
|
124
|
+
@snapshot
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Processes the responses from the server to populate metrics and,
|
130
|
+
# if applicable, the snapshot. This method is called internally by
|
131
|
+
# {#explain_metrics} and {#snapshot} and ensures that processing
|
132
|
+
# happens only once.
|
133
|
+
# @private
|
134
|
+
def ensure_fetched!
|
135
|
+
return if @metrics_fetched
|
136
|
+
|
137
|
+
@responses_enum.each do |response|
|
138
|
+
if @explain_metrics.nil? && response.explain_metrics
|
139
|
+
@explain_metrics = response.explain_metrics
|
140
|
+
end
|
141
|
+
|
142
|
+
if @snapshot.nil? && response.result
|
143
|
+
@snapshot = AggregateQuerySnapshot.from_run_aggregate_query_response response
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@metrics_fetched = true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -20,6 +20,7 @@ require "google/cloud/firestore/convert"
|
|
20
20
|
require "google/cloud/firestore/aggregate_query"
|
21
21
|
require "google/cloud/firestore/filter"
|
22
22
|
require "json"
|
23
|
+
require "google/cloud/firestore/query_explain_result"
|
23
24
|
|
24
25
|
module Google
|
25
26
|
module Cloud
|
@@ -79,6 +80,12 @@ module Google
|
|
79
80
|
|
80
81
|
##
|
81
82
|
# @private Creates a new Query.
|
83
|
+
#
|
84
|
+
# @param [Google::Cloud::Firestore::V1::StructuredQuery] query The structured query object.
|
85
|
+
# @param [String] parent_path The parent path for the query.
|
86
|
+
# @param [Google::Cloud::Firestore::Client] client The firestore client object.
|
87
|
+
# @param [Symbol] limit_type (Optional) The type of limit query (:first or :last).
|
88
|
+
# Defaults to `nil` if not provided.
|
82
89
|
def initialize query, parent_path, client, limit_type: nil
|
83
90
|
query ||= StructuredQuery.new
|
84
91
|
@query = query
|
@@ -135,7 +142,7 @@ module Google
|
|
135
142
|
new_query.select.fields << field_ref
|
136
143
|
end
|
137
144
|
|
138
|
-
|
145
|
+
start_new_query new_query
|
139
146
|
end
|
140
147
|
|
141
148
|
##
|
@@ -171,7 +178,7 @@ module Google
|
|
171
178
|
|
172
179
|
new_query.from.last.all_descendants = true
|
173
180
|
|
174
|
-
|
181
|
+
start_new_query new_query
|
175
182
|
end
|
176
183
|
|
177
184
|
##
|
@@ -207,7 +214,7 @@ module Google
|
|
207
214
|
|
208
215
|
new_query.from.last.all_descendants = false
|
209
216
|
|
210
|
-
|
217
|
+
start_new_query new_query
|
211
218
|
end
|
212
219
|
|
213
220
|
##
|
@@ -305,7 +312,7 @@ module Google
|
|
305
312
|
add_filters_to_query new_query, new_filter.filter
|
306
313
|
end
|
307
314
|
|
308
|
-
|
315
|
+
start_new_query new_query
|
309
316
|
end
|
310
317
|
|
311
318
|
##
|
@@ -373,7 +380,7 @@ module Google
|
|
373
380
|
direction: order_direction(direction)
|
374
381
|
)
|
375
382
|
|
376
|
-
|
383
|
+
start_new_query new_query
|
377
384
|
end
|
378
385
|
alias order_by order
|
379
386
|
|
@@ -406,7 +413,7 @@ module Google
|
|
406
413
|
|
407
414
|
new_query.offset = num
|
408
415
|
|
409
|
-
|
416
|
+
start_new_query new_query
|
410
417
|
end
|
411
418
|
|
412
419
|
##
|
@@ -443,7 +450,7 @@ module Google
|
|
443
450
|
|
444
451
|
new_query.limit = Google::Protobuf::Int32Value.new value: num
|
445
452
|
|
446
|
-
|
453
|
+
start_new_query new_query, limit_type_override: :first
|
447
454
|
end
|
448
455
|
|
449
456
|
##
|
@@ -503,7 +510,7 @@ module Google
|
|
503
510
|
|
504
511
|
new_query.limit = Google::Protobuf::Int32Value.new value: num
|
505
512
|
|
506
|
-
|
513
|
+
start_new_query new_query, limit_type_override: :last
|
507
514
|
end
|
508
515
|
|
509
516
|
##
|
@@ -611,7 +618,7 @@ module Google
|
|
611
618
|
cursor.before = true
|
612
619
|
new_query.start_at = cursor
|
613
620
|
|
614
|
-
|
621
|
+
start_new_query new_query
|
615
622
|
end
|
616
623
|
|
617
624
|
##
|
@@ -720,7 +727,7 @@ module Google
|
|
720
727
|
cursor.before = false
|
721
728
|
new_query.start_at = cursor
|
722
729
|
|
723
|
-
|
730
|
+
start_new_query new_query
|
724
731
|
end
|
725
732
|
|
726
733
|
##
|
@@ -829,7 +836,7 @@ module Google
|
|
829
836
|
cursor.before = true
|
830
837
|
new_query.end_at = cursor
|
831
838
|
|
832
|
-
|
839
|
+
start_new_query new_query
|
833
840
|
end
|
834
841
|
|
835
842
|
##
|
@@ -938,7 +945,7 @@ module Google
|
|
938
945
|
cursor.before = false
|
939
946
|
new_query.end_at = cursor
|
940
947
|
|
941
|
-
|
948
|
+
start_new_query new_query
|
942
949
|
end
|
943
950
|
|
944
951
|
##
|
@@ -1001,6 +1008,83 @@ module Google
|
|
1001
1008
|
end
|
1002
1009
|
alias run get
|
1003
1010
|
|
1011
|
+
##
|
1012
|
+
# Retrieves the query explanation for the query.
|
1013
|
+
# By default the query is only planned, not executed. returning only metrics from the
|
1014
|
+
# planning stages. If `analyze` is set to `true` the query will be planned and executed,
|
1015
|
+
# returning the full query results alongside both planning and execution stage metrics.
|
1016
|
+
#
|
1017
|
+
# Unlike the Enumerator object that is returned from the `Query#get`,
|
1018
|
+
# iterating over QueryExplainResult multiple times will not result in
|
1019
|
+
# multiple requests to the server. The first set of results will be saved
|
1020
|
+
# and re-used instead.
|
1021
|
+
# This is to avoid the situations where the metrics change unpredictably when results are looked at.
|
1022
|
+
#
|
1023
|
+
# @param [Time] read_time Reads documents as they were at the given time.
|
1024
|
+
# This may not be older than 270 seconds. Optional
|
1025
|
+
#
|
1026
|
+
# @param [Boolean] analyze
|
1027
|
+
# Whether to execute the query and return the execution stage metrics
|
1028
|
+
# in addition to planning metrics.
|
1029
|
+
# If set to `false` the query will be planned only and will return planning
|
1030
|
+
# stage metrics without results.
|
1031
|
+
# If set to `true` the query will be executed, and will return the query results,
|
1032
|
+
# planning stage metrics, and execution stage metrics.
|
1033
|
+
# Defaults to `false`.
|
1034
|
+
#
|
1035
|
+
# @example Iterating over results multiple times
|
1036
|
+
# require "google/cloud/firestore"
|
1037
|
+
#
|
1038
|
+
# firestore = Google::Cloud::Firestore.new
|
1039
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
1040
|
+
# explanation_result = query.explain analyze: true
|
1041
|
+
# results = explanation_result.to_a
|
1042
|
+
# results_2 = explanation_result.to_a # same results, no re-query
|
1043
|
+
#
|
1044
|
+
# @return [QueryExplainResult]
|
1045
|
+
#
|
1046
|
+
# @example Getting only the planning stage metrics for the query
|
1047
|
+
# require "google/cloud/firestore"
|
1048
|
+
#
|
1049
|
+
# firestore = Google::Cloud::Firestore.new
|
1050
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
1051
|
+
#
|
1052
|
+
# # Get the execution plan without running the query
|
1053
|
+
# explain_result = query.explain
|
1054
|
+
# metrics = explain_result.explain_metrics
|
1055
|
+
# puts "Plan summary: #{metrics.plan_summary}" if metrics&.plan_summary
|
1056
|
+
#
|
1057
|
+
# @example Getting planning and execution stage metrics, as well as query results
|
1058
|
+
# require "google/cloud/firestore"
|
1059
|
+
#
|
1060
|
+
# firestore = Google::Cloud::Firestore.new
|
1061
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
1062
|
+
#
|
1063
|
+
# # Run the query and return metrics from the planning and execution stages
|
1064
|
+
# explain_result = query.explain analyze: true
|
1065
|
+
# metrics = explain_result.explain_metrics
|
1066
|
+
# puts "Plan summary: #{metrics.plan_summary}" if metrics&.plan_summary
|
1067
|
+
# puts "Results returned: #{metrics.execution_stats.results_returned}" if metrics&.execution_stats
|
1068
|
+
# results = explain_result.to_a
|
1069
|
+
#
|
1070
|
+
def explain read_time: false, analyze: false
|
1071
|
+
ensure_service!
|
1072
|
+
|
1073
|
+
# Validate analyze parameter
|
1074
|
+
unless [true, false].include? analyze
|
1075
|
+
raise ArgumentError, "analyze must be a boolean"
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
explain_options = ::Google::Cloud::Firestore::V1::ExplainOptions.new
|
1079
|
+
explain_options.analyze = analyze
|
1080
|
+
|
1081
|
+
results = service.run_query parent_path, @query, read_time: read_time, explain_options: explain_options
|
1082
|
+
|
1083
|
+
# Reverse the results for Query#limit_to_last queries since that method reversed the order_by directions.
|
1084
|
+
results = results.to_a.reverse if limit_type == :last
|
1085
|
+
QueryExplainResult.new results, client
|
1086
|
+
end
|
1087
|
+
|
1004
1088
|
##
|
1005
1089
|
# Creates an AggregateQuery object for the query.
|
1006
1090
|
#
|
@@ -1117,12 +1201,41 @@ module Google
|
|
1117
1201
|
|
1118
1202
|
##
|
1119
1203
|
# @private Start a new Query.
|
1204
|
+
#
|
1205
|
+
# This method creates and returns a new `Query` instance, initializing it with the provided parameters.
|
1206
|
+
#
|
1207
|
+
# @param [Google::Cloud::Firestore::V1::StructuredQuery] query
|
1208
|
+
# The structured query object representing the query to be executed.
|
1209
|
+
# @param [String] parent_path
|
1210
|
+
# The parent path of the collection or document the query is operating on.
|
1211
|
+
# @param [Google::Cloud::Firestore::Client] client
|
1212
|
+
# The Firestore client instance.
|
1213
|
+
# @param [Symbol, nil] limit_type
|
1214
|
+
# (Optional) The type of limit to apply to the query results, either `:first` or `:last`.
|
1215
|
+
# Defaults to `nil` if no limit is applied.
|
1216
|
+
# @return [Google::Cloud::Firestore::Query]
|
1217
|
+
# A new `Query` instance initialized with the given parameters.
|
1120
1218
|
def self.start query, parent_path, client, limit_type: nil
|
1121
1219
|
new query, parent_path, client, limit_type: limit_type
|
1122
1220
|
end
|
1123
1221
|
|
1124
1222
|
protected
|
1125
1223
|
|
1224
|
+
##
|
1225
|
+
# @private Helper for starting a new query copying existing parameters.
|
1226
|
+
#
|
1227
|
+
# @param [Google::Cloud::Firestore::V1::StructuredQuery] new_query
|
1228
|
+
# The new structured query object.
|
1229
|
+
# @param [Symbol] limit_type_override The limit type override for the new query
|
1230
|
+
#
|
1231
|
+
# @return [Query] A new query
|
1232
|
+
def start_new_query new_query, limit_type_override: nil
|
1233
|
+
Query.start(new_query,
|
1234
|
+
parent_path,
|
1235
|
+
client,
|
1236
|
+
limit_type: limit_type_override || limit_type)
|
1237
|
+
end
|
1238
|
+
|
1126
1239
|
##
|
1127
1240
|
# @private
|
1128
1241
|
StructuredQuery = Google::Cloud::Firestore::V1::StructuredQuery
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# Copyright 2024 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
|
+
# # QueryExplainResult
|
21
|
+
#
|
22
|
+
# Represents the result of a Firestore query explanation. This class
|
23
|
+
# provides an enumerable interface to iterate over the {DocumentSnapshot}
|
24
|
+
# results (if the explanation was run with `analyze: true`) and allows
|
25
|
+
# access to the {Google::Cloud::Firestore::V1::ExplainMetrics} which
|
26
|
+
# contain details about the query plan and execution statistics.
|
27
|
+
#
|
28
|
+
# Unlike the Enumerator object that is returned from the `Query#get`,
|
29
|
+
# iterating over QueryExplainResult multiple times will not result in
|
30
|
+
# multiple requests to the server. The first set of results will be saved
|
31
|
+
# and re-used instead.
|
32
|
+
#
|
33
|
+
# This is to avoid the situations where the metrics do not correspond to the results
|
34
|
+
# if results are partially re-enumerated
|
35
|
+
#
|
36
|
+
# @see Query#explain
|
37
|
+
#
|
38
|
+
# @example Iterating over results and accessing metrics
|
39
|
+
# require "google/cloud/firestore"
|
40
|
+
#
|
41
|
+
# firestore = Google::Cloud::Firestore.new
|
42
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
43
|
+
#
|
44
|
+
# # Run the query and return metrics from the planning and execution stages
|
45
|
+
# explanation_result = query.explain analyze: true
|
46
|
+
#
|
47
|
+
# explanation_result.each do |city_snapshot|
|
48
|
+
# puts "City: #{city_snapshot.document_id}, Population: #{city_snapshot[:population]}"
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# metrics = explanation_result.explain_metrics
|
52
|
+
# puts "Results returned: #{metrics.execution_stats.results_returned}" if metrics&.execution_stats
|
53
|
+
#
|
54
|
+
# @example Fetching metrics directly (which also iterates internally if needed)
|
55
|
+
# require "google/cloud/firestore"
|
56
|
+
#
|
57
|
+
# firestore = Google::Cloud::Firestore.new
|
58
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
59
|
+
#
|
60
|
+
# # Get the execution plan without running the query (or with analyze: true)
|
61
|
+
# explanation_result = query.explain analyze: false # or true
|
62
|
+
#
|
63
|
+
# metrics = explanation_result.explain_metrics
|
64
|
+
# puts "Plan summary: #{metrics.plan_summary}" if metrics&.plan_summary
|
65
|
+
# puts "Results returned: #{metrics.execution_stats.results_returned}" if metrics&.execution_stats
|
66
|
+
#
|
67
|
+
# @example Iterating over results multiple times
|
68
|
+
# require "google/cloud/firestore"
|
69
|
+
#
|
70
|
+
# firestore = Google::Cloud::Firestore.new
|
71
|
+
# query = firestore.col(:cities).where(:population, :>, 100000)
|
72
|
+
# explanation_result = query.explain analyze: true
|
73
|
+
# results = explanation_result.to_a
|
74
|
+
# results_2 = explanation_result.to_a # same results, no re-query
|
75
|
+
#
|
76
|
+
##
|
77
|
+
class QueryExplainResult
|
78
|
+
include Enumerable
|
79
|
+
|
80
|
+
# Indicates whether the {#explain_metrics} have been populated.
|
81
|
+
# This becomes `true` after iterating through the results (e.g., via {#each})
|
82
|
+
# or by explicitly calling {#explain_metrics}.
|
83
|
+
#
|
84
|
+
# @return [Boolean] `true` if metrics are populated, `false` otherwise.
|
85
|
+
attr_reader :metrics_fetched
|
86
|
+
alias metrics_fetched? metrics_fetched
|
87
|
+
|
88
|
+
# @private Creates a new QueryRunResult.
|
89
|
+
def initialize results_enum, client
|
90
|
+
@results_enum = results_enum
|
91
|
+
@client = client
|
92
|
+
@metrics_fetched = false
|
93
|
+
end
|
94
|
+
|
95
|
+
# The metrics from planning and execution stages of the query.
|
96
|
+
# Calling this the first time will enumerate and cache all results as well as cache the metrics.
|
97
|
+
#
|
98
|
+
# Subsequent calls will return the cached value.
|
99
|
+
#
|
100
|
+
# @return [Google::Cloud::Firestore::V1::ExplainMetrics] The query explanation metrics.
|
101
|
+
def explain_metrics
|
102
|
+
# rubocop:disable Lint/EmptyBlock
|
103
|
+
each {} unless metrics_fetched?
|
104
|
+
# rubocop:enable Lint/EmptyBlock
|
105
|
+
@explain_metrics
|
106
|
+
end
|
107
|
+
|
108
|
+
# Iterates over the document snapshots returned by the query explanation
|
109
|
+
# if `analyze: true` was used. If `analyze: false` was used, this
|
110
|
+
# method will still iterate but will not yield any documents, though it
|
111
|
+
# will populate the query explanation metrics.
|
112
|
+
#
|
113
|
+
# @yieldparam [DocumentSnapshot] snapshot A document snapshot from the query results.
|
114
|
+
# @return [Enumerator] If no block is given.
|
115
|
+
def each
|
116
|
+
return enum_for :each unless block_given?
|
117
|
+
@results ||= @results_enum.to_a
|
118
|
+
|
119
|
+
@results.each do |result|
|
120
|
+
@explain_metrics ||= result.explain_metrics if result.explain_metrics
|
121
|
+
@metrics_fetched = !@explain_metrics.nil?
|
122
|
+
next if result.document.nil?
|
123
|
+
|
124
|
+
yield DocumentSnapshot.from_query_result(result, @client)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -12,6 +12,7 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
+
require "cgi/escape"
|
15
16
|
|
16
17
|
require "google/cloud/env"
|
17
18
|
require "google/cloud/errors"
|
@@ -52,7 +53,8 @@ module Google
|
|
52
53
|
config.endpoint = host if host
|
53
54
|
config.lib_name = "gccl"
|
54
55
|
config.lib_version = Google::Cloud::Firestore::VERSION
|
55
|
-
|
56
|
+
routing_val = CGI.escapeURIComponent "projects/#{@project}/databases/#{@database}"
|
57
|
+
config.metadata = { "x-goog-request-params": "database=#{routing_val}" }
|
56
58
|
end
|
57
59
|
end
|
58
60
|
end
|
@@ -120,26 +122,32 @@ module Google
|
|
120
122
|
paged_enum.response
|
121
123
|
end
|
122
124
|
|
123
|
-
def run_query path, query_grpc, transaction: nil, read_time: nil
|
125
|
+
def run_query path, query_grpc, transaction: nil, read_time: nil, explain_options: nil
|
124
126
|
run_query_req = {
|
125
127
|
parent: path,
|
126
128
|
structured_query: query_grpc
|
127
129
|
}
|
130
|
+
|
128
131
|
if transaction.is_a? String
|
129
132
|
run_query_req[:transaction] = transaction
|
130
133
|
elsif transaction
|
131
134
|
run_query_req[:new_transaction] = transaction
|
132
135
|
end
|
136
|
+
|
133
137
|
if read_time
|
134
138
|
run_query_req[:read_time] = read_time_to_timestamp(read_time)
|
135
139
|
end
|
136
140
|
|
141
|
+
if explain_options
|
142
|
+
run_query_req[:explain_options] = explain_options
|
143
|
+
end
|
144
|
+
|
137
145
|
firestore.run_query run_query_req, call_options(parent: database_path)
|
138
146
|
end
|
139
147
|
|
140
148
|
##
|
141
149
|
# Returns Google::Cloud::Firestore::V1::RunAggregationQueryResponse
|
142
|
-
def run_aggregate_query parent, structured_aggregation_query, transaction: nil
|
150
|
+
def run_aggregate_query parent, structured_aggregation_query, transaction: nil, explain_options: nil
|
143
151
|
request = Google::Cloud::Firestore::V1::RunAggregationQueryRequest.new(
|
144
152
|
parent: parent,
|
145
153
|
structured_aggregation_query: structured_aggregation_query
|
@@ -149,6 +157,9 @@ module Google
|
|
149
157
|
elsif transaction
|
150
158
|
request.new_transaction = transaction
|
151
159
|
end
|
160
|
+
if explain_options
|
161
|
+
request.explain_options = explain_options
|
162
|
+
end
|
152
163
|
firestore.run_aggregation_query request
|
153
164
|
end
|
154
165
|
|
@@ -227,7 +238,8 @@ module Google
|
|
227
238
|
|
228
239
|
def default_headers parent = nil
|
229
240
|
parent ||= database_path
|
230
|
-
|
241
|
+
routing_val = CGI.escapeURIComponent parent
|
242
|
+
{ "x-goog-request-params" => "database=#{routing_val}" }
|
231
243
|
end
|
232
244
|
|
233
245
|
def call_options parent: nil, token: nil
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google-cloud-firestore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Google Inc
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: bigdecimal
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- lib/google-cloud-firestore.rb
|
100
100
|
- lib/google/cloud/firestore.rb
|
101
101
|
- lib/google/cloud/firestore/aggregate_query.rb
|
102
|
+
- lib/google/cloud/firestore/aggregate_query_explain_result.rb
|
102
103
|
- lib/google/cloud/firestore/aggregate_query_snapshot.rb
|
103
104
|
- lib/google/cloud/firestore/batch.rb
|
104
105
|
- lib/google/cloud/firestore/bulk_commit_batch.rb
|
@@ -125,6 +126,7 @@ files:
|
|
125
126
|
- lib/google/cloud/firestore/generate.rb
|
126
127
|
- lib/google/cloud/firestore/promise/future.rb
|
127
128
|
- lib/google/cloud/firestore/query.rb
|
129
|
+
- lib/google/cloud/firestore/query_explain_result.rb
|
128
130
|
- lib/google/cloud/firestore/query_listener.rb
|
129
131
|
- lib/google/cloud/firestore/query_partition.rb
|
130
132
|
- lib/google/cloud/firestore/query_snapshot.rb
|
@@ -155,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
155
157
|
- !ruby/object:Gem::Version
|
156
158
|
version: '0'
|
157
159
|
requirements: []
|
158
|
-
rubygems_version: 3.6.
|
160
|
+
rubygems_version: 3.6.9
|
159
161
|
specification_version: 4
|
160
162
|
summary: API Client library for Google Cloud Firestore API
|
161
163
|
test_files: []
|