Hi there! I'm Shrijith Venkatrama, founder of Hexmos. Right now, I’m building LiveAPI, a first of its kind tool for helping you automatically index API endpoints across all your repositories. LiveAPI helps you discover, understand and use APIs in large tech infrastructures with ease.
Meilisearch is a fast, open-source search engine that makes adding search to your app a breeze. One of its killer features is filtering, which lets you narrow down search results based on specific criteria. Whether you're building an e-commerce site or a blog, filters are key to delivering relevant results. This guide dives deep into Meilisearch filters, with practical examples and tips to get you up and running.
We'll cover how filters work, syntax, common use cases, and some gotchas to avoid. By the end, you'll be confident in using filters to make your search experience sharp and user-friendly. Let's get started.
Why Filters Matter in Meilisearch
Filters in Meilisearch let you refine search results by applying conditions to your data. Think of it like a SQL WHERE
clause for search. Want to show only products under $50 or blog posts from 2024? Filters make it happen. They’re essential for precision and improve user experience by cutting through noise.
Meilisearch supports filtering on attributes you mark as filterable
in your index settings. This flexibility makes it powerful but requires setup. Let’s explore how to configure and use filters effectively.
Setting Up Filterable Attributes
Before you can filter, you need to tell Meilisearch which attributes to allow filtering on. This is done by setting filterable attributes on your index. If you skip this, Meilisearch will ignore your filter requests.
Here’s how to set it up using the Meilisearch JavaScript client. This example assumes you have a movies
index with documents containing genre
, release_year
, and rating
.
// Connect to Meilisearch
const { MeiliSearch } = require('meilisearch')
const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'your-api-key' })
// Set filterable attributes
async function setFilterableAttributes() {
await client.index('movies').updateFilterableAttributes([
'genre',
'release_year',
'rating'
])
}
setFilterableAttributes()
// Output: Updates the index settings, enabling filtering on genre, release_year, and rating
Run this code after creating your index but before adding documents. You can update filterable attributes later, but Meilisearch needs to reindex, which can take time for large datasets. Plan your filterable attributes early to avoid delays.
For more on index settings, check the Meilisearch documentation.
Basic Filter Syntax
Meilisearch filter syntax is straightforward but has specific rules. Filters are passed as strings or arrays of strings in the filter
parameter of a search query. Each filter is a condition like attribute operator value
.
Supported operators include:
-
=
,!=
for equality/inequality -
>
,>=
,<
,<=
for comparisons -
EXISTS
to check if an attribute exists
Here’s a simple example filtering movies by genre:
async function searchByGenre() {
const response = await client.index('movies').search('action', {
filter: 'genre = action'
})
console.log(response.hits)
// Output: Returns movies where genre is "action"
}
searchByGenre()
Key rule: The attribute must be set as filterable, and the value must match the attribute’s data type (e.g., numbers for release_year
, strings for genre
). Whitespace around operators is required.
Combining Filters with AND, OR
Real-world searches often need multiple conditions. Meilisearch supports logical operators (AND
, OR
) to combine filters. Use arrays to group conditions for clarity.
Here’s an example filtering movies that are either action or sci-fi and released after 2010:
async function searchWithMultipleFilters() {
const response = await client.index('movies').search('', {
filter: ['genre = action OR genre = sci-fi', 'release_year > 2010']
})
console.log(response.hits)
// Output: Returns action or sci-fi movies released after 2010
}
searchWithMultipleFilters()
Tip: Each array element is implicitly joined with AND
. Within a string, use OR
to combine conditions. This structure keeps complex filters readable. Avoid overly nested filters, as they can slow down searches.
Filtering with Numeric and Boolean Values
Numeric and boolean filters are common in e-commerce or analytics apps. For example, filtering products by price or availability. Meilisearch handles these cleanly, but you need to ensure your data types align.
Here’s an example filtering movies with a rating above 7.5 and available on a platform (is_available = true
):
async function searchHighRatedAvailable() {
const response = await client.index('movies').search('', {
filter: ['rating > 7.5', 'is_available = true']
})
console.log(response.hits)
// Output: Returns movies with rating > 7.5 and is_available = true
}
searchHighRatedAvailable()
Gotcha: For numeric filters, don’t quote the value (e.g., rating > 7.5
, not rating > "7.5"
). For booleans, use true
or false
without quotes. Mismatching types will return no results.
Using Filter Facets for Dynamic Filtering
Facets are a powerful way to let users dynamically filter results, like selecting categories in an online store. Meilisearch’s facets
parameter counts occurrences of values in filterable attributes, which you can use to build filter UIs.
Here’s how to get facet counts for genres and filter by a selected genre:
async function searchWithFacets() {
const response = await client.index('movies').search('', {
facets: ['genre'],
filter: 'genre = drama'
})
console.log(response.facetDistribution)
// Output: { genre: { action: 50, drama: 30, sci-fi: 20, ... } }
}
searchWithFacets()
This returns the number of documents per genre, even after applying the drama filter. Use this data to show users how many results they’ll get if they switch filters. Facets are ideal for interactive search UIs but require filterable attributes to be set.
Learn more about facets in the Meilisearch facets guide.
Common Filter Pitfalls and How to Avoid Them
Filters are powerful but can trip you up. Here’s a table of common issues and fixes:
Issue | Cause | Fix |
---|---|---|
No results returned | Attribute not filterable | Set filterable attributes in index settings |
Filter ignored | Syntax error (e.g., missing spaces) | Use correct syntax: attribute = value
|
Slow performance | Too many filter conditions | Limit filter complexity; optimize index |
Type mismatch | Wrong data type in filter | Match filter value to attribute type (e.g., numbers unquoted) |
Pro tip: Test filters with small datasets first. Use Meilisearch’s error messages to debug syntax issues. If performance lags, consider reducing filterable attributes or using pagination.
Practical Example: Building a Filtered Search UI
Let’s tie it together with a real-world example: a movie search app with filters for genre and release year. Below is a Node.js Express app serving a simple HTML page with a search form and Meilisearch integration.
const express = require('express')
const { MeiliSearch } = require('meilisearch')
const app = express()
const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'your-api-key' })
app.use(express.urlencoded({ extended: true }))
app.get('/', (req, res) => {
res.send(`
<form action="/search" method="POST">
<input type="text" name="query" placeholder="Search movies...">
<select name="genre">
<option value="">All Genres</option>
<option value="action">Action</option>
<option value="drama">Drama</option>
<option value="sci-fi">Sci-fi</option>
</select>
<input type="number" name="year" placeholder="Release Year">
<button type="submit">Search</button>
</form>
`)
})
app.post('/search', async (req, res) => {
const { query, genre, year } = req.body
const filters = []
if (genre) filters.push(`genre = ${genre}`)
if (year) filters.push(`release_year = ${year}`)
const response = await client.index('movies').search(query || '', {
filter: filters.length ? filters : undefined
})
res.send(`
<h2>Results</h2>
<ul>${response.hits.map(hit => `<li>${hit.title} (${hit.release_year})</li>`).join('')}</ul>
<a href="/">Back</a>
`)
})
app.listen(3000, () => console.log('Server running on http://localhost:3000'))
// Output: Starts a server with a search form; displays filtered movie results
How it works: The form collects user input for query, genre, and year. The server builds a filter array dynamically and passes it to Meilisearch. Results are rendered as a list. This is a minimal example—add CSS and error handling for production.
Next Steps for Mastering Meilisearch Filters
Filters are a game-changer for tailoring search results, but they’re just one part of Meilisearch’s toolkit. To level up, experiment with faceting for dynamic UIs, optimize your index for performance, and explore other features like sorting or typo tolerance. Start small, test your filters, and scale as needed.
If you’re building a real app, consider:
- Adding pagination for large result sets
- Using facets to show filter options
- Monitoring performance with Meilisearch’s logs
- Integrating with frameworks like React or Vue for smoother UIs
Dive into the Meilisearch docs for more advanced features. With filters mastered, you’re well on your way to building a killer search experience.
Top comments (2)
Great walkthrough of Meilisearch filters! It might help beginners even more if there were a few visual diagrams or code comments explaining how filters combine, especially for more complex
AND
/OR
logic. Otherwise, very helpful and clear examples!Really appreciate the hands-on approach and sample UI code, practical guides like this always save me so much time when diving into new tools. Curious if you’ve run into any performance bottlenecks with more complex filters in bigger datasets?