Files
2026-04-23 23:58:59 -05:00

7.2 KiB

Advanced Features Reference

Contents


Semantic search using Vertex AI embeddings and PostgreSQL's pgvector.

Schema Setup

type Movie @table {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  description: String
  # Vector field for embeddings - size must match model output (768 for gecko)
  descriptionEmbedding: Vector! @col(size: 768)
}

Generate Embeddings in Mutations

Use _embed server value to auto-generate embeddings via Vertex AI:

mutation CreateMovieWithEmbedding($title: String!, $description: String!) 
  @auth(level: USER) {
  movie_insert(data: {
    title: $title,
    description: $description,
    descriptionEmbedding_embed: {
      model: "textembedding-gecko@003",
      text: $description
    }
  })
}

Similarity Search Query

Data Connect generates _similarity fields for Vector columns:

query SearchMovies($query: String!) @auth(level: PUBLIC) {
  movies_descriptionEmbedding_similarity(
    compare_embed: { model: "textembedding-gecko@003", text: $query },
    method: L2,         # L2, COSINE, or INNER_PRODUCT
    within: 2.0,        # Max distance threshold
    limit: 5
  ) {
    id
    title
    description
    _metadata { distance }  # See how close each result is
  }
}

Similarity Parameters

Parameter Description
compare Raw Vector to compare against
compare_embed Generate embedding from text via Vertex AI
method Distance function: L2, COSINE, INNER_PRODUCT
within Max distance (results further are excluded)
where Additional filters
limit Max results to return

Custom Embeddings

Pass pre-computed vectors directly:

mutation StoreCustomEmbedding($id: UUID!, $embedding: Vector!) @auth(level: USER) {
  movie_update(id: $id, data: { descriptionEmbedding: $embedding })
}

query SearchWithCustomVector($vector: Vector!) @auth(level: PUBLIC) {
  movies_descriptionEmbedding_similarity(
    compare: $vector,
    method: COSINE,
    limit: 10
  ) { id title }
}

Fast keyword/phrase search using PostgreSQL's full-text capabilities.

Enable with @searchable

type Movie @table {
  title: String! @searchable
  description: String @searchable(language: "english")
  genre: String @searchable
}

Search Query

Data Connect generates _search fields:

query SearchMovies($query: String!) @auth(level: PUBLIC) {
  movies_search(
    query: $query,
    queryFormat: QUERY,  # QUERY, PLAIN, PHRASE, or ADVANCED
    limit: 20
  ) {
    id title description
    _metadata { relevance }  # Relevance score
  }
}

Query Formats

Format Description
QUERY Web-style (default): quotes, AND, OR supported
PLAIN Match all words, any order
PHRASE Match exact phrase
ADVANCED Full tsquery syntax

Tuning Results

query SearchWithThreshold($query: String!) @auth(level: PUBLIC) {
  movies_search(
    query: $query,
    relevanceThreshold: 0.05,  # Min relevance score
    where: { genre: { eq: "Action" }},
    orderBy: [{ releaseYear: DESC }]
  ) { id title }
}

Supported Languages

english (default), french, german, spanish, italian, portuguese, dutch, danish, finnish, norwegian, swedish, russian, arabic, hindi, simple


Cloud Functions Integration

Trigger Cloud Functions when mutations execute.

Basic Trigger (Node.js)

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

export const onUserCreate = onMutationExecuted(
  {
    service: "myService",
    connector: "default",
    operation: "CreateUser",
    region: "us-central1"  // Must match Data Connect location
  },
  (event) => {
    const variables = event.data.payload.variables;
    const returnedData = event.data.payload.data;
    
    logger.info("User created:", returnedData);
    // Send welcome email, sync to analytics, etc.
  }
);

Basic Trigger (Python)

from firebase_functions import dataconnect_fn, logger

@dataconnect_fn.on_mutation_executed(
  service="myService",
  connector="default",
  operation="CreateUser"
)
def on_user_create(event: dataconnect_fn.Event):
  variables = event.data.payload.variables
  returned_data = event.data.payload.data
  logger.info("User created:", returned_data)

Event Data

// event.authType: "app_user" | "unauthenticated" | "admin"
// event.authId: Firebase Auth UID (for app_user)
// event.data.payload.variables: mutation input variables
// event.data.payload.data: mutation response data
// event.data.payload.errors: any errors that occurred

Filtering with Wildcards

// Trigger on all User* mutations
export const onUserMutation = onMutationExecuted(
  { operation: "User*" },
  (event) => { /* ... */ }
);

// Capture operation name
export const onAnyMutation = onMutationExecuted(
  { service: "myService", operation: "{operationName}" },
  (event) => {
    console.log("Operation:", event.params.operationName);
  }
);

Use Cases

  • Data sync: Replicate to Firestore, BigQuery, external APIs
  • Notifications: Send emails, push notifications on events
  • Async workflows: Image processing, data aggregation
  • Audit logging: Track all data changes

⚠️ Avoid infinite loops: Don't trigger mutations that would fire the same trigger. Use filters to exclude self-triggered events.


Data Seeding & Bulk Operations

Local Prototyping with _insertMany

mutation SeedMovies @transaction {
  movie_insertMany(data: [
    { id: "uuid-1", title: "Movie 1", genre: "Action" },
    { id: "uuid-2", title: "Movie 2", genre: "Drama" },
    { id: "uuid-3", title: "Movie 3", genre: "Comedy" }
  ])
}

Reset Data with _upsertMany

mutation ResetData {
  movie_upsertMany(data: [
    { id: "uuid-1", title: "Movie 1", genre: "Action" },
    { id: "uuid-2", title: "Movie 2", genre: "Drama" }
  ])
}

Clear All Data

mutation ClearMovies {
  movie_deleteMany(all: true)
}

Production: Admin SDK Bulk Operations

import { initializeApp } from 'firebase-admin/app';
import { getDataConnect } from 'firebase-admin/data-connect';

const app = initializeApp();
const dc = getDataConnect({ location: "us-central1", serviceId: "my-service" });

const movies = [
  { id: "uuid-1", title: "Movie 1", genre: "Action" },
  { id: "uuid-2", title: "Movie 2", genre: "Drama" }
];

// Bulk insert
await dc.insertMany("movie", movies);

// Bulk upsert
await dc.upsertMany("movie", movies);

// Single operations
await dc.insert("movie", movies[0]);
await dc.upsert("movie", movies[0]);

Emulator Data Persistence

# Export emulator data
npx -y firebase-tools@latest emulators:export ./seed-data

# Start with saved data
npx -y firebase-tools@latest emulators:start --only dataconnect --import=./seed-data