google-cloud-firestore 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/.yardopts +8 -0
- data/LICENSE +201 -0
- data/README.md +30 -0
- data/lib/google-cloud-firestore.rb +106 -0
- data/lib/google/cloud/firestore.rb +514 -0
- data/lib/google/cloud/firestore/batch.rb +462 -0
- data/lib/google/cloud/firestore/client.rb +449 -0
- data/lib/google/cloud/firestore/collection_reference.rb +249 -0
- data/lib/google/cloud/firestore/commit_response.rb +145 -0
- data/lib/google/cloud/firestore/convert.rb +561 -0
- data/lib/google/cloud/firestore/credentials.rb +35 -0
- data/lib/google/cloud/firestore/document_reference.rb +468 -0
- data/lib/google/cloud/firestore/document_snapshot.rb +324 -0
- data/lib/google/cloud/firestore/field_path.rb +216 -0
- data/lib/google/cloud/firestore/field_value.rb +113 -0
- data/lib/google/cloud/firestore/generate.rb +35 -0
- data/lib/google/cloud/firestore/query.rb +651 -0
- data/lib/google/cloud/firestore/service.rb +176 -0
- data/lib/google/cloud/firestore/transaction.rb +726 -0
- data/lib/google/cloud/firestore/v1beta1.rb +121 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/common.rb +63 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/document.rb +134 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/firestore.rb +584 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/query.rb +215 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/firestore/v1beta1/write.rb +167 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/any.rb +124 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/timestamp.rb +106 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/protobuf/wrappers.rb +89 -0
- data/lib/google/cloud/firestore/v1beta1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/firestore/v1beta1/doc/overview.rb +53 -0
- data/lib/google/cloud/firestore/v1beta1/firestore_client.rb +974 -0
- data/lib/google/cloud/firestore/v1beta1/firestore_client_config.json +100 -0
- data/lib/google/cloud/firestore/version.rb +22 -0
- data/lib/google/firestore/v1beta1/common_pb.rb +44 -0
- data/lib/google/firestore/v1beta1/document_pb.rb +49 -0
- data/lib/google/firestore/v1beta1/firestore_pb.rb +219 -0
- data/lib/google/firestore/v1beta1/firestore_services_pb.rb +87 -0
- data/lib/google/firestore/v1beta1/query_pb.rb +103 -0
- data/lib/google/firestore/v1beta1/write_pb.rb +73 -0
- metadata +251 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
# Copyright 2017 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
|
+
require "google/cloud/env"
|
17
|
+
require "google/cloud/errors"
|
18
|
+
require "google/cloud/firestore/credentials"
|
19
|
+
require "google/cloud/firestore/version"
|
20
|
+
require "google/cloud/firestore/v1beta1"
|
21
|
+
|
22
|
+
module Google
|
23
|
+
module Cloud
|
24
|
+
module Firestore
|
25
|
+
##
|
26
|
+
# @private Represents the gRPC Firestore service, including all the API
|
27
|
+
# methods.
|
28
|
+
class Service
|
29
|
+
attr_accessor :project, :credentials, :timeout, :client_config
|
30
|
+
|
31
|
+
##
|
32
|
+
# @private Default project.
|
33
|
+
def self.default_project_id
|
34
|
+
ENV["FIRESTORE_PROJECT"] ||
|
35
|
+
ENV["GOOGLE_CLOUD_PROJECT"] ||
|
36
|
+
ENV["GCLOUD_PROJECT"] ||
|
37
|
+
Google::Cloud.env.project_id
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Creates a new Service instance.
|
42
|
+
def initialize project, credentials, timeout: nil, client_config: nil
|
43
|
+
@project = project
|
44
|
+
@credentials = credentials
|
45
|
+
@timeout = timeout
|
46
|
+
@client_config = client_config || {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def firestore
|
50
|
+
@firestore ||= \
|
51
|
+
V1beta1::FirestoreClient.new(
|
52
|
+
credentials: credentials,
|
53
|
+
timeout: timeout,
|
54
|
+
client_config: client_config,
|
55
|
+
lib_name: "gccl",
|
56
|
+
lib_version: Google::Cloud::Firestore::VERSION)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_documents document_paths, mask: nil, transaction: nil
|
60
|
+
batch_get_args = { mask: document_mask(mask) }
|
61
|
+
if transaction.is_a? String
|
62
|
+
batch_get_args[:transaction] = transaction
|
63
|
+
elsif transaction
|
64
|
+
batch_get_args[:new_transaction] = transaction
|
65
|
+
end
|
66
|
+
batch_get_args[:options] = call_options parent: database_path
|
67
|
+
|
68
|
+
execute do
|
69
|
+
firestore.batch_get_documents database_path, document_paths,
|
70
|
+
batch_get_args
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def list_collections parent, transaction: nil
|
75
|
+
list_args = {}
|
76
|
+
if transaction.is_a? String
|
77
|
+
list_args[:transaction] = transaction
|
78
|
+
elsif transaction
|
79
|
+
list_args[:new_transaction] = transaction
|
80
|
+
end
|
81
|
+
list_args[:options] = call_options parent: database_path
|
82
|
+
|
83
|
+
execute do
|
84
|
+
firestore.list_collection_ids parent, list_args
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_query path, query_grpc, transaction: nil
|
89
|
+
run_query_args = { structured_query: query_grpc }
|
90
|
+
if transaction.is_a? String
|
91
|
+
run_query_args[:transaction] = transaction
|
92
|
+
elsif transaction
|
93
|
+
run_query_args[:new_transaction] = transaction
|
94
|
+
end
|
95
|
+
run_query_args[:options] = call_options parent: database_path
|
96
|
+
|
97
|
+
execute do
|
98
|
+
firestore.run_query path, run_query_args
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def begin_transaction transaction_opt
|
103
|
+
options = call_options parent: database_path
|
104
|
+
|
105
|
+
execute do
|
106
|
+
firestore.begin_transaction database_path,
|
107
|
+
options_: transaction_opt,
|
108
|
+
options: options
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def commit writes, transaction: nil
|
113
|
+
commit_args = {}
|
114
|
+
commit_args[:transaction] = transaction if transaction
|
115
|
+
commit_args[:options] = call_options parent: database_path
|
116
|
+
|
117
|
+
execute do
|
118
|
+
firestore.commit database_path, writes, commit_args
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def rollback transaction
|
123
|
+
options = call_options parent: database_path
|
124
|
+
|
125
|
+
execute do
|
126
|
+
firestore.rollback database_path, transaction, options: options
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def database_path project_id: project, database_id: "(default)"
|
131
|
+
V1beta1::FirestoreClient.database_root_path project_id, database_id
|
132
|
+
end
|
133
|
+
|
134
|
+
def documents_path project_id: project, database_id: "(default)"
|
135
|
+
V1beta1::FirestoreClient.document_root_path project_id, database_id
|
136
|
+
end
|
137
|
+
|
138
|
+
def inspect
|
139
|
+
"#{self.class}(#{@project})"
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
|
144
|
+
def default_headers parent = nil
|
145
|
+
parent ||= database_path
|
146
|
+
{ "google-cloud-resource-prefix" => parent }
|
147
|
+
end
|
148
|
+
|
149
|
+
def call_options parent: nil, token: nil
|
150
|
+
Google::Gax::CallOptions.new({
|
151
|
+
kwargs: default_headers(parent),
|
152
|
+
page_token: token
|
153
|
+
}.delete_if { |_, v| v.nil? })
|
154
|
+
end
|
155
|
+
|
156
|
+
def document_mask mask
|
157
|
+
return nil if mask.nil?
|
158
|
+
|
159
|
+
mask = Array(mask).map(&:to_s).reject(&:nil?).reject(&:empty?)
|
160
|
+
return nil if mask.empty?
|
161
|
+
|
162
|
+
Google::Firestore::V1beta1::DocumentMask.new field_paths: mask
|
163
|
+
end
|
164
|
+
|
165
|
+
def execute
|
166
|
+
yield
|
167
|
+
rescue Google::Gax::GaxError => e
|
168
|
+
# GaxError wraps BadStatus, but exposes it as #cause
|
169
|
+
raise Google::Cloud::Error.from_error(e.cause)
|
170
|
+
rescue GRPC::BadStatus => e
|
171
|
+
raise Google::Cloud::Error.from_error(e)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,726 @@
|
|
1
|
+
# Copyright 2017 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
|
+
require "google/cloud/firestore/collection_reference"
|
17
|
+
require "google/cloud/firestore/document_reference"
|
18
|
+
require "google/cloud/firestore/document_snapshot"
|
19
|
+
require "google/cloud/firestore/commit_response"
|
20
|
+
require "google/cloud/firestore/convert"
|
21
|
+
|
22
|
+
module Google
|
23
|
+
module Cloud
|
24
|
+
module Firestore
|
25
|
+
##
|
26
|
+
# # Transaction
|
27
|
+
#
|
28
|
+
# A transaction in Cloud Firestore is a set of reads and writes that
|
29
|
+
# execute atomically at a single logical point in time.
|
30
|
+
#
|
31
|
+
# All changes are accumulated in memory until the block passed to
|
32
|
+
# {Database#transaction} completes. Transactions will be automatically
|
33
|
+
# retried when documents change before the transaction is committed. See
|
34
|
+
# {Database#transaction}.
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# require "google/cloud/firestore"
|
38
|
+
#
|
39
|
+
# firestore = Google::Cloud::Firestore.new
|
40
|
+
#
|
41
|
+
# city = firestore.col("cities").doc("SF")
|
42
|
+
# city.set({ name: "San Francisco",
|
43
|
+
# state: "CA",
|
44
|
+
# country: "USA",
|
45
|
+
# capital: false,
|
46
|
+
# population: 860000 })
|
47
|
+
#
|
48
|
+
# firestore.transaction do |tx|
|
49
|
+
# new_population = tx.get(city).data[:population] + 1
|
50
|
+
# tx.update(city, { population: new_population })
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
class Transaction
|
54
|
+
##
|
55
|
+
# @private New Transaction object.
|
56
|
+
def initialize
|
57
|
+
@writes = []
|
58
|
+
@transaction_id = nil
|
59
|
+
@previous_transaction = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# The transaction identifier.
|
64
|
+
#
|
65
|
+
# @return [String] transaction identifier.
|
66
|
+
def transaction_id
|
67
|
+
@transaction_id
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# The client the Cloud Firestore transaction belongs to.
|
72
|
+
#
|
73
|
+
# @return [Client] firestore client.
|
74
|
+
def firestore
|
75
|
+
@client
|
76
|
+
end
|
77
|
+
alias_method :client, :firestore
|
78
|
+
|
79
|
+
# @!group Access
|
80
|
+
|
81
|
+
##
|
82
|
+
# Retrieves a list of document snapshots.
|
83
|
+
#
|
84
|
+
# @param [String, DocumentReference] docs One or more strings
|
85
|
+
# representing the path of the document, or document reference
|
86
|
+
# objects.
|
87
|
+
#
|
88
|
+
# @yield [documents] The block for accessing the document snapshots.
|
89
|
+
# @yieldparam [DocumentSnapshot] document A document snapshot.
|
90
|
+
#
|
91
|
+
# @return [Enumerator<DocumentSnapshot>] document snapshots list.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# require "google/cloud/firestore"
|
95
|
+
#
|
96
|
+
# firestore = Google::Cloud::Firestore.new
|
97
|
+
#
|
98
|
+
# firestore.transaction do |tx|
|
99
|
+
# # Get and print city documents
|
100
|
+
# tx.get_all("cities/NYC", "cities/SF", "cities/LA").each do |city|
|
101
|
+
# puts "#{city.document_id} has #{city[:population]} residents."
|
102
|
+
# end
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
def get_all *docs
|
106
|
+
ensure_not_closed!
|
107
|
+
ensure_service!
|
108
|
+
|
109
|
+
return enum_for(:get_all, docs) unless block_given?
|
110
|
+
|
111
|
+
doc_paths = Array(docs).flatten.map do |doc_path|
|
112
|
+
coalesce_doc_path_argument doc_path
|
113
|
+
end
|
114
|
+
|
115
|
+
results = service.get_documents \
|
116
|
+
doc_paths, transaction: transaction_or_create
|
117
|
+
results.each do |result|
|
118
|
+
extract_transaction_from_result! result
|
119
|
+
next if result.result.nil?
|
120
|
+
yield DocumentSnapshot.from_batch_result(result, self)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
alias_method :get_docs, :get_all
|
124
|
+
alias_method :get_documents, :get_all
|
125
|
+
alias_method :find, :get_all
|
126
|
+
|
127
|
+
##
|
128
|
+
# Retrieves document snapshots for the given value. Valid values can be
|
129
|
+
# a string representing either a document or a collection of documents,
|
130
|
+
# a document reference object, a collection reference object, or a query
|
131
|
+
# to be run.
|
132
|
+
#
|
133
|
+
# @param [String, DocumentReference, CollectionReference, Query] obj
|
134
|
+
# A string representing the path of a document or collection, a
|
135
|
+
# document reference object, a collection reference object, or a query
|
136
|
+
# to run.
|
137
|
+
#
|
138
|
+
# @yield [documents] The block for accessing the document snapshots.
|
139
|
+
# @yieldparam [DocumentReference] document A document snapshot.
|
140
|
+
#
|
141
|
+
# @return [DocumentReference, Enumerator<DocumentReference>] A
|
142
|
+
# single document snapshot when passed a document path a document
|
143
|
+
# reference, or a list of document snapshots when passed other valid
|
144
|
+
# values.
|
145
|
+
#
|
146
|
+
# @example Get a document snapshot given a document path:
|
147
|
+
# require "google/cloud/firestore"
|
148
|
+
#
|
149
|
+
# firestore = Google::Cloud::Firestore.new
|
150
|
+
#
|
151
|
+
# firestore.transaction do |tx|
|
152
|
+
# # Get a document snapshot
|
153
|
+
# nyc_snap = tx.get "cities/NYC"
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# @example Get a document snapshot given a document reference:
|
157
|
+
# require "google/cloud/firestore"
|
158
|
+
#
|
159
|
+
# firestore = Google::Cloud::Firestore.new
|
160
|
+
#
|
161
|
+
# # Get a document reference
|
162
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
163
|
+
#
|
164
|
+
# firestore.transaction do |tx|
|
165
|
+
# # Get a document snapshot
|
166
|
+
# nyc_snap = tx.get nyc_ref
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# @example Get document snapshots given a collection path:
|
170
|
+
# require "google/cloud/firestore"
|
171
|
+
#
|
172
|
+
# firestore = Google::Cloud::Firestore.new
|
173
|
+
#
|
174
|
+
# firestore.transaction do |tx|
|
175
|
+
# # Get documents for a collection path
|
176
|
+
# tx.get("cities").each do |city|
|
177
|
+
# # Update the city population by 1
|
178
|
+
# tx.update(city, { population: city[:population] + 1})
|
179
|
+
# end
|
180
|
+
# end
|
181
|
+
#
|
182
|
+
# @example Get document snapshots given a collection reference:
|
183
|
+
# require "google/cloud/firestore"
|
184
|
+
#
|
185
|
+
# firestore = Google::Cloud::Firestore.new
|
186
|
+
#
|
187
|
+
# # Get a collection reference
|
188
|
+
# cities_col = firestore.col :cities
|
189
|
+
#
|
190
|
+
# firestore.transaction do |tx|
|
191
|
+
# # Get documents for a collection
|
192
|
+
# tx.get(cities_col).each do |city|
|
193
|
+
# # Update the city population by 1
|
194
|
+
# tx.update(city, { population: city[:population] + 1})
|
195
|
+
# end
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# @example Get document snapshots given a query:
|
199
|
+
# require "google/cloud/firestore"
|
200
|
+
#
|
201
|
+
# firestore = Google::Cloud::Firestore.new
|
202
|
+
#
|
203
|
+
# # Create a query
|
204
|
+
# query = firestore.col(:cities).select(:population)
|
205
|
+
#
|
206
|
+
# firestore.transaction do |tx|
|
207
|
+
# # Get/run a query
|
208
|
+
# tx.get(query).each do |city|
|
209
|
+
# # Update the city population by 1
|
210
|
+
# tx.update(city, { population: city[:population] + 1})
|
211
|
+
# end
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
def get obj
|
215
|
+
ensure_not_closed!
|
216
|
+
ensure_service!
|
217
|
+
|
218
|
+
obj = coalesce_get_argument obj
|
219
|
+
|
220
|
+
if obj.is_a?(DocumentReference)
|
221
|
+
doc = get_all([obj]).first
|
222
|
+
yield doc if block_given?
|
223
|
+
return doc
|
224
|
+
end
|
225
|
+
|
226
|
+
return enum_for(:get, obj) unless block_given?
|
227
|
+
|
228
|
+
results = service.run_query obj.parent_path, obj.query,
|
229
|
+
transaction: transaction_or_create
|
230
|
+
results.each do |result|
|
231
|
+
extract_transaction_from_result! result
|
232
|
+
next if result.document.nil?
|
233
|
+
yield DocumentSnapshot.from_query_result(result, self)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
alias_method :run, :get
|
237
|
+
|
238
|
+
# @!endgroup
|
239
|
+
|
240
|
+
# @!group Modifications
|
241
|
+
|
242
|
+
##
|
243
|
+
# Creates a document with the provided data (fields and values).
|
244
|
+
#
|
245
|
+
# The operation will fail if the document already exists.
|
246
|
+
#
|
247
|
+
# @param [String, DocumentReference] doc A string representing the
|
248
|
+
# path of the document, or a document reference object.
|
249
|
+
# @param [Hash] data The document's fields and values.
|
250
|
+
#
|
251
|
+
# @example Create a document using a document path:
|
252
|
+
# require "google/cloud/firestore"
|
253
|
+
#
|
254
|
+
# firestore = Google::Cloud::Firestore.new
|
255
|
+
#
|
256
|
+
# firestore.transaction do |tx|
|
257
|
+
# tx.create("cities/NYC", { name: "New York City" })
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
# @example Create a document using a document reference:
|
261
|
+
# require "google/cloud/firestore"
|
262
|
+
#
|
263
|
+
# firestore = Google::Cloud::Firestore.new
|
264
|
+
#
|
265
|
+
# # Get a document reference
|
266
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
267
|
+
#
|
268
|
+
# firestore.transaction do |tx|
|
269
|
+
# tx.create(nyc_ref, { name: "New York City" })
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# @example Create a document and set a field to server_time:
|
273
|
+
# require "google/cloud/firestore"
|
274
|
+
#
|
275
|
+
# firestore = Google::Cloud::Firestore.new
|
276
|
+
#
|
277
|
+
# # Get a document reference
|
278
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
279
|
+
#
|
280
|
+
# firestore.transaction do |tx|
|
281
|
+
# tx.create(nyc_ref, { name: "New York City",
|
282
|
+
# updated_at: firestore.field_server_time })
|
283
|
+
# end
|
284
|
+
#
|
285
|
+
def create doc, data
|
286
|
+
ensure_not_closed!
|
287
|
+
|
288
|
+
doc_path = coalesce_doc_path_argument doc
|
289
|
+
|
290
|
+
@writes << Convert.writes_for_create(doc_path, data)
|
291
|
+
|
292
|
+
nil
|
293
|
+
end
|
294
|
+
|
295
|
+
##
|
296
|
+
# Writes the provided data (fields and values) to the provided document.
|
297
|
+
# If the document does not exist, it will be created. By default, the
|
298
|
+
# provided data overwrites existing data, but the provided data can be
|
299
|
+
# merged into the existing document using the `merge` argument.
|
300
|
+
#
|
301
|
+
# If you're not sure whether the document exists, use the `merge`
|
302
|
+
# argument to merge the new data with any existing document data to
|
303
|
+
# avoid overwriting entire documents.
|
304
|
+
#
|
305
|
+
# @param [String, DocumentReference] doc A string representing the
|
306
|
+
# path of the document, or a document reference object.
|
307
|
+
# @param [Hash] data The document's fields and values.
|
308
|
+
# @param [Boolean, FieldPath, String, Symbol] merge When
|
309
|
+
# `true`, all provided data is merged with the existing document data.
|
310
|
+
# When the argument is one or more field path, only the data for
|
311
|
+
# fields in this argument is merged with the existing document data.
|
312
|
+
# The default is to not merge, but to instead overwrite the existing
|
313
|
+
# document data.
|
314
|
+
#
|
315
|
+
# @example Set a document using a document path:
|
316
|
+
# require "google/cloud/firestore"
|
317
|
+
#
|
318
|
+
# firestore = Google::Cloud::Firestore.new
|
319
|
+
#
|
320
|
+
# firestore.transaction do |tx|
|
321
|
+
# # Update a document
|
322
|
+
# tx.set("cities/NYC", { name: "New York City" })
|
323
|
+
# end
|
324
|
+
#
|
325
|
+
# @example Create a document using a document reference:
|
326
|
+
# require "google/cloud/firestore"
|
327
|
+
#
|
328
|
+
# firestore = Google::Cloud::Firestore.new
|
329
|
+
#
|
330
|
+
# # Get a document reference
|
331
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
332
|
+
#
|
333
|
+
# firestore.transaction do |tx|
|
334
|
+
# # Update a document
|
335
|
+
# tx.set(nyc_ref, { name: "New York City" })
|
336
|
+
# end
|
337
|
+
#
|
338
|
+
# @example Set a document and merge all data:
|
339
|
+
# require "google/cloud/firestore"
|
340
|
+
#
|
341
|
+
# firestore = Google::Cloud::Firestore.new
|
342
|
+
#
|
343
|
+
# firestore.transaction do |tx|
|
344
|
+
# tx.set("cities/NYC", { name: "New York City" }, merge: true)
|
345
|
+
# end
|
346
|
+
#
|
347
|
+
# @example Set a document and merge only name:
|
348
|
+
# require "google/cloud/firestore"
|
349
|
+
#
|
350
|
+
# firestore = Google::Cloud::Firestore.new
|
351
|
+
#
|
352
|
+
# firestore.transaction do |tx|
|
353
|
+
# tx.set("cities/NYC", { name: "New York City" }, merge: :name)
|
354
|
+
# end
|
355
|
+
#
|
356
|
+
# @example Set a document and deleting a field using merge:
|
357
|
+
# require "google/cloud/firestore"
|
358
|
+
#
|
359
|
+
# firestore = Google::Cloud::Firestore.new
|
360
|
+
#
|
361
|
+
# # Get a document reference
|
362
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
363
|
+
#
|
364
|
+
# nyc_data = { name: "New York City",
|
365
|
+
# trash: firestore.field_delete }
|
366
|
+
#
|
367
|
+
# firestore.transaction do |tx|
|
368
|
+
# tx.set(nyc_ref, nyc_data, merge: true)
|
369
|
+
# end
|
370
|
+
#
|
371
|
+
# @example Set a document and set a field to server_time:
|
372
|
+
# require "google/cloud/firestore"
|
373
|
+
#
|
374
|
+
# firestore = Google::Cloud::Firestore.new
|
375
|
+
#
|
376
|
+
# # Get a document reference
|
377
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
378
|
+
#
|
379
|
+
# nyc_data = { name: "New York City",
|
380
|
+
# updated_at: firestore.field_server_time }
|
381
|
+
#
|
382
|
+
# firestore.transaction do |tx|
|
383
|
+
# tx.set(nyc_ref, nyc_data, merge: true)
|
384
|
+
# end
|
385
|
+
#
|
386
|
+
def set doc, data, merge: nil
|
387
|
+
ensure_not_closed!
|
388
|
+
|
389
|
+
doc_path = coalesce_doc_path_argument doc
|
390
|
+
|
391
|
+
@writes << Convert.writes_for_set(doc_path, data, merge: merge)
|
392
|
+
|
393
|
+
nil
|
394
|
+
end
|
395
|
+
|
396
|
+
##
|
397
|
+
# Updates the document with the provided data (fields and values). The
|
398
|
+
# provided data is merged into the existing document data.
|
399
|
+
#
|
400
|
+
# The operation will fail if the document does not exist.
|
401
|
+
#
|
402
|
+
# @param [String, DocumentReference] doc A string representing the
|
403
|
+
# path of the document, or a document reference object.
|
404
|
+
# @param [Hash<FieldPath|String|Symbol, Object>] data The document's
|
405
|
+
# fields and values.
|
406
|
+
#
|
407
|
+
# The top-level keys in the data hash are considered field paths, and
|
408
|
+
# can either be a FieldPath object, or a string representing the
|
409
|
+
# nested fields. In other words the string represents individual
|
410
|
+
# fields joined by ".". Fields containing `~`, `*`, `/`, `[`, `]`, and
|
411
|
+
# `.` cannot be in a dotted string, and should provided using a
|
412
|
+
# {FieldPath} object instead.
|
413
|
+
# @param [Time] update_time When set, the document must have been last
|
414
|
+
# updated at that time. Optional.
|
415
|
+
#
|
416
|
+
# @example Update a document using a document path:
|
417
|
+
# require "google/cloud/firestore"
|
418
|
+
#
|
419
|
+
# firestore = Google::Cloud::Firestore.new
|
420
|
+
#
|
421
|
+
# firestore.transaction do |tx|
|
422
|
+
# tx.update("cities/NYC", { name: "New York City" })
|
423
|
+
# end
|
424
|
+
#
|
425
|
+
# @example Directly update a deeply-nested field with a `FieldPath`:
|
426
|
+
# require "google/cloud/firestore"
|
427
|
+
#
|
428
|
+
# firestore = Google::Cloud::Firestore.new
|
429
|
+
#
|
430
|
+
# nested_field_path = Google::Cloud::Firestore::FieldPath.new(
|
431
|
+
# :favorites, :food
|
432
|
+
# )
|
433
|
+
#
|
434
|
+
# firestore.transaction do |tx|
|
435
|
+
# tx.update("users/frank", { nested_field_path: "Pasta" })
|
436
|
+
# end
|
437
|
+
#
|
438
|
+
# @example Update a document using a document reference:
|
439
|
+
# require "google/cloud/firestore"
|
440
|
+
#
|
441
|
+
# firestore = Google::Cloud::Firestore.new
|
442
|
+
#
|
443
|
+
# # Get a document reference
|
444
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
445
|
+
#
|
446
|
+
# firestore.transaction do |tx|
|
447
|
+
# tx.update(nyc_ref, { name: "New York City" })
|
448
|
+
# end
|
449
|
+
#
|
450
|
+
# @example Update a document using the `update_time` precondition:
|
451
|
+
# require "google/cloud/firestore"
|
452
|
+
#
|
453
|
+
# firestore = Google::Cloud::Firestore.new
|
454
|
+
#
|
455
|
+
# last_updated_at = Time.now - 42 # 42 seconds ago
|
456
|
+
#
|
457
|
+
# firestore.transaction do |tx|
|
458
|
+
# tx.update("cities/NYC", { name: "New York City" },
|
459
|
+
# update_time: last_updated_at)
|
460
|
+
# end
|
461
|
+
#
|
462
|
+
# @example Update a document and deleting a field:
|
463
|
+
# require "google/cloud/firestore"
|
464
|
+
#
|
465
|
+
# firestore = Google::Cloud::Firestore.new
|
466
|
+
#
|
467
|
+
# # Get a document reference
|
468
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
469
|
+
#
|
470
|
+
# nyc_data = { name: "New York City",
|
471
|
+
# trash: firestore.field_delete }
|
472
|
+
#
|
473
|
+
# firestore.transaction do |tx|
|
474
|
+
# tx.update(nyc_ref, nyc_data)
|
475
|
+
# end
|
476
|
+
#
|
477
|
+
# @example Update a document and set a field to server_time:
|
478
|
+
# require "google/cloud/firestore"
|
479
|
+
#
|
480
|
+
# firestore = Google::Cloud::Firestore.new
|
481
|
+
#
|
482
|
+
# # Get a document reference
|
483
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
484
|
+
#
|
485
|
+
# nyc_data = { name: "New York City",
|
486
|
+
# updated_at: firestore.field_server_time }
|
487
|
+
#
|
488
|
+
# firestore.transaction do |tx|
|
489
|
+
# tx.update(nyc_ref, nyc_data)
|
490
|
+
# end
|
491
|
+
#
|
492
|
+
def update doc, data, update_time: nil
|
493
|
+
ensure_not_closed!
|
494
|
+
|
495
|
+
doc_path = coalesce_doc_path_argument doc
|
496
|
+
|
497
|
+
@writes << Convert.writes_for_update(doc_path, data,
|
498
|
+
update_time: update_time)
|
499
|
+
|
500
|
+
nil
|
501
|
+
end
|
502
|
+
|
503
|
+
##
|
504
|
+
# Deletes a document from the database.
|
505
|
+
#
|
506
|
+
# @param [String, DocumentReference] doc A string representing the
|
507
|
+
# path of the document, or a document reference object.
|
508
|
+
# @param [Boolean] exists Whether the document must exist. When `true`,
|
509
|
+
# the document must exist or an error is raised. Default is `false`.
|
510
|
+
# Optional.
|
511
|
+
# @param [Time] update_time When set, the document must have been last
|
512
|
+
# updated at that time. Optional.
|
513
|
+
#
|
514
|
+
# @example Delete a document using a document path:
|
515
|
+
# require "google/cloud/firestore"
|
516
|
+
#
|
517
|
+
# firestore = Google::Cloud::Firestore.new
|
518
|
+
#
|
519
|
+
# firestore.transaction do |tx|
|
520
|
+
# # Delete a document
|
521
|
+
# tx.delete "cities/NYC"
|
522
|
+
# end
|
523
|
+
#
|
524
|
+
# @example Delete a document using a document reference:
|
525
|
+
# require "google/cloud/firestore"
|
526
|
+
#
|
527
|
+
# firestore = Google::Cloud::Firestore.new
|
528
|
+
#
|
529
|
+
# # Get a document reference
|
530
|
+
# nyc_ref = firestore.doc "cities/NYC"
|
531
|
+
#
|
532
|
+
# firestore.transaction do |tx|
|
533
|
+
# # Delete a document
|
534
|
+
# tx.delete nyc_ref
|
535
|
+
# end
|
536
|
+
#
|
537
|
+
# @example Delete a document using `exists`:
|
538
|
+
# require "google/cloud/firestore"
|
539
|
+
#
|
540
|
+
# firestore = Google::Cloud::Firestore.new
|
541
|
+
#
|
542
|
+
# firestore.transaction do |tx|
|
543
|
+
# # Delete a document
|
544
|
+
# tx.delete "cities/NYC", exists: true
|
545
|
+
# end
|
546
|
+
#
|
547
|
+
# @example Delete a document using the `update_time` precondition:
|
548
|
+
# require "google/cloud/firestore"
|
549
|
+
#
|
550
|
+
# firestore = Google::Cloud::Firestore.new
|
551
|
+
#
|
552
|
+
# last_updated_at = Time.now - 42 # 42 seconds ago
|
553
|
+
#
|
554
|
+
# firestore.transaction do |tx|
|
555
|
+
# # Delete a document
|
556
|
+
# tx.delete "cities/NYC", update_time: last_updated_at
|
557
|
+
# end
|
558
|
+
#
|
559
|
+
def delete doc, exists: nil, update_time: nil
|
560
|
+
ensure_not_closed!
|
561
|
+
|
562
|
+
doc_path = coalesce_doc_path_argument doc
|
563
|
+
|
564
|
+
@writes << Convert.write_for_delete(
|
565
|
+
doc_path, exists: exists, update_time: update_time)
|
566
|
+
|
567
|
+
nil
|
568
|
+
end
|
569
|
+
|
570
|
+
# @!endgroup
|
571
|
+
|
572
|
+
##
|
573
|
+
# @private commit the transaction
|
574
|
+
def commit
|
575
|
+
ensure_not_closed!
|
576
|
+
|
577
|
+
if @transaction_id.nil? && @writes.empty?
|
578
|
+
@closed = true
|
579
|
+
return CommitResponse.from_grpc nil, @writes
|
580
|
+
end
|
581
|
+
|
582
|
+
ensure_transaction_id!
|
583
|
+
|
584
|
+
resp = service.commit @writes.flatten, transaction: transaction_id
|
585
|
+
@closed = true
|
586
|
+
CommitResponse.from_grpc resp, @writes
|
587
|
+
end
|
588
|
+
|
589
|
+
##
|
590
|
+
# @private rollback and close the transaction
|
591
|
+
def rollback
|
592
|
+
ensure_not_closed!
|
593
|
+
|
594
|
+
if @transaction_id.nil? && @writes.empty?
|
595
|
+
@closed = true
|
596
|
+
return
|
597
|
+
end
|
598
|
+
|
599
|
+
service.rollback @transaction_id
|
600
|
+
@closed = true
|
601
|
+
nil
|
602
|
+
end
|
603
|
+
|
604
|
+
##
|
605
|
+
# @private the transaction is complete and closed
|
606
|
+
def closed?
|
607
|
+
@closed
|
608
|
+
end
|
609
|
+
|
610
|
+
##
|
611
|
+
# @private New Transaction reference object from a path.
|
612
|
+
def self.from_client client, previous_transaction: nil
|
613
|
+
new.tap do |s|
|
614
|
+
s.instance_variable_set :@client, client
|
615
|
+
s.instance_variable_set :@previous_transaction, previous_transaction
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
##
|
620
|
+
# @private The Service object.
|
621
|
+
def service
|
622
|
+
ensure_client!
|
623
|
+
|
624
|
+
firestore.service
|
625
|
+
end
|
626
|
+
|
627
|
+
protected
|
628
|
+
|
629
|
+
##
|
630
|
+
# @private The full Database path for the Cloud Firestore transaction.
|
631
|
+
#
|
632
|
+
# @return [String] database resource path.
|
633
|
+
def path
|
634
|
+
@client.path
|
635
|
+
end
|
636
|
+
|
637
|
+
##
|
638
|
+
# @private
|
639
|
+
def coalesce_get_argument obj
|
640
|
+
if obj.is_a?(String) || obj.is_a?(Symbol)
|
641
|
+
if obj.to_s.split("/").count.even?
|
642
|
+
return client.doc obj # Convert a DocumentReference
|
643
|
+
else
|
644
|
+
return client.col obj # Convert to CollectionReference
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
return obj.ref if obj.is_a? DocumentSnapshot
|
649
|
+
|
650
|
+
obj
|
651
|
+
end
|
652
|
+
|
653
|
+
##
|
654
|
+
# @private
|
655
|
+
def coalesce_doc_path_argument doc_path
|
656
|
+
return doc_path.path if doc_path.respond_to? :path
|
657
|
+
|
658
|
+
client.doc(doc_path).path
|
659
|
+
end
|
660
|
+
|
661
|
+
##
|
662
|
+
# @private
|
663
|
+
def transaction_or_create
|
664
|
+
return @transaction_id if @transaction_id
|
665
|
+
|
666
|
+
transaction_opt
|
667
|
+
end
|
668
|
+
|
669
|
+
##
|
670
|
+
# @private
|
671
|
+
def transaction_opt
|
672
|
+
read_write = \
|
673
|
+
Google::Firestore::V1beta1::TransactionOptions::ReadWrite.new
|
674
|
+
|
675
|
+
if @previous_transaction
|
676
|
+
read_write.retry_transaction = @previous_transaction
|
677
|
+
@previous_transaction = nil
|
678
|
+
end
|
679
|
+
|
680
|
+
Google::Firestore::V1beta1::TransactionOptions.new(
|
681
|
+
read_write: read_write
|
682
|
+
)
|
683
|
+
end
|
684
|
+
|
685
|
+
##
|
686
|
+
# @private
|
687
|
+
def extract_transaction_from_result! result
|
688
|
+
return if @transaction_id
|
689
|
+
return if result.transaction.nil?
|
690
|
+
return if result.transaction.empty?
|
691
|
+
|
692
|
+
@transaction_id = result.transaction
|
693
|
+
end
|
694
|
+
|
695
|
+
##
|
696
|
+
# @private
|
697
|
+
def ensure_not_closed!
|
698
|
+
fail "transaction is closed" if closed?
|
699
|
+
end
|
700
|
+
|
701
|
+
##
|
702
|
+
# @private Raise an error unless an database available.
|
703
|
+
def ensure_transaction_id!
|
704
|
+
ensure_service!
|
705
|
+
|
706
|
+
return unless @transaction_id.nil?
|
707
|
+
resp = service.begin_transaction transaction_opt
|
708
|
+
@transaction_id = resp.transaction
|
709
|
+
end
|
710
|
+
|
711
|
+
##
|
712
|
+
# @private Raise an error unless an database available.
|
713
|
+
def ensure_client!
|
714
|
+
fail "Must have active connection to service" unless firestore
|
715
|
+
end
|
716
|
+
|
717
|
+
##
|
718
|
+
# @private Raise an error unless an active connection to the service
|
719
|
+
# is available.
|
720
|
+
def ensure_service!
|
721
|
+
fail "Must have active connection to service" unless service
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|