Skip to content
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ scripts/agent-evals/output
scripts/agent-evals/node_modules
scripts/agent-evals/lib
scripts/agent-evals/templates
julesbot
julesbot
skills
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/scripts/agent-evals/output/**
/src/frameworks/docs/**
/prompts
/skills

# Intentionally invalid YAML file:
/src/test/fixtures/extension-yamls/invalid/extension.yaml
29 changes: 29 additions & 0 deletions skills/firestore-basics/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: firestore-basics
description: Comprehensive guide for Firestore basics including provisioning, security rules, and SDK usage. Use this skill when the user needs help setting up Firestore, writing security rules, or using the Firestore SDK in their application.
compatibility: This skill is best used with the Firebase CLI, but does not require it. Install it by running `npm install -g firebase-tools`.
---

# Firestore Basics

This skill provides a complete guide for getting started with Cloud Firestore, including provisioning, securing, and integrating it into your application.

## Provisioning

To set up Cloud Firestore in your Firebase project and local environment, see [provisioning.md](references/provisioning.md).

## Security Rules

For guidance on writing and deploying Firestore Security Rules to protect your data, see [security_rules.md](references/security_rules.md).

## SDK Usage

To learn how to use Cloud Firestore in your application code, choose your platform:

* **Web (Modular SDK)**: [web_sdk_usage.md](references/web_sdk_usage.md)
* **Android (Kotlin)**: [android_sdk_usage.md](references/android_sdk_usage.md)
* **iOS (Swift)**: [ios_sdk_usage.md](references/ios_sdk_usage.md)

## Indexes

For checking index types, query support tables, and best practices, see [indexes.md](references/indexes.md).
157 changes: 157 additions & 0 deletions skills/firestore-basics/references/android_sdk_usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Firestore Android SDK Usage Guide

This guide uses **Kotlin** and **KTX extensions**, which correspond to the modern Android development standards.

## Initialization

```kotlin
// In your Activity or Application class
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.firestore.SetOptions
import com.google.firebase.ktx.Firebase

val db = Firebase.firestore

// Connect to Emulator
// Use 10.0.2.2 to access localhost from the Android Emulator
if (BuildConfig.DEBUG) {
db.useEmulator("10.0.2.2", 8080)
}
```

## Writing Data

### Set a Document (`set`)
Creates or overwrites a document.

```kotlin
val city = hashMapOf(
"name" to "Los Angeles",
"state" to "CA",
"country" to "USA"
)

db.collection("cities").document("LA")
.set(city)
.addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully written!") }
.addOnFailureListener { e -> Log.w(TAG, "Error writing document", e) }

// Merge
db.collection("cities").document("LA")
.set(mapOf("population" to 3900000), SetOptions.merge())
```

### Add a Document with Auto-ID (`add`)

```kotlin
val data = hashMapOf(
"name" to "Tokyo",
"country" to "Japan"
)

db.collection("cities")
.add(data)
.addOnSuccessListener { documentReference ->
Log.d(TAG, "DocumentSnapshot written with ID: ${documentReference.id}")
}
```

### Update a Document (`update`)

```kotlin
val laRef = db.collection("cities").document("LA")

laRef.update("capital", true)
.addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully updated!") }
```

### Transactions
Atomic read-modify-write.

```kotlin
db.runTransaction { transaction ->
val sfDocRef = db.collection("cities").document("SF")
val snapshot = transaction.get(sfDocRef)

// Note: You can also use FieldValue.increment() for simple counters
val newPopulation = (snapshot.getDouble("population") ?: 0.0) + 1
transaction.update(sfDocRef, "population", newPopulation)

// Success
null
}.addOnSuccessListener { Log.d(TAG, "Transaction success!") }
.addOnFailureListener { e -> Log.w(TAG, "Transaction failure.", e) }
```

## Reading Data

### Get a Single Document (`get`)

```kotlin
val docRef = db.collection("cities").document("SF")

docRef.get().addOnSuccessListener { document ->
if (document != null && document.exists()) {
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
} else {
Log.d(TAG, "No such document")
}
}
```

### Get Multiple Documents (`get`)

```kotlin
db.collection("cities")
.get()
.addOnSuccessListener { result ->
for (document in result) {
Log.d(TAG, "${document.id} => ${document.data}")
}
}
```

## Realtime Updates

### Listen to Changes (`addSnapshotListener`)

```kotlin
val docRef = db.collection("cities").document("SF")

docRef.addSnapshotListener { snapshot, e ->
if (e != null) {
Log.w(TAG, "Listen failed.", e)
return@addSnapshotListener
}

if (snapshot != null && snapshot.exists()) {
val source = if (snapshot.metadata.hasPendingWrites()) "Local" else "Server"
Log.d(TAG, "$source data: ${snapshot.data}")
} else {
Log.d(TAG, "Current data: null")
}
}
```

## Queries

### Simple and Compound
Note: Compound queries on different fields require an index.

```kotlin
// Simple
db.collection("cities").whereEqualTo("state", "CA")

// Compound (AND)
db.collection("cities")
.whereEqualTo("state", "CA")
.whereGreaterThan("population", 1000000)
```

### Order and Limit

```kotlin
db.collection("cities")
.orderBy("name", Query.Direction.ASCENDING)
.limit(3)
```
82 changes: 82 additions & 0 deletions skills/firestore-basics/references/indexes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Firestore Indexes Reference

Indexes allow Firestore to ensure that query performance depends on the size of the result set, not the size of the database.

## Index Types

### Single-Field Indexes
Firestore **automatically creates** a single-field index for every field in a document (and subfields in maps).
* **Support**: Simple equality queries (`==`) and single-field range/sort queries (`<`, `<=`, `orderBy`).
* **Behavior**: You generally don't need to manage these unless you want to *exempt* a field.

### Composite Indexes
A composite index stores a sorted mapping of all documents based on an ordered list of fields.
* **Support**: Complex queries that filter or sort by **multiple fields**.
* **Creation**: These are **NOT** automatically created. You must define them manually or via the console/CLI.

## Automatic vs. Manual Management

### What is Automatic?
* Indexes for simple queries.
* Merging of single-field indexes for multiple equality filters (e.g., `where("state", "==", "CA").where("country", "==", "USA")`).

### When Do I Need to Act?
If you attempt a query that requires a composite index, the SDK will throw an error containing a **direct link** to the Firebase Console to create that specific index.

**Example Error:**
> "The query requires an index. You can create it here: https://console.firebase.google.com/project/..."

## Query Support Examples

| Query Type | Index Required |
| :--- | :--- |
| **Simple Equality**<br>`where("a", "==", 1)` | Automatic (Single-Field) |
| **Simple Range/Sort**<br>`where("a", ">", 1).orderBy("a")` | Automatic (Single-Field) |
| **Multiple Equality**<br>`where("a", "==", 1).where("b", "==", 2)` | Automatic (Merged Single-Field) |
| **Equality + Range/Sort**<br>`where("a", "==", 1).where("b", ">", 2)` | **Composite Index** |
| **Multiple Ranges**<br>`where("a", ">", 1).where("b", ">", 2)` | **Composite Index** (and technically limited query support) |
| **Array Contains + Equality**<br>`where("tags", "array-contains", "news").where("active", "==", true)` | **Composite Index** |

## Best Practices & Exemptions

You can **exempt** fields from automatic indexing to save storage or strictly enforce write limits.

### 1. High Write Rates (Sequential Values)
* **Problem**: Indexing fields that increase sequentially (like `timestamp`) limits the write rate to ~500 writes/second per collection.
* **Solution**: If you don't query on this field, **exempt** it from simple indexing.

### 2. Large String/Map/Array Fields
* **Problem**: Indexing limits (40k entries per doc). Indexing large blobs wastes storage.
* **Solution**: Exempt large text blobs or huge arrays if they aren't used for filtering.

### 3. TTL Fields
* **Problem**: TTL (Time-To-Live) deletion can cause index churn.
* **Solution**: Exempt the TTL timestamp field from indexing if you don't query it.

## Management

### Config files
Your indexes should be defined in `firestore.indexes.json` (pointed to by `firebase.json`).

```json
{
"indexes": [
{
"collectionGroup": "cities",
"queryScope": "COLLECTION",
"fields": [
{ "fieldPath": "country", "order": "ASCENDING" },
{ "fieldPath": "population", "order": "DESCENDING" }
]
}
],
"fieldOverrides": []
}
```

### CLI Commands

Deploy indexes only:
```bash
firebase deploy --only firestore:indexes
```
Loading
Loading