BoxLang 🚀 A New JVM Dynamic Language Learn More...

Boxlang Meilisearch Module

v1.0.0-snapshot-snapshot BoxLang Modules

⚡︎ Meilisearch+

|:------------------------------------------------------: |
| ⚡︎ B o x L a n g ⚡︎
| Dynamic : Modular : Productive |
| :----------------------------:  |
Copyright Since 2023 by Ortus Solutions, Corp
www.boxlang.io | www.ortussolutions.com

 

Add native support for the powerful and lightweight Meilisearch search engine in your BoxLang application.

What is Meilisearch?

Meilisearch is a lightning-fast, open-source search engine that delivers instant search results with typo tolerance, filters, facets, and more. It handles millions of documents with ease while maintaining sub-50ms response times.

Key Features:

  • 🔍 Instant search-as-you-type results
  • 🎯 Typo tolerance and relevance ranking
  • 🔧 Faceted search and filtering
  • 📊 Document ranking and sorting
  • 🚀 RESTful HTTP API

Bx-Meilisearch+ Capabilities

This module provides a fluent, BoxLang-native interface to the Meilisearch HTTP API. It simplifies integration and makes building search functionality a breeze.

Current Features:

  • Index Management - Create, list, get, delete, and swap indexes
  • Document Management - Add, replace, update, and delete documents
  • Search - Full search with filters, pagination, and fluent DSL
  • Task Management - Track async operations with Task objects
  • Settings Management - Update index settings
  • API Keys - Manage API keys for authentication
  • Statistics & Health - Monitor instance health and performance
  • 🔄 Fluent DSL - Chain methods for readable, maintainable code
  • 🔐 Environment Configuration - Configure via env vars or module settings
  • Async Operations - All operations return Task objects for tracking

Coming Soon:

  • 📈 Advanced analytics
  • 🗂️ Multi-tenant support

Quick Start

Installation

Install via CommandBox:

box install bx-meilisearch

Configuration

You can configure the Meilisearch API connection via environment variables:

export MEILISEARCH_URL=http://localhost:7700
export MEILISEARCH_MASTER_KEY=your-master-key

Or configure bx-meilisearch via boxlang.json:

// boxlang.json
"modules": {
	"meilisearch": {
		"enabled": true,
		"settings": {
			// Meilisearch instance URL (default: http://localhost:7700)
			"url"                 : getSystemSetting( "MEILISEARCH_URL", "http://localhost:7700" ),
			// Master or API key used for Authorization header
			"masterKey"           : getSystemSetting( "MEILISEARCH_MASTER_KEY", "changeMe" ),
			// Optional callback invoked before each HTTP request is sent.
			// Signature: ( httpResult, httpClient ) => {}
			"onRequestStart"      : null,
			// Optional callback invoked after each HTTP request completes successfully.
			// Signature: ( httpResult ) => {}
			"onResponseCompletion": null
		}
	}
}

API Reference

Indexes

List indexes

List all indexes with pagination and filtering options.

var result = meilisearch()
    .indexes()
    .list();

🔗 Meilisearch: List All Indexes

Swap indexes

Swap two indexes for zero-downtime deployments.

meilisearch()
    .indexes()
    .swap( [ "books", "books_staging" ] );

🔗 Meilisearch: Swap Indexes

Get stats

Get database and index statistics including document counts and sizes.

var result = meilisearch()
    .indexes()
    .stats();

🔗 Meilisearch: Get Stats

Single Index

Get an index

Retrieve a single index by its unique identifier.

var result = meilisearch()
    .index( "books" )
    .get();

🔗 Meilisearch: Get One Index

Create an index

Create a new index with an optional primary key.

// Create with auto-generated primary key
meilisearch()
    .index( "books" )
    .create();

// Create with specific primary key
meilisearch()
    .index( "books" )
    .create( "id" );

🔗 Meilisearch: Create an Index

Get Index stats

Get index statistics.

var result = meilisearch()
    .index( "books" )
    .stats();

🔗 Meilisearch: Get stats of an index

Compact an index

Compact an index to reduce database fragmentation.

var result = meilisearch()
    .index( "books" )
    .compact();

🔗 Meilisearch: Compact an Index

Delete an index

Permanently delete an index and all its documents.

meilisearch()
    .index( "books" )
    .delete();

🔗 Meilisearch: Delete an Index

Documents

Add or Replace Documents

Add or replace documents in an index. Replaces existing documents with the same primary key.

var task = meilisearch()
    .index( "books" )
    .documents()
    .add([
        { id: 1, title: "BoxLang for Beginners", author: "Jane Doe" },
        { id: 2, title: "Advanced BoxLang", author: "John Smith" }
    ])
    .orReplace();

// Returns a Task object - use task.getStatus() to check progress
// task.getType() == "documentAdditionOrUpdate"

🔗 Meilisearch: Add or Replace Documents

Add or Update Documents

Add or update documents in an index. Updates existing documents instead of replacing them entirely.

var task = meilisearch()
    .index( "books" )
    .documents()
    .add([
        { id: 1, title: "BoxLang for Beginners - Updated Edition", author: "Jane Doe" },
        { id: 3, title: "BoxLang Cookbook", author: "New Author" }
    ])
    .orUpdate();

// Returns a Task object - use task.getStatus() to check progress
// task.getType() == "documentAdditionOrUpdate"

🔗 Meilisearch: Add or Update Documents

Delete All Documents

Delete all documents from an index.

var task = meilisearch()
    .index( "books" )
    .documents()
    .deleteAll();

// Returns a Task object - use task.getStatus() to check progress
// task.getType() == "documentDeletion"

🔗 Meilisearch: Delete All Documents

Get Documents

Retrieve documents from an index with optional filtering.

var result = meilisearch()
    .index( "books" )
    .documents()
    .get({ offset: 0, limit: 20 });

🔗 Meilisearch: Get Documents

Get Single Document

Retrieve a specific document by its ID.

var result = meilisearch()
    .index( "books" )
    .document( "1" )
    .get();

🔗 Meilisearch: Get One Document

Delete Single Document

Delete a specific document by its ID.

var task = meilisearch()
    .index( "books" )
    .document( "1" )
    .delete();

// Returns a Task object

🔗 Meilisearch: Delete One Document

The Search DSL provides a fluent interface for querying documents in an index. It supports both POST (default) and GET request methods.

Search for documents using a query string.

var result = meilisearch()
    .index( "books" )
    .search( "tolkien" )
    .send();

// Returns: {
//   hits: [ { id: 1, title: "The Hobbit", ... }, ... ],
//   query: "tolkien",
//   processingTimeMs: 1,
//   estimatedTotalHits: 2,
//   ...
// }

🔗 Meilisearch: Search Documents

Search with parameters

Pass initial parameters as the second argument to search().

var result = meilisearch()
    .index( "books" )
    .search( "tolkien", { limit: 5, offset: 0 } )
    .send();

🔗 Meilisearch: Search Parameters

Fluent DSL with chained methods

Chain parameter methods for a readable, builder-style interface.

var result = meilisearch()
    .index( "books" )
    .search( "tolkien" )
    .filter( "genres = fantasy" )
    .limit( 5 )
    .offset( 0 )
    .attributesToRetrieve( "title,author" )
    .send();

Any Meilisearch search parameter can be used as a method call. The method name becomes the parameter name, and the method value becomes the parameter value.

Common parameters:

  • filter - Filter results using Meilisearch filter expressions
  • limit - Maximum number of results to return
  • offset - Number of results to skip (for pagination)
  • attributesToRetrieve - Comma-separated list of fields to return
  • sort - Sort order (e.g., "price:asc")
  • facets - Facets to compute

🔗 Meilisearch: Search Parameters

Search with GET request

Use sendWithGet() instead of send() to make a GET request instead of POST.

var result = meilisearch()
    .index( "books" )
    .search( "tolkien" )
    .limit( 10 )
    .sendWithGet();

🔗 Meilisearch: Search with GET

Facets

The Facets API allows you to search for facet values using AI-powered prefix search and typo tolerance.

Note: This endpoint requires attributes to be added to filterableAttributes in index settings first.

Search for facet values within a given facet.

// Search for facet values
var result = meilisearch()
    .index( "books" )
    .facetSearch( "genre", { facetQuery: "fiction" } )
    .send();

// With additional parameters
var result = meilisearch()
    .index( "books" )
    .facetSearch( "genre", {
        facetQuery: "fiction",
        q: "fantasy",
        filter: "rating > 3",
        matchingStrategy: "all"
    })
    .send();

// Returns: {
//   facetHits: [
//     { value: "fiction", count: 7 }
//   ],
//   facetQuery: "fiction",
//   processingTimeMs: 0
// }

🔗 Meilisearch: Facet Search

Keys

List API keys

List all API keys with optional pagination.

var result = meilisearch()
    .keys()
    .list();

// With pagination
var result = meilisearch()
    .keys()
    .list( { offset: 0, limit: 10 } );

🔗 Meilisearch: Get Keys

Get an API key

Retrieve a single API key by its UID or key value.

var result = meilisearch()
    .keys()
    .get( "abc123" );

🔗 Meilisearch: Get Key

Update an API key

Update an existing API key's name or description.

meilisearch()
    .keys()
    .update( "abc123", { name: "Updated Key Name" } );

🔗 Meilisearch: Update Key

Delete an API key

Permanently delete an API key.

meilisearch()
    .keys()
    .delete( "abc123" );

🔗 Meilisearch: Delete Key

Settings

List settings

Retrieve the complete settings configuration for an index.

var result = meilisearch()
    .index( "books" )
    .settings()
    .list();

🔗 Meilisearch: Get Settings

Update settings

Update index settings including ranking rules, searchable attributes, and more.

meilisearch()
    .index( "books" )
    .settings()
    .update( {
      "searchableAttributes": [ "title", "description", "author" ],
      "filterableAttributes": [ "genre", "year" ],
      "sortableAttributes": [ "year", "price" ],
      "rankingRules": [
          "words",
          "typo",
          "proximity",
          "attribute",
          "sort",
          "exactness"
      ],
      "stopWords": [ "the", "a", "an" ],
      "synonyms": {
          "computer": [ "pc", "laptop", "desktop" ]
      }
  } );

🔗 Meilisearch: Update Settings

Reset settings

Reset all index settings to their default values.

meilisearch()
    .index( "books" )
    .settings()
    .reset();

🔗 Meilisearch: Reset Settings

Tasks

List tasks

List all asynchronous tasks with filtering and pagination options.

var result = meilisearch()
    .tasks()
    .list();

// With filters
var result = meilisearch()
    .tasks()
    .list( {
      limit: 20,
      statuses: "enqueued,processing",
      types: "indexCreation,indexUpdate"
  } );

🔗 Meilisearch: Get Tasks

Get a task

Retrieve a single task by its unique identifier.

var result = meilisearch()
    .tasks()
    .get( "123" );

🔗 Meilisearch: Get Task

Cancel tasks

Cancel enqueued or processing tasks.

// Cancel specific tasks by UID
meilisearch()
    .tasks()
    .cancel( { uids: [ "1", "5", "7" ] } );

// Cancel tasks by status
meilisearch()
    .tasks()
    .cancel( { statuses: "enqueued" } );

🔗 Meilisearch: Cancel Tasks

Delete tasks

Delete tasks matching specific criteria.

// Delete specific tasks by UID
meilisearch()
    .tasks()
    .delete( { uids: [ "1", "5" ] } );

// Delete tasks by status
meilisearch()
    .tasks()
    .delete( { statuses: "canceled" } );

// Delete completed tasks older than a date
meilisearch()
    .tasks()
    .delete( {
        statuses: "succeeded,failed",
        beforeEnqueuedAt: "2024-01-01T00:00: 00Z"
    } );

🔗 Meilisearch: Delete Tasks

Batches

The Batches API allows you to monitor how Meilisearch groups and processes asynchronous operations. Batches show the progress of grouped tasks.

List batches

List all batches with filtering and pagination options.

var result = meilisearch()
    .batches()
    .list();

// With filters
var result = meilisearch()
    .batches()
    .list({
        limit: 10,
        from: 0,
        reverse: false
    });

// Returns: {
//   results: [
//     {
//       uid: 2,
//       progress: null,
//       details: { receivedDocuments: 6, indexedDocuments: 6 },
//       stats: { totalNbTasks: 1, status: { succeeded: 1 } },
//       duration: "PT0.110083S",
//       startedAt: "2024-12-10T15:49:  04.995321Z",
//       finishedAt: "2024-12-10T15:49:  05.105404Z",
//       batchStrategy: "batched all enqueued tasks"
//     }
//   ],
//   total: 3,
//   limit: 1,
//   from: 2
// }

🔗 Meilisearch: Get Batches

Get a batch

Retrieve a single batch by its unique identifier.

var result = meilisearch()
    .batches()
    .get( "0" );

// Returns: {
//   uid: 0,
//   progress: { steps: [...], percentage: 32.8471 },
//   details: { receivedDocuments: 6, indexedDocuments: 6 },
//   stats: { totalNbTasks: 1, status: { succeeded: 1 } },
//   duration: "PT0.250518S",
//   startedAt: "2024-12-10T15:20:  30.18182Z",
//   finishedAt: "2024-12-10T15:20:  30.432338Z",
//   batchStrategy: "batched all enqueued tasks"
// }

🔗 Meilisearch: Get One Batch

Webhooks

The Webhooks API allows you to trigger automatic workflows when Meilisearch finishes processing tasks. When a task completes, Meilisearch sends the task object to all configured webhooks.

Note: You can create up to 20 webhooks. Having multiple webhooks active at the same time may negatively impact performance.

List webhooks

List all configured webhooks.

var result = meilisearch()
    .webhooks()
    .list();

// Returns: {
//   results: [
//     {
//       uuid: "627ea538-733d-4545-8d2d-03526eb381ce",
//       url: "https://example.com/webhook",
//       headers: { authorization: "REDACTED" },
//       isEditable: true
//     }
//   ]
// }

🔗 Meilisearch: Get Webhooks

Create a webhook

Create a new webhook to receive task completion notifications.

var result = meilisearch()
    .webhooks()
    .create({
        url: "https://example.com/webhook",
        headers: {
            authorization: "Bearer SECRET_KEY",
            referer: "https://example.com"
        }
    });

// Returns: {
//   uuid: "627ea538-733d-4545-8d2d-03526eb381ce",
//   url: "https://example.com/webhook",
//   headers: {
//     authorization: "Bearer SECRET_KEY",
//     referer: "https://example.com"
//   },
//   isEditable: true
// }

🔗 Meilisearch: Create Webhook

Get a webhook

Retrieve a single webhook by its UUID.

var result = meilisearch()
    .webhooks()
    .webhook( "WEBHOOK_UUID" )
    .get();

🔗 Meilisearch: Get Webhook

Update a webhook

Update a webhook's configuration. To remove a field, set its value to null.

var result = meilisearch()
    .webhooks()
    .webhook( "WEBHOOK_UUID" )
    .update({
        headers: {
            referer: null  // Remove this header
        }
    });

// Returns: {
//   uuid: "627ea538-733d-4545-8d2d-03526eb381ce",
//   url: "https://example.com/webhook",
//   headers: {
//     authorization: "Bearer SECRET_KEY"
//   },
//   isEditable: true
// }

🔗 Meilisearch: Update Webhook

Delete a webhook

Delete a webhook to stop receiving task completion notifications.

meilisearch()
    .webhooks()
    .webhook( "WEBHOOK_UUID" )
    .delete();
// Returns: 204 No Content

🔗 Meilisearch: Delete Webhook

Task Object

Every asynchronous operation in Meilisearch (index creation, document updates, index swaps, etc.) returns a Task object. This object provides methods to track the operation's progress and wait for completion.

Understanding Tasks

Meilisearch operations are asynchronous - when you create an index or add documents, Meilisearch creates a Task to track that operation while it continues in the background. You use the Task object to:

  1. Check the current status of the operation
  2. Wait for the operation to complete
  3. Get detailed information about the operation result

Example: Creating an Index with Task Tracking

// Create an index - returns a Task object immediately
var task = meilisearch()
    .index( "books" )
    .create();

// Check initial status
print( "Task Status: " & task.getStatus() );  // "enqueued"
print( "Task Type: " & task.getType() );      // "indexCreation"
print( "Task UID: " & task.getTaskUid() );    // "123"

// Wait for the task to complete (polls every second, by default)
var completedTask = task.waitForCompletion();

// Check final status
if ( completedTask.getStatus() == "succeeded" ) {
    print( "Index created successfully!" );
} else if ( completedTask.getStatus() == "failed" ) {
    print( "Failed: " & completedTask.getError().message );
}

Task.getStatus()

Get the current status of the task.

var task = meilisearch()
    .index( "books" )
    .create();

print( task.getStatus() );  // "enqueued", "processing", "succeeded", "failed", or "canceled"

Possible values:

  • enqueued - Task is in the queue, waiting to be processed
  • processing - Task is currently being processed
  • succeeded - Task completed successfully
  • failed - Task failed (check getError() for details)
  • canceled - Task was canceled

Task.getType()

Get the type of operation this task represents.

var task = meilisearch()
    .index( "books" )
    .documents()
    .add([ { id: 1, title: "Book" } ])
    .orReplace();

print( task.getType() );  // "documentAdditionOrUpdate"

Common types:

  • indexCreation - Creating a new index
  • indexUpdate - Updating an index
  • indexDeletion - Deleting an index
  • documentAdditionOrUpdate - Adding or updating documents
  • documentDeletion - Deleting documents
  • indexSwap - Swapping two indexes
  • settingsUpdate - Updating index settings

Task.toStruct()

Convert the Task object to a plain struct with all task data.

var task = meilisearch()
    .index( "books" )
    .create();

var taskData = task.toStruct();

// Returns: {
//   taskUid: "123",
//   indexUid: "books",
//   status: "succeeded",
//   type: "indexCreation",
//   enqueuedAt: "2024-01-15T10:30:   00.000Z",
//   startedAt: "2024-01-15T10:30:   01.000Z",
//   finishedAt: "2024-01-15T10:30:   02.000Z",
//   duration: "1s",
//   details: { ... }
// }

Task.waitForCompletion( interval )

Wait for the task to complete by polling at regular intervals.

var task = meilisearch()
    .index( "books" )
    .documents()
    .add([
        { id: 1, title: "Book 1" },
        { id: 2, title: "Book 2" }
    ])
    .orReplace();

// Wait up to 30 seconds, polling every 2 seconds
var completedTask = task.waitForCompletion( 2000, 30000 );

if ( completedTask.getStatus() == "succeeded" ) {
    print( "Documents added successfully!" );
    print( "Documents processed: " & completedTask.getDetails().receivedDocuments );
}

Parameters:

  • interval (integer) - Number of milliseconds to wait between polls (default: 1000)
  • timeout (integer) - Maximum time to wait for task completion in milliseconds (default: 30000)

Returns: A new Task object with the final task status

Note: The original Task object is not modified; waitForCompletion() returns a new Task instance with updated data.

Additional Task Getters

var task = meilisearch().index( "books" ).create();

// Get basic info
task.getTaskUid();      // Unique task identifier
task.getIndexUid();     // Index this task operates on
task.getEnqueuedAt();   // When task was queued (ISO timestamp)
task.getStartedAt();    // When processing started
task.getFinishedAt();   // When processing finished
task.getDuration();     // How long processing took

// Get detailed info
task.getDetails();      // Task-specific details (varies by type)
task.getError();        // Error info if task failed
task.getCanceledBy();   // UID of task that canceled this one (if any)

// Check completion status
task.isComplete();      // Returns true if status is "succeeded", "failed", or "canceled"

Experimental Features

List experimental features

List all available experimental features and their current status.

var result = meilisearch()
    .experimentalFeatures()
    .list();

🔗 Meilisearch: Get Experimental Features

Get an experimental feature

Retrieve the status of a specific experimental feature.

var result = meilisearch()
    .experimentalFeatures()
    .get( "metrics" );

🔗 Meilisearch: Get One Experimental Feature

Enable/disable an experimental feature

Enable or disable a specific experimental feature.

// Enable
meilisearch()
    .experimentalFeatures()
    .set( "metrics", true );

// Disable
meilisearch()
    .experimentalFeatures()
    .set( "metrics", false );

🔗 Meilisearch: Update Experimental Features

Set multiple experimental features

Enable or disable multiple experimental features at once.

meilisearch()
    .experimentalFeatures()
    .setAll( {
      "metrics": true,
      "exportPayer": false,
      "logs": true
  } );

🔗 Meilisearch: Update Experimental Features

Snapshots

Create a snapshot

Create a snapshot of the Meilisearch instance for backup purposes.

var result = meilisearch()
    .snapshots()
    .create();

🔗 Meilisearch: Create Snapshot

Network

The Network API allows you to create a network of Meilisearch instances for federated search and horizontal database partitioning strategies like sharding.

Note: This is an experimental feature. Enable it first using the experimental features endpoint.

Get network configuration

Retrieve the current network configuration including self instance name, sharding status, and remote instances.

var result = meilisearch()
    .network()
    .get();

// Returns: {
//   self: "ms-00",
//   sharding: false,
//   remotes: {
//     "ms-00": {
//       url: "http://ms-1235.example.meilisearch.io",
//       searchApiKey: "Ecd1SDDi4pqdJD6qYLxD3y7VZAEb4d9j6LJgt4d6xas"
//     }
//   }
// }

🔗 Meilisearch: Get Network

Update network configuration

Update the network configuration. Updates are partial - only provide the fields you want to update.

meilisearch()
    .network()
    .update({
        self: "ms-00",
        sharding: true,
        remotes: {
            "ms-00": {
                url: "http://localhost:7700",
                searchApiKey: "masterKey",
                writeApiKey: "masterKey"
            },
            "ms-01": {
                url: "http://remote-instance:7700",
                searchApiKey: "remoteSearchKey",
                writeApiKey: "remoteWriteKey"
            }
        }
    });

🔗 Meilisearch: Update Network

Similar Documents

The Similar Documents API uses AI-powered search to find documents similar to a specific document. This requires the document to have vector embeddings generated by a configured embedder.

Note: This endpoint requires an embedder to be configured in the index settings first.

Get similar documents

Find documents similar to a target document using POST or GET requests.

// Using POST (recommended)
var result = meilisearch()
    .index( "books" )
    .document( "123" )
    .similar({ embedder: "default" })
    .send();

// Using GET
var result = meilisearch()
    .index( "books" )
    .document( "123" )
    .similar({ embedder: "default" })
    .sendGet();

// With additional parameters
var result = meilisearch()
    .index( "books" )
    .document( "123" )
    .similar({
        embedder: "default",
        limit: 10,
        filter: "genre = fiction",
        showRankingScore: true
    })
    .send();

// Returns: {
//   hits: [
//     { id: "456", title: "Similar Book 1" },
//     { id: "789", title: "Similar Book 2" }
//   ],
//   id: "123",
//   processingTimeMs: 5,
//   limit: 10,
//   offset: 0,
//   estimatedTotalHits: 2
// }

🔗 Meilisearch: Similar Documents

Health Check

Check instance health

Check if the Meilisearch instance is available and responding.

var result = meilisearch()
    .health();
// Returns: { "status": "available" }

🔗 Meilisearch: Health

Version

Get instance version

Retrieve the version information of the Meilisearch instance.

var result = meilisearch()
    .version();
// Returns: { "commitSha": "...", "commitDate": "...", "pkgVersion": "1.x.x" }

🔗 Meilisearch: Version


BoxLang+ Membership

Bx-Meilisearch+ is a premium BoxLang module available exclusively to BoxLang+ members.

BoxLang+ provides:

  • 🌟 Premium modules like this one
  • 🚀 Early access to new features
  • 📞 Priority support from the Ortus team
  • 💼 Commercial licensing for enterprise use

Learn more about BoxLang+ and join today!


Ortus Sponsors

BoxLang is a professional open-source project and it is completely funded by the community and Ortus Solutions, Corp. Ortus Patreons get many benefits like a cfcasts account, a FORGEBOX Pro account and so much more. If you are interested in becoming a sponsor, please visit our patronage page: https://patreon.com/ortussolutions

THE DAILY BREAD

"I am the way, and the truth, and the life; no one comes to the Father, but by me (JESUS)" Jn 14:1-12

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.


[Unreleased]

  • First iteration of this module

$ box install bx-meilisearch

No collaborators yet.
     
  • {{ getFullDate("2025-12-09T05:18:00Z") }}
  • {{ getFullDate("2026-04-16T16:09:10Z") }}
  • 453
  • 18