Aktywatory bazy danych czasu rzeczywistego


Dzięki Cloud Functions możesz obsługiwać zdarzenia w Firebase Realtime Database bez konieczności aktualizowania kodu klienta. Cloud Functions umożliwia wykonywanie operacji Realtime Database z pełnymi uprawnieniami administracyjnymi i zapewnia, że każda zmiana w Realtime Database jest przetwarzana oddzielnie. Możesz wprowadzać Firebase Realtime Database zmiany za pomocą migawki danych lub pakietu Admin SDK.

W typowym cyklu życia funkcja Firebase Realtime Database wykonuje te czynności:

  1. Czeka na zmiany w określonej Realtime Database ścieżce.
  2. Uruchamia się, gdy wystąpi zdarzenie, i wykonuje swoje zadania.
  3. Otrzymuje obiekt danych, który zawiera zrzut danych przechowywanych w tej ścieżce.

Funkcję możesz aktywować w odpowiedzi na zapisywanie, tworzenie, aktualizowanie lub usuwanie węzłów bazy danych w Firebase Realtime Database. Aby określić, kiedy funkcja ma się uruchamiać, podaj jeden z modułów obsługi zdarzeń i ścieżkę Realtime Database, na której będzie nasłuchiwać zdarzeń.

Ustawianie lokalizacji funkcji

Odległość między lokalizacją instancji Realtime Database a lokalizacją funkcji może powodować znaczne opóźnienia sieci. Niezgodność między regionami może też spowodować niepowodzenie wdrożenia. Aby uniknąć takich sytuacji, określ lokalizację funkcji, tak aby była zgodna z lokalizacją instancji bazy danych.

Obsługa zdarzeń Realtime Database

Funkcje umożliwiają obsługę Realtime Databasezdarzeń na 2 poziomach szczegółowości: możesz nasłuchiwać tylko zdarzeń zapisu, tworzenia, aktualizacji lub usuwania albo nasłuchiwać dowolnych zmian w odniesieniu.

Dostępne są te moduły obsługi zdarzeń Realtime Database:

Node.jsPython
  • onValueWritten() Aktywowany, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.
  • onValueCreated() Wywoływane tylko wtedy, gdy dane są tworzone w Realtime Database.
  • onValueUpdated() Aktywowane tylko wtedy, gdy dane są aktualizowane w Realtime Database.
  • onValueDeleted() Wyzwalany tylko wtedy, gdy dane są usuwane w Realtime Database.
  • on_value_written() Aktywowany, gdy dane są tworzone, aktualizowane lub usuwane w Realtime Database.
  • on_value_created() Wywoływane tylko wtedy, gdy dane są tworzone w Realtime Database.
  • on_value_updated() Aktywowane tylko wtedy, gdy dane są aktualizowane w Realtime Database.
  • on_value_deleted() Wyzwalany tylko wtedy, gdy dane są usuwane w Realtime Database.

Importowanie wymaganych modułów

W źródle funkcji musisz zaimportować moduły pakietu SDK, których chcesz używać. W tym przykładzie konieczne jest zaimportowanie modułów HTTP i Realtime Database oraz modułu Firebase Admin SDK do zapisywania w Realtime Database.

Node.jsPython
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

Określanie instancji i ścieżki

Aby określić, kiedy i gdzie funkcja ma się uruchamiać, skonfiguruj ją, podając ścieżkę i opcjonalnie Realtime Database instancję. Jeśli nie określisz instancji, funkcja będzie nasłuchiwać wszystkich instancji Realtime Database w regionie funkcji. Możesz też określić wzorzec instancji Realtime Database, aby wdrożyć go w wybranym podzbiorze instancji w tym samym regionie.

Przykład:

Node.jsPython
// All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
// There must be at least one Realtime Database present in us-central1.
const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

// Instance named "my-app-db-2", at path "/user/{uid}".
// The "my-app-db-2" instance must exist in this region.
const OnWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

// Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
// There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);
# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

Te parametry kierują funkcję do obsługi zapisów w określonej ścieżce w ramach instancji Realtime Database.

Specyfikacje ścieżek pasują do wszystkich zapisów, które dotyczą ścieżki, w tym zapisów, które występują w dowolnym miejscu poniżej niej. Jeśli ścieżka funkcji to /foo/bar, będzie ona pasować do zdarzeń w obu tych lokalizacjach:

 /foo/bar
 /foo/bar/baz/really/deep/path

W obu przypadkach Firebase interpretuje, że zdarzenie występuje w momencie /foo/bar, a dane zdarzenia zawierają stare i nowe dane w momencie /foo/bar. Jeśli dane zdarzenia mogą być duże, rozważ użycie wielu funkcji na głębszych ścieżkach zamiast jednej funkcji w pobliżu katalogu głównego bazy danych. Aby uzyskać najlepszą wydajność, wysyłaj prośby o dane tylko na najniższym możliwym poziomie.

Używanie symboli wieloznacznych i przechwytywanie

Do rejestrowania możesz używać {key}, {key=*}, {key=prefix*}, {key=*suffix}. *, prefix*, *suffix w przypadku symboli wieloznacznych w jednym segmencie. Uwaga: symbol ** oznacza wielosegmentowe symbole wieloznaczne, które nie są obsługiwane przez Realtime Database. Zobacz Omówienie wzorców ścieżek.

Wieloznaczność ścieżki Możesz określić składnik ścieżki jako symbol wieloznaczny:

  • Używanie gwiazdki, * Na przykład foo/* pasuje do wszystkich elementów podrzędnych na poziomie 1 hierarchii węzłów poniżej foo/.
  • Używanie segmentu zawierającego dokładnie gwiazdkę *. Na przykład foo/app*-us pasuje do wszystkich segmentów podrzędnych poniżej foo/ z prefiksem app i sufiksem -us.

Ścieżki z symbolami wieloznacznymi mogą pasować do wielu zdarzeń, np. z jednego zapisu. Wstawka

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

pasuje do ścieżki "/foo/*" 2 razy: raz z "hello": "world" i raz z "firebase": "functions".

Rejestrowanie ścieżki Dopasowania ścieżek możesz przechwytywać do nazwanych zmiennych, które będą używane w kodzie funkcji (np. /user/{uid}, /user/{uid=*-us}).

Wartości zmiennych przechwytywania są dostępne w obiekcie database.DatabaseEvent.params funkcji.

Symbole wieloznaczne w instancjach Możesz też określić komponent instancji za pomocą symboli wieloznacznych. Symbol wieloznaczny instancji może mieć prefiks, sufiks lub oba te elementy (np. my-app-*-prod).

Symbole wieloznaczne i odwołania do przechwyconych wartości

W przypadku Cloud Functions (2 generacji) i Realtime Database wzorca można używać podczas określania refinstance. Każdy interfejs wyzwalacza będzie miał te opcje określania zakresu funkcji:

Określanie ref Określanie instance Zachowanie
Pojedynczy (/foo/bar) Nie określono Ogranicza zakres działania do wszystkich instancji w regionie funkcji.
Pojedynczy (/foo/bar) Pojedynczy (‘my-new-db') Obsługa zakresów w konkretnej instancji w regionie funkcji.
Pojedynczy (/foo/bar) Wzór (‘inst-prefix*') Ogranicza zakres działania funkcji do wszystkich instancji pasujących do wzorca w regionie funkcji.
Wzór (/foo/{bar}) Nie określono Ogranicza zakres działania do wszystkich instancji w regionie funkcji.
Wzór (/foo/{bar}) Pojedynczy (‘my-new-db') Obsługa zakresów w konkretnej instancji w regionie funkcji.
Wzór (/foo/{bar}) Wzór (‘inst-prefix*') Ogranicza zakres działania funkcji do wszystkich instancji pasujących do wzorca w regionie funkcji.

Obsługa danych zdarzenia

Gdy zostanie wywołane zdarzenie Realtime Database, do funkcji obsługi przekazywany jest obiekt Event. Ten obiekt ma właściwość data, która w przypadku zdarzeń tworzenia i usuwania zawiera migawkę utworzonych lub usuniętych danych.

W tym przykładzie funkcja pobiera dane ze ścieżki, do której się odwołuje, przekształca ciąg znaków w tej lokalizacji na wielkie litery i zapisuje zmodyfikowany ciąg znaków w bazie danych:

Node.jsPython
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the
      // Realtime Database returns a Promise.
      return event.data.ref.parent.child("uppercase").set(uppercase);
    },
);
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

Odczytywanie poprzedniej wartości

W przypadku zdarzeń write lub update właściwość data jest obiektem Change, który zawiera 2 migawki przedstawiające stan danych przed zdarzeniem wywołującym i po nim. Obiekt Change ma właściwość before, która umożliwia sprawdzenie, co zostało zapisane w Realtime Database przed zdarzeniem, oraz właściwość after, która reprezentuje stan danych po zdarzeniu.

Na przykład właściwość before może służyć do zapewnienia, że funkcja będzie przekształcać tekst na wielkie litery tylko podczas jego pierwszego tworzenia:

Node.jsPython
  exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => {
        // Only edit data when it is first created.
        if (event.data.before.exists()) {
          return null;
        }
        // Exit when the data is deleted.
        if (!event.data.after.exists()) {
          return null;
        }
        // Grab the current value of what was written to the Realtime Database.
        const original = event.data.after.val();
        console.log('Uppercasing', event.params.pushId, original);
        const uppercase = original.toUpperCase();
        // You must return a Promise when performing asynchronous tasks inside a Functions such as
        // writing to the Firebase Realtime Database.
        // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
        return event.data.after.ref.parent.child('uppercase').set(uppercase);
      });
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)