{book.title}
{book.authors?.join(', ') || 'Unknown Author'}
Published: {book.publication_year}
{/if}| create (default) | Creates a new document. Fails if a document with the same id already exists |
| upsert | Creates a new document or updates an existing document if a document with the same id already exists.
Requires the whole document to be sent. For partial updates, use the update action below. |
| update | Updates an existing document. Fails if a document with the given id does not exist. You can send
a partial document containing only the fields that are to be updated. |
| emplace | Creates a new document or updates an existing document if a document with the same id already exists.
You can send either the whole document or a partial document for update. |
category:= [\`Running Shoes, Men\`, \`Sneaker (Men)\`, Boots].Published: {{ book.publication_year }}
}
Initially when you do `facet_by: artist`, you'll get facet counts for all artist names in the search results.
However, once you apply `filter_by: artist:=The Beatles`, and then facet by `artist`, now the result set will only contain documents where `artist` is `The Beatles`, so the facet counts will only contain this value.
But you'd still want to show counts for the unfiltered / unselected items to the user. For eg, in the image above, you'd still want to fetch counts for `Various Artists`, `Hello`, `Hello Searhorse!`, etc.
To fetch these counts, you'd need to do a second search query where you remove the `filter_by` selected by the user and only send the `facet_by: artist` parameter.
This will now give you facet counts for all the other unfiltered items, and you would use these counts in the filter widget.
You'd want to do this 2nd query inside a [`multi_search`](/30.2/api/federated-multi-search.html) request, so it's still a single http request.
If you have multiple filter widgets, you would have to issue one additional query for each filter widget, that removes the particular filter for that widget, but keeps all other filters.
:::tip Live Example
To see a live example of the types of queries you'd need to generate, go to [songs-search.typesense.org](https://songs-search.typesense.org), open the browser's dev console, and then the network tab.
Type in "Hello" in the search bar, and then click on "The Beatles".
Now look for a request to `multi_search` in the browser's dev console network tab, and look at the structure of the queries generated.
:::
### What is the difference between filtering and faceting?
Let's say you have a [dataset of songs](https://songs-search.typesense.org/) like in the screenshot below.
The **_count_** next to each of the "Release Dates" and "Artists" on the left is obtained by faceting on the `release_date` and `artist` fields.
If you click on say "John Denver" and want to only fetch songs where the artist is "John Denver", that is called filtering.

## Indexing
### Why don't all my records from my database show up in Typesense when I sync my data?
If you're using the [`import`](/30.2/api/documents.html#index-multiple-documents) API endpoint to bulk import documents into Typesense,
this endpoint returns an HTTP 200 in all cases, even if a few documents fail to import (to account for partial successes).
So your HTTP client might not be throwing an error even during an error.
Always be sure to check the API response for any `{success: false, ...}` records to see if there are any documents that failed import, which usually happens due to schema validation errors.
You can use the [`?return_id=true`](/30.2/api/documents.html#returning-the-id-of-the-imported-documents) query parameter to get Typesense to return the exact document ID that errored out.
## Operations
### How do I get the memory usage for a single collection or field?
Typesense does not track memory usage by individual collections or fields. It only tracks memory metrics at an aggregate process-level.
You can access these metrics using the [`GET /metrics.json`](/30.2/api/cluster-operations.html#cluster-metrics) endpoint.
### How do I set up HTTPS with Typesense?
By default, Typesense runs on port `8108` and serves HTTP traffic.
To enable HTTPS, you want to change the [`api-port`](/30.2/api/server-configuration.html#networking) to `443` and then use the [`ssl-certificate` and `ssl-certificate-key`](/30.2/api/server-configuration.html#ssl-https) parameters to point to your SSL certificate and SSL private key respectively.
Providers like [LetsEncrypt](https://letsencrypt.org) offer free SSL certificates.
### I see an HTTP 503, "Not Ready or Lagging". Why?
If you see an HTTP 503 during writes, that is due to Typesense's built-in backpressure mechanism. This section talks about how to [Handle these HTTP 503s](./syncing-data-into-typesense.md#handling-http-503s).
If you see an HTTP 503 after a restart, see [below](#i-restarted-typesense-and-im-now-seeing-an-http-503-why).
### I restarted Typesense, and I'm now seeing an HTTP 503. Why?
Typesense is an in-memory data store. When you restart the Typesense process, we read the data that was previously sent into Typesense (and stored on disk as a backup) and use it to re-build the in-memory indices.
The amount of time this takes depends on the size of your dataset. So until your data is fully re-indexed in RAM, the node will return an HTTP 503.
To avoid this downtime, you want to set up Typesense in a [Highly Available](./high-availability.md) configuration so that even if one node needs to be restarted, the other nodes in the cluster will continue servicing traffic without any downtime.
If you see an HTTP 503 during writes, that is due to Typesense's built-in backpressure mechanism documented under this section about [Handling 503s during writes](./syncing-data-into-typesense.md#handling-http-503s).
### I'm seeing a large amount of pending write batches. How can I speed this up?
This usually happens when there is a high volume of single-document writes being sent into Typesense. You want to switch to using the bulk import API as described in this section about [High Volume Writes](./syncing-data-into-typesense.md#high-volume-writes).
## Releases
### When is the next version of Typesense launching?
We tend to add features and fixes on a continuous iterative cycle and publish RC (Release Candidate) builds every 1-2 weeks. Once the amount of changes have reached a critical mass, we then go into a feature freeze for the upcoming version, address a final round of last-mile issues if any, run performance benchmarks and regression tests, continuously dogfood the builds and if everything looks good we do a final GA (Generally Available) release.
We do not have a fixed timeline for GA releases since we want final builds to be sufficiently tested and stable. That said, in the past we've done [GA releases](https://github.com/typesense/typesense/releases) every 2-3 months.
In the meantime, most RC builds are safe to run in production. Read more here: [Can I run RC builds in production?](#can-i-run-rc-builds-in-production).
### How do you plan your roadmap?
We do Just-In-Time planning, at most 2 months in advance. Even within that window we tend to reprioritize based on your feedback. So items you see on [our roadmap](https://github.com/orgs/typesense/projects/1) more than one version out are subject to change.
While we generally tend to prioritize requests from our [Prioritized Support](https://typesense.org/support/) users, [Typesense Cloud](https://cloud.typesense.org/) users, our [GitHub Sponsors](https://github.com/sponsors/typesense) and our open source Community Contributors, we also prioritize features based on the number of people asking for it, features/fixes that are small enough and can be addressed while we work on other related features, features/fixes that help improve stability & relevance and features that address interesting use cases that excite us!
If you'd like to have a request prioritized, we ask that you add a detailed use-case for it, either as a comment on an existing issue (in addition to a thumbs-up) or in a new issue. The detailed context you share about how you'd use the feature you're asking for helps significantly.
[View Roadmap »](https://github.com/orgs/typesense/projects/1)
### Can I run RC builds in production?
Usually yes. We tend to fix issues in RC builds pretty quickly.
So if a build has been published for at least 1-2 weeks, and there are no newer builds, you should be safe to run that RC build in production.
You can find the published timestamps of the latest RC builds in our [Docker repo tags](https://hub.docker.com/r/typesense/typesense/tags?ordering=last_updated).
### How do I access RC builds?
If you're on Typesense Cloud, you'll find the latest RC build by going to Cluster Configuration > Modify > Typesense Server Version.
RC builds are all the way at the bottom of this version selector dropdown.
If you're self-hosting Typesense, you can replace the version strings in the URLs in the [installation guide](./install-typesense.md) to the RC version for the Docker image, Linux binary, DEB and RPM packages.
We do not publish RC builds for other platforms.
## Firebase
Here's a dedicated FAQ for the Firebase function kit: [https://github.com/typesense/firestore-typesense-search?tab=readme-ov-file#faqs](https://github.com/typesense/firestore-typesense-search?tab=readme-ov-file#faqs)
If you're still using the Firebase Extension, see [Moving from the Firebase Extension](./firebase-full-text-search.md#moving-from-the-firebase-extension).
## Typesense Cloud
### What is the difference between Typesense Cloud and Self-Hosted version?
Here's how Typesense Cloud and Self-Hosted (on any VPS or other cloud) compare:
- **API Parity:** We run the same binaries we publish open source in Typesense Cloud as well, so the core APIs and features are the same.
- **Admin UI:** Typesense Self-Hosted is an API-only product. Whereas in Typesense Cloud we provide a UI to explore the data, setup synonyms, aliases, overrides, merchandizing, etc by pointing and clicking - ideal for non-technical members of your team to manage search behavior themselves, without having to take up engineering time.
- **Managed Infrastructure:** In Typesense Cloud we take care of the infrastructure for you completely. As you scale, we can automatically scale your clusters for you (when you turn this setting on). Setting up a [Highly Available cluster](./high-availability.md), changing [cluster capacity](./system-requirements.md) and changing [Typesense versions](./updating-typesense.md) is point and click (or can be automated [via an API](/cloud-management-api/v1/README.md)) in Typesense Cloud, saving your infrastructure team valuable time.
- **Globally Distributed:** In Typesense Cloud, we offer a [Search Delivery Network](/guide/typesense-cloud/search-delivery-network.md) feature that works like a CDN, but we _replicate_ your full dataset to each of the regions you choose (unlike in a CDN where only frequently used items are kept on the edge), and the node that's closest to a searcher will respond to the search request. This reduces network latency and speeds up the search for your users around the world.
- **Role-Based Access Control:** In Typesense Cloud, we give you Role Based Access control for members of your team, so you don’t have to share API keys with team members to give them selective access to different parts of the UI.
- **Hosted MCP Server:** We offer an official [Hosted MCP Server on Typesense Cloud](./typesense-cloud/mcp-server.md) that you can connect with your AI agents like Claude Code, Codex, Cursor, etc to interact with your clusters using your agents. This MCP Server is deeply integrated with the RBAC permission system in Typesense Cloud, so if you can have different members of your team with varying levels of access, they'll only get access to the subset of capabilities you've defined via their agent, just like in the web UI.
- **Switching Effort:** Since we run the same binaries, you can migrate between self-hosted and Typesense Cloud by just exporting and importing data.
- **Cost:** You'll have to compare the [pricing](https://cloud.typesense.org/pricing/) we publish on Typesense Cloud, with what it would cost to run an equivalent capacity VM(s) on your server hosting provider, plus add the engineering time required to self-host, manage infrastructure and keep up with updates. In general, we find that once you add engineering time, Typesense Cloud becomes cheaper, because we’re able to amortize engineering time on our side across all customers, bringing down costs for all users.
- **Support:** We offer [Prioritized Support](https://typesense.org/support/) on Typesense Cloud, whereas for Self-Hosted support is best-effort or usually [self-service](/help.md).
## Help
### I don't see my question answered here or in the docs. What do I do?
Read our [Help](/help.md) section for information on how to get additional help.
# Full-text Search for Firebase with Typesense
[Firebase](https://firebase.google.com/) is a popular app development platform backed by Google and used by developers across the globe.
It gives you a variety of tools to build, release and monitor your applications. However, there is no native indexing or search solution with Firebase.
The Firebase [documentation](https://firebase.google.com/docs/firestore/solutions/search) talks about using services like Algolia and Elasticsearch and more recently Typesense for full-text search.
Algolia is a good solution, but it is proprietary and can be expensive for even moderate scale.
Elasticsearch is a versatile search solution, but it has a steep learning curve and running Elasticsearch in production requires a non-trivial amount of devops knowledge, even with Elastic Cloud.
**Enter Typesense** - Typesense is an [open-source](https://github.com/typesense/typesense) typo-tolerant fuzzy search engine that is easy to use, run and scale, with clean APIs and documentation.
Think of it as an open source alternative to Algolia and an easier-to-use, batteries-included alternative to ElasticSearch.
Typesense is blazing fast and highly configurable, so you can tailor results according to your needs. You can learn more about Typesense features [here](https://github.com/typesense/typesense#features).
You can either self-host Typesense or use the managed SaaS service [Typesense Cloud](https://cloud.typesense.org).
:::tip UPDATE
Firebase Extensions shut down on March 31, 2027. Because of it, Typesense Firebase Extension is now a function kit.
If you have the extension installed, see [Migrating from the Firebase Extension](#migrating-from-the-firebase-extension).
:::
In this walk-through, we are going to show you how to integrate Typesense with Firebase, to build a full-text search experience for your Firebase app.
We'll assume that you're already familiar with Firebase, Firestore and how these tools work. To get started, let's take a sample app that stores book titles and their publication year.
```javascript
// books/${bookID}
{
id: string,
title: string,
publication_year: int32
}
```
## Step 1: Run Typesense
The easiest way to run Typesense is using [Typesense cloud](https://cloud.typesense.org), which is the hosted version of Typesense:
1. Visit [https://cloud.typesense.org](https://cloud.typesense.org) and Login with GitHub.
2. Select the configuration you need or leave the defaults as is and click on "Launch"
3. Your cluster should be fully provisioned and running in about 5 minutes. Then click on "Generate API Key".
We'll now use the hostname and API keys you generate for the rest of the tutorial.
You can also run Typesense locally or on a server on GCP, if you choose to self-host.
[Here](./install-typesense.md#option-2-local-machine-self-hosting) are instructions on how to
install Typesense on any cloud server.
## Step 2: Create a Typesense Collection
To use Typesense, we first need to create a client. Typesense supports [multiple API clients](../latest/api/api-clients.md) including Javascript, Python, Ruby, PHP etc.
To initialize the Javascript client, you need the API key of the Typesense server you generated in Step 1:
```javascript
import Typesense from 'typesense'
let client = new Typesense.Client({
'nodes': [{
'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster
'port': '443',
'protocol': 'https'
}],
'apiKey': '{hit.title} - {hit.description}
) return (
Requests sent to the Load-Balanced endpoint are distributed between all the nodes in the cluster.
If a particular node has an infrastructure issue, or is inaccessible for any reason, it is automatically quarantined and traffic is re-routed to the other healthy nodes.
By default, a highly available cluster in Typesense Cloud runs 3 nodes, which lets it tolerate a 1-node failure. When you launch or modify a cluster, you can choose to run between 3 and 7 nodes, so your cluster can tolerate more simultaneous node failures (as explained above) and also scale read throughput, as each node holds the entire replicated copy of your dataset and can handle searches independently.
::: warning Note
You will only see the Load Balanced endpoint for HA clusters provisioned after **June 16, 2022**.
For HA clusters provisioned **before June 16, 2022**, you will only see the individual hostnames. Health-checking and traffic re-routing are done client-side in our official client libraries.
See [Client Configuration](#client-configuration) below.
To enable server-side load balancing on your existing clusters, open your Cluster Dashboard, go to Cluster Configuration, select Modify, toggle Load Balancing ON, and schedule the change.
After that, configure your client libraries to use the `nearestNode` parameter as described under [Client Configuration](#client-configuration) below.
:::
## High Availability when Self-Hosting Typesense
### Configuring a Typesense cluster
To start a Typesense node as part of a cluster, create a new file on each node that's part of the cluster with the following format, and use the [`--nodes` server configuration](../latest/api/server-configuration.md) to point to the file.
Each node definition in the file should be in the following format, separated by commas:
`
By: {authors?.join(', ')}
{publication_year &&Published: {publication_year}
}Published: {{ book.publication_year }}
By: {authors && authors.length > 0 ? authors.join(", ") : "Unknown"}
{publication_year && (Published: {publication_year}
)}
Searching...
Try adjusting your search or try different keywords.
{book.authors?.join(', ') || 'Unknown Author'}
Published: {book.publication_year}
{/if}
|| | Logical OR | country:=USA || country:=Canada |
When using logical operators, you can use parentheses to group your filters.
For example, to filter for products that are either made in the USA or Canada, and are located in New York, you can use the following filter:
```shell
(country:=USA || country:=Canada) && city:=New York
```
This will return products that are either made in the USA or Canada, and are located in New York.
### Operator Precedence
In boolean operations, the AND operator (`&&`) and the OR operator (`||`) have the same precedence and are evaluated left to right, unless parentheses are used to specify a different order.
For example:
```shell
country:=USA || country:=Canada && city:=New York
```
Would be interpreted as:
```shell
(country:=USA || country:=Canada) && city:=New York
```
To force a different order of operations, use parentheses explicitly:
```shell
country:=USA || (country:=Canada && city:=New York)
```
## Filtering Array Types
Any of the array types can be used for filtering, matching any of their values.
For example, if there's a field called `department_prices` that's a `int32[]` and there's a document with `department_prices: [32, 60, 80]`,
then the filter of:
```shell
department_prices:<80
```
Would in turn return this document.
Subsequently, you can also use array operators:
```shell
deparment_prices:[20..80]
```
## Escaping special characters
If you want to filter for a field _value_ that includes special characters, such as a `&&`, `||`, `(`, `)`, `,`, `[`, `]`, you can use
backticks around your filter value:
```shell
country:=`United States, Minor Outlying Islands`
country:=[`United States (USA) [Country]`, `Canada (CA) [Country]`]
```
Typesense does not allow special characters in field _names_, only field _values_ can be escaped using backticks.
## Filtering Geopoints
You can filter on `geopoint` fields by searching either within a specific radius, or by specifying a bounding box.
For example, to filter for results within a 10 kilometer radius of a specific location, you can use the following filter:
```shell
location:(48.90615915923891, 2.3435897727061175, 10 km),
```
:::tip
You can use the same filter for miles by changing the distance unit to `mi`.
:::
Or, to filter for results within a bounding box, you can use the following filter:
```shell
location:(48.8662, 2.3255, 48.8581, 2.3209, 48.8561, 2.3448, 48.8641, 2.3469)
```
For information on filtering `geopoint` fields to search within a specific area, please refer to our [documentation](/30.2/api/geosearch.html#searching-within-a-radius) on Geosearch.
## Filtering Nested Object Fields
You can also filter on [nested object fields](/30.2/api/collections.html#indexing-nested-fields) by using the dot (`.`) notation to refer to nested fields.
For example, if you have a `customer` nested object inside your collection schema that looks like this:
```json{3}
{
"customer": {
"name": "John Doe",
"city": "Alpha",
"dob": 964924500
}
}
```
You can filter by the customer's name like:
```
customer.name:=John Doe
```
## Filtering Nested Array Objects
When filtering on fields inside nested array objects, you need to use a special syntax to ensure the filters are applied to the same object within the array. The syntax is: `
Any requests sent to this nearest node hostname will be routed to the node that's closest to the geographic region where the query originates.
### Client Configuration
The official client libraries support specifying a nearest node hostname, and then specifying the individual nodes' hostnames as a fallback.
Requests are first sent to the Nearest Node endpoint, and if it fails for some reason, the request is then retried on the fallback nodes in a round-robin fashion.
```js
/*
* Our JavaScript client library works on both the server and the browser.
* When using the library on the browser, please be sure to use the
* search-only API Key rather than the master API key since the latter
* has write access to Typesense and you don't want to expose that.
*/
const Typesense = require('typesense')
let client = new Typesense.Client({
'nearestNode': { 'host': 'xxx.a1.typesense.net', 'port': 443, 'protocol': 'https' }, // This is the special Nearest Node hostname that you'll see in the Typesense Cloud dashboard if you turn on Search Delivery Network
'nodes': [
{ 'host': 'xxx-1.a1.typesense.net', 'port': 443, 'protocol': 'https' },
{ 'host': 'xxx-2.a1.typesense.net', 'port': 443, 'protocol': 'https' },
{ 'host': 'xxx-3.a1.typesense.net', 'port': 443, 'protocol': 'https' },
],
'apiKey': 'powered by typesense|
Published: ${hit.publication_year}
` : ''}Try adjusting your search or try different keywords.