Firestore에서 값이 변경될 때마다 값을 업데이트합니다.
더 살펴보기
이 코드 샘플이 포함된 자세한 문서는 다음을 참조하세요.
코드 샘플
C#
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
using CloudNative.CloudEvents;
using Google.Cloud.Firestore;
using Google.Cloud.Functions.Framework;
using Google.Cloud.Functions.Hosting;
using Google.Events.Protobuf.Cloud.Firestore.V1;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace FirestoreReactive;
public class Startup : FunctionsStartup
{
public override void ConfigureServices(WebHostBuilderContext context, IServiceCollection services) =>
services.AddSingleton(FirestoreDb.Create());
}
// Register the startup class to provide the Firestore dependency.
[FunctionsStartup(typeof(Startup))]
public class Function : ICloudEventFunction<DocumentEventData>
{
private readonly ILogger _logger;
private readonly FirestoreDb _firestoreDb;
public Function(ILogger<Function> logger, FirestoreDb firestoreDb) =>
(_logger, _firestoreDb) = (logger, firestoreDb);
public async Task HandleAsync(CloudEvent cloudEvent, DocumentEventData data, CancellationToken cancellationToken)
{
// Get the recently-written value. This expression will result in a null value
// if any of the following is true:
// - The event doesn't contain a "new" document
// - The value doesn't contain a field called "original"
// - The "original" field isn't a string
string currentValue = data.Value?.ConvertFields().GetValueOrDefault("original") as string;
if (currentValue is null)
{
_logger.LogWarning($"Event did not contain a suitable document");
return;
}
string newValue = currentValue.ToUpperInvariant();
if (newValue == currentValue)
{
_logger.LogInformation("Value is already upper-cased; no replacement necessary");
return;
}
// The CloudEvent subject is "documents/x/y/...".
// The Firestore SDK FirestoreDb.Document method expects a reference relative to
// "documents" (so just the "x/y/..." part). This may be simplified over time.
if (cloudEvent.Subject is null || !cloudEvent.Subject.StartsWith("documents/"))
{
_logger.LogWarning("CloudEvent subject is not a document reference.");
return;
}
string documentPath = cloudEvent.Subject.Substring("documents/".Length);
_logger.LogInformation("Replacing '{current}' with '{new}' in '{path}'", currentValue, newValue, documentPath);
await _firestoreDb.Document(documentPath).UpdateAsync("original", newValue);
}
}
Go
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
// Package upper contains a Firestore Cloud Function.
package upper
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"cloud.google.com/go/firestore"
firebase "firebase.google.com/go/v4"
)
// FirestoreEvent is the payload of a Firestore event.
type FirestoreEvent struct {
OldValue FirestoreValue `json:"oldValue"`
Value FirestoreValue `json:"value"`
UpdateMask struct {
FieldPaths []string `json:"fieldPaths"`
} `json:"updateMask"`
}
// FirestoreValue holds Firestore fields.
type FirestoreValue struct {
CreateTime time.Time `json:"createTime"`
// Fields is the data for this value. The type depends on the format of your
// database. Log an interface{} value and inspect the result to see a JSON
// representation of your database fields.
Fields MyData `json:"fields"`
Name string `json:"name"`
UpdateTime time.Time `json:"updateTime"`
}
// MyData represents a value from Firestore. The type definition depends on the
// format of your database.
type MyData struct {
Original struct {
StringValue string `json:"stringValue"`
} `json:"original"`
}
// GOOGLE_CLOUD_PROJECT is automatically set by the Cloud Functions runtime.
var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
// client is a Firestore client, reused between function invocations.
var client *firestore.Client
func init() {
// Use the application default credentials.
conf := &firebase.Config{ProjectID: projectID}
// Use context.Background() because the app/client should persist across
// invocations.
ctx := context.Background()
app, err := firebase.NewApp(ctx, conf)
if err != nil {
log.Fatalf("firebase.NewApp: %v", err)
}
client, err = app.Firestore(ctx)
if err != nil {
log.Fatalf("app.Firestore: %v", err)
}
}
// MakeUpperCase is triggered by a change to a Firestore document. It updates
// the `original` value of the document to upper case.
func MakeUpperCase(ctx context.Context, e FirestoreEvent) error {
fullPath := strings.Split(e.Value.Name, "/documents/")[1]
pathParts := strings.Split(fullPath, "/")
collection := pathParts[0]
doc := strings.Join(pathParts[1:], "/")
curValue := e.Value.Fields.Original.StringValue
newValue := strings.ToUpper(curValue)
if curValue == newValue {
log.Printf("%q is already upper case: skipping", curValue)
return nil
}
log.Printf("Replacing value: %q -> %q", curValue, newValue)
data := map[string]string{"original": newValue}
_, err := client.Collection(collection).Doc(doc).Set(ctx, data)
if err != nil {
return fmt.Errorf("Set: %w", err)
}
return nil
}
Java
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.firestore.SetOptions;
import com.google.cloud.functions.Context;
import com.google.cloud.functions.RawBackgroundFunction;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FirebaseFirestoreReactive implements RawBackgroundFunction {
// Use GSON (https://github.com/google/gson) to parse JSON content.
private static final Gson gson = new Gson();
private static final Logger logger = Logger.getLogger(FirebaseFirestoreReactive.class.getName());
private static final Firestore FIRESTORE = FirestoreOptions.getDefaultInstance().getService();
private final Firestore firestore;
public FirebaseFirestoreReactive() {
this(FIRESTORE);
}
FirebaseFirestoreReactive(Firestore firestore) {
this.firestore = firestore;
}
@Override
public void accept(String json, Context context) {
// Get the recently-written value
JsonObject body = gson.fromJson(json, JsonObject.class);
JsonObject tempJson = body.getAsJsonObject("value");
// Verify that value.fields.original.stringValue exists
String currentValue = null;
if (tempJson != null) {
tempJson = tempJson.getAsJsonObject("fields");
}
if (tempJson != null) {
tempJson = tempJson.getAsJsonObject("original");
}
if (tempJson != null && tempJson.has("stringValue")) {
currentValue = tempJson.get("stringValue").getAsString();
}
if (currentValue == null) {
throw new IllegalArgumentException("Malformed JSON: " + json);
}
// Convert recently-written value to ALL CAPS
String newValue = currentValue.toUpperCase(Locale.getDefault());
// Update Firestore DB with ALL CAPS value
Map<String, String> newFields = Map.of("original", newValue);
String affectedDoc = context.resource().split("/documents/")[1].replace("\"", "");
if (!currentValue.equals(newValue)) {
// The stored value needs to be updated
// Write the upper-cased value to Firestore
logger.info(String.format("Replacing value: %s --> %s", currentValue, newValue));
try {
FIRESTORE.document(affectedDoc).set(newFields, SetOptions.merge()).get();
} catch (ExecutionException | InterruptedException e) {
logger.log(Level.SEVERE, "Error updating Firestore document: " + e.getMessage(), e);
}
} else {
// The stored value is already upper-case, and doesn't need updating.
// (Don't perform a "second" write, since that could trigger an infinite loop.)
logger.info(String.format("Value is already upper-case."));
}
}
}
Node.js
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
const Firestore = require('@google-cloud/firestore');
const firestore = new Firestore({
projectId: process.env.GOOGLE_CLOUD_PROJECT,
});
// Converts strings added to /messages/{pushId}/original to uppercase
exports.makeUpperCase = event => {
const resource = event.value.name;
const affectedDoc = firestore.doc(resource.split('/documents/')[1]);
const curValue = event.value.fields.original.stringValue;
const newValue = curValue.toUpperCase();
if (curValue !== newValue) {
console.log(`Replacing value: ${curValue} --> ${newValue}`);
return affectedDoc.set({
original: newValue,
});
} else {
// Value is already upper-case
// Don't perform a(nother) write to avoid infinite loops
console.log('Value is already upper-case.');
}
};
PHP
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
use Google\Cloud\Firestore\FirestoreClient;
use Google\CloudFunctions\CloudEvent;
function firebaseReactive(CloudEvent $cloudevent)
{
$log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');
$data = $cloudevent->getData();
$resource = $data['value']['name'];
$db = new FirestoreClient();
$docPath = explode('/documents/', $resource)[1];
$affectedDoc = $db->document($docPath);
$curValue = $data['value']['fields']['original']['stringValue'];
$newValue = strtoupper($curValue);
if ($curValue !== $newValue) {
fwrite($log, 'Replacing value: ' . $curValue . ' --> ' . $newValue . PHP_EOL);
$affectedDoc->set(['original' => $newValue]);
} else {
// Value is already upper-case
// Don't perform another write (it might cause an infinite loop)
fwrite($log, 'Value is already upper-case.' . PHP_EOL);
}
}
Python
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
from google.cloud import firestore
client = firestore.Client()
# Converts strings added to /messages/{pushId}/original to uppercase
def make_upper_case(data, context):
path_parts = context.resource.split("/documents/")[1].split("/")
collection_path = path_parts[0]
document_path = "/".join(path_parts[1:])
affected_doc = client.collection(collection_path).document(document_path)
cur_value = data["value"]["fields"]["original"]["stringValue"]
new_value = cur_value.upper()
if cur_value != new_value:
print(f"Replacing value: {cur_value} --> {new_value}")
affected_doc.set({"original": new_value})
else:
# Value is already upper-case
# Don't perform a second write (which can trigger an infinite loop)
print("Value is already upper-case.")
Ruby
Cloud Functions에 인증하려면 애플리케이션 기본 사용자 인증 정보를 설정합니다. 자세한 내용은 로컬 개발 환경의 인증 설정을 참조하세요.
require "functions_framework"
FunctionsFramework.on_startup do
# Lazily construct a Firestore client when needed, and reuse it on
# subsequent calls.
set_global :firestore_client do
require "google/cloud/firestore"
Google::Cloud::Firestore.new project_id: ENV["GOOGLE_CLOUD_PROJECT"]
end
end
# Converts strings added to /messages/{pushId}/original to uppercase
FunctionsFramework.cloud_event "make_upper_case" do |event|
# Event-triggered Ruby functions receive a CloudEvents::Event::V1 object.
# See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
# The Firebase event payload can be obtained from the event data.
cur_value = event.data["value"]["fields"]["original"]["stringValue"]
# Compute new value and determine whether it needs to be modified.
# If the value is already upper-case, don't perform another write,
# to avoid infinite loops.
new_value = cur_value.upcase
if cur_value == new_value
logger.info "Value is already upper-case"
return
end
# Use the Firestore client library to update the value.
# The document name can be obtained from the event subject.
logger.info "Replacing value: #{cur_value} --> #{new_value}"
doc_name = event.subject.split("documents/").last
affected_doc = global(:firestore_client).doc doc_name
new_doc_data = { original: new_value }
affected_doc.set new_doc_data, merge: false
end
다음 단계
다른 Google Cloud 제품의 코드 샘플을 검색하고 필터링하려면 Google Cloud 샘플 브라우저를 참조하세요.