Meilisearch

๐Ÿš€ Initialization

import 'package:meilisearch/meilisearch.dart';
// Create client instancefinal client = MeiliSearchClient('https://your-url.com', 'your_api_key');
// Target a specific indexfinal index = client.index('movies');

๐Ÿ“‘ Document Operations## Add / Update Documents

// Adds new or overwrites existing documents (by matching 'id')await index.addDocuments([
  {'id': '1', 'title': 'Avatar', 'year': 2009},
  {'id': '2', 'title': 'Gladiator', 'year': 2000},
]);
// Partially updates fields of existing documentsawait index.updateDocuments([
  {'id': '1', 'year': 2010}, 
]);

Fetch / Delete Documents

OperationCode
Get one document by IDvar doc = await index.getDocument('1');
Get a paginated list of documentsvar docs = await index.getDocuments(params: GetDocumentsQuery(limit: 20, offset: 0));
Delete single documentawait index.deleteDocument('1');
Delete multiple documentsawait index.deleteDocuments(['1', '2']);
Delete all documents in indexawait index.deleteAllDocuments();

var result = await index.search('avatar');
for (var hit in result.hits ?? []) {
  print(hit['title']);
}

Advanced Search Options

var result = await index.search(
  'gladiator',
  SearchQuery(
    limit: 10,
    offset: 0,
    // Filter results (requires setting up filterableAttributes first)
    filter: 'year > 2000 AND genre = "Action"',
    // Sort results (requires setting up sortableAttributes first)
    sort: ['year:desc'],
    // Highlight matching text patterns
    attributesToHighlight: ['title'],
  ),
);

โš™๏ธ Index Settings

  • Note: Change these settings on your administrative backend or during app setup, not on every search query.
// Enable fields for filtering and sortingawait index.updateSettings(IndexSettings(
  filterableAttributes: ['year', 'genre'],
  sortableAttributes: ['year'],
  rankingRules: [
    'words',
    'typo',
    'proximity',
    'attribute',
    'sort',
    'exactness',
  ],
));

โณ Managing Tasks (Asynchronous Operations)

  • Write operations in Meilisearch (like adding or updating documents) are asynchronous.
  • They return a Task object immediately while the server processes the request in the background.
// 1. Get task information from an operation
TaskInfo taskInfo = await index.addDocuments([...]);
// 2. Wait for the server to finish processing it
TaskResult finishedTask = await client.waitForTask(taskInfo.uid!);
// 3. Verify successif (finishedTask.status == 'succeeded') {
  print('Documents added successfully!');
} else {
  print('Error: ${finishedTask.error?.message}');
}

๐Ÿ› ๏ธ Index Management

// Create a new index explicitlyawait client.createIndex('books', primaryKey: 'isbn');
// List all indexes
List<MeiliSearchIndex> indexes = await client.getIndexes();
// Delete an index completelyawait client.deleteIndex('books');

1. What is the easiest way to implement typo tolerance?

  • The easiest way to implement typo tolerance in
  • Meilisearch is built specifically for “search-as-you-type” storefronts and apps, so typo tolerance is enabled by default for every index and query. You do not need to pass any special flags or parameters in your Flutter code.

How Meilisearch Handles Typos Out of the Box:

  • It calculates the Levenshtein distance automatically, scaling the number of allowed typos based on the length of the query word:
    • 1โ€“4 characters: 0 typos allowed (strictly prefix matching).
    • 5โ€“8 characters: 1 typo allowed.
    • 9+ characters: 2 typos allowed (the maximum cap).

How to Customise Typo Tolerance in Flutter:

  • If you need to change these default lengths (e.g., allow 1 typo on a 4-letter word), or disable typos entirely for specific attributes like SKU numbers, use the index settings via the Flutter SDK:
final index = client.index('products');
await index.updateTypoTolerance(TypoTolerance(
  enabled: true,
  // Change word length thresholds
  minWordSizeForTypos: MinWordSizeForTypos(
    oneTypo: 4,  // 1 typo allowed if the word is 4+ characters
    twoTypos: 8, // 2 typos allowed if the word is 8+ characters
  ),
  // Disable typo checking on specific fields (e.g., serial numbers)
  disableOnAttributes: ['sku_code', 'phone_number'],
));

2. Does Meilisearch have a built-in tokenizer to break up search strings?

  • Yes, Meilisearch has a highly sophisticated, open-source, built-in multilingual tokenizer called Charabia.
  • When you push a collection of documents or type a search string, Meilisearch runs it through Charabia automatically before it ever hits the database.
  • You don’t have to break your strings or pass string arrays manually.

What the Built-In Tokenizer Handles:

  1. Word Segmentation:
  • It knows how to break sentences into tokens.
  • For Western languages (Latin script), it splits on spaces, punctuation, and hyphens.
  • For languages that don’t use spaces (like Japanese, Chinese, or Thai), it uses dictionary-based pipelines to intelligently extract actual words.
  1. Normalization:
  • It converts all tokens to lowercase, removes accents/diacritics (e.g., converting crรจme to creme), and unifies casing.
  • This is why searches are case-insensitive by default.
  1. CamelCase & SnakeCase Separation:
  • It automatically tokenizes terms like MeiliSearch into meili and search, or flutter_app into flutter and app.

  • Because tokenization is completely managed on the Meilisearch server engine, your Flutter code stays extremely clean.

  • You simply pass raw string text directly from your TextField into the index.search(‘raw text here’) function.

3. Case Insensitivity

  • How it works: All searches are entirely case-insensitive by default.
  • Example: Querying FLUTTER, flutter, or FlUtTeR will return the exact same database records without any configuration