Manticore Search
"Manticore Search: open-source full-text search, SQL-based queries, real-time indexes, columnar storage, Elasticsearch-compatible API"
You are an expert in integrating Manticore Search for full-text search functionality.
## Key Points
- **Use real-time tables for mutable data** — real-time tables support INSERT, UPDATE, and DELETE operations. Plain tables are read-only and require re-indexing to update.
- **Enable columnar storage for large analytical datasets** — add `engine='columnar'` to column definitions that are primarily used for filtering and aggregation rather than full-text search.
## Quick Example
```typescript
// Suggest corrections for misspelled words
const suggestions = await utilsApi.sql({
body: `CALL SUGGEST('wireles', 'products')`,
});
```skilldb get search-services-skills/Manticore SearchFull skill: 247 linesManticore Search — Search Integration
You are an expert in integrating Manticore Search for full-text search functionality.
Core Philosophy
Overview
Manticore Search is an open-source search engine forked from Sphinx Search. It provides full-text search with SQL syntax support, real-time index updates, and an Elasticsearch-compatible HTTP JSON API. Manticore excels at searching structured and semi-structured data with complex filtering, supports columnar storage for analytical queries, and handles billions of documents on modest hardware. It is a strong choice when you need SQL-familiar search syntax, real-time indexing, and low operational complexity.
Setup & Configuration
Running with Docker
docker run -d \
--name manticore \
-p 9306:9306 \
-p 9308:9308 \
-v manticore-data:/var/lib/manticore \
manticoresearch/manticore
Port 9306 is the MySQL-compatible protocol. Port 9308 is the HTTP JSON API.
Configuration File
Manticore's config file lives at /etc/manticoresearch/manticore.conf:
searchd {
listen = 0.0.0.0:9306:mysql
listen = 0.0.0.0:9308:http
log = /var/log/manticore/searchd.log
query_log = /var/log/manticore/query.log
pid_file = /var/run/manticore/searchd.pid
data_dir = /var/lib/manticore
}
Client Setup in Node.js
// npm install manticoresearch
import * as Manticoresearch from "manticoresearch";
const config = new Manticoresearch.Configuration();
config.basePath = "http://localhost:9308";
const indexApi = new Manticoresearch.IndexApi(config);
const searchApi = new Manticoresearch.SearchApi(config);
const utilsApi = new Manticoresearch.UtilsApi(config);
Client Setup in Python
# pip install manticoresearch
import manticoresearch
from manticoresearch.api import index_api, search_api, utils_api
config = manticoresearch.Configuration(host="http://localhost:9308")
client = manticoresearch.ApiClient(config)
index_client = index_api.IndexApi(client)
search_client = search_api.SearchApi(client)
utils_client = utils_api.UtilsApi(client)
Core Patterns
Creating a Real-Time Table
// Using the SQL endpoint via HTTP
await utilsApi.sql({
body: `CREATE TABLE products (
title text,
description text,
price float,
category string,
created_at timestamp
) morphology='stem_en' min_word_len='2' html_strip='1'`,
});
Indexing Documents
// Insert a single document
await indexApi.insert({
index: "products",
id: 1,
doc: {
title: "Wireless Mouse",
description: "Ergonomic wireless mouse with USB-C receiver",
price: 29.99,
category: "electronics",
created_at: Math.floor(Date.now() / 1000),
},
});
// Bulk insert
await indexApi.bulk(
[
{ insert: { index: "products", id: 2, doc: { title: "Keyboard", price: 79.99, category: "electronics" } } },
{ insert: { index: "products", id: 3, doc: { title: "Monitor", price: 349.99, category: "electronics" } } },
]
.map((line) => JSON.stringify(line))
.join("\n")
);
Full-Text Search via JSON API
const searchResult = await searchApi.search({
index: "products",
query: {
bool: {
must: [
{ match: { title: "wireless" } },
],
filter: {
range: { price: { gte: 10, lte: 100 } },
},
},
},
highlight: {
fields: { title: {}, description: {} },
pre_tags: "<b>",
post_tags: "</b>",
},
sort: [{ price: "asc" }],
limit: 20,
offset: 0,
});
for (const hit of searchResult.hits.hits) {
console.log(hit._id, hit._source.title, hit._score);
}
Full-Text Search via SQL
Manticore supports querying via MySQL protocol or the /sql HTTP endpoint:
const result = await utilsApi.sql({
body: `SELECT id, title, price, HIGHLIGHT() AS h
FROM products
WHERE MATCH('wireless mouse')
AND price BETWEEN 10 AND 100
ORDER BY price ASC
LIMIT 20
OPTION ranker=bm25`,
});
# Using the MySQL-compatible protocol directly
import mysql.connector
conn = mysql.connector.connect(host="127.0.0.1", port=9306)
cursor = conn.cursor(dictionary=True)
cursor.execute("""
SELECT id, title, price, HIGHLIGHT({}, 'title') AS title_hl
FROM products
WHERE MATCH('wireless mouse')
ORDER BY WEIGHT() DESC
LIMIT 20
""")
rows = cursor.fetchall()
Faceted Search
const result = await utilsApi.sql({
body: `SELECT category, COUNT(*) AS cnt
FROM products
WHERE MATCH('mouse')
GROUP BY category
ORDER BY cnt DESC`,
});
Autocomplete with CALL SUGGEST
// Suggest corrections for misspelled words
const suggestions = await utilsApi.sql({
body: `CALL SUGGEST('wireles', 'products')`,
});
Updating and Deleting
// Partial update
await indexApi.update({
index: "products",
id: 1,
doc: { price: 24.99 },
});
// Delete by ID
await indexApi.callDelete({
index: "products",
id: 1,
});
// Delete by query
await indexApi.callDelete({
index: "products",
query: { equals: { category: "discontinued" } },
});
Best Practices
- Use real-time tables for mutable data — real-time tables support INSERT, UPDATE, and DELETE operations. Plain tables are read-only and require re-indexing to update.
- Leverage SQL syntax for complex queries — Manticore's SQL support is extensive and often more readable than JSON queries for multi-condition filters, GROUP BY, and JOIN operations across tables.
- Enable columnar storage for large analytical datasets — add
engine='columnar'to column definitions that are primarily used for filtering and aggregation rather than full-text search.
Common Pitfalls
- Confusing
stringandtextcolumn types —textcolumns are full-text indexed and searchable with MATCH().stringcolumns are stored as-is and can only be filtered with exact equality. Usetextfor searchable content andstringfor category-like attributes. - Not tuning
morphologysettings — by default, no stemming or lemmatization is applied. Enablemorphology='stem_en'(or the relevant language) on the table to ensure search matches word variants like "running" and "run".
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add search-services-skills
Related Skills
Algolia
"Algolia: instant search, faceted search, InstantSearch.js/React, indexing, ranking, search analytics"
Elasticsearch
"Elasticsearch: full-text search, aggregations, mapping, bulk indexing, Node.js client, relevance tuning"
Fuse Js
Fuse.js is a lightweight, powerful fuzzy-search library for JavaScript that runs entirely client-side. It's ideal for quickly adding flexible, typo-tolerant search capabilities to web applications without server-side infrastructure.
Lunr
Lunr is a small, fast JavaScript search library for browsers and Node.js. It allows you to build a search index directly within your application, providing full-text search capabilities without a backend API or external service. It's ideal for static sites, documentation, or client-side applications requiring offline-capable search.
Meilisearch
"Meilisearch: self-hosted search engine, typo tolerance, faceting, filtering, sorting, REST API, JavaScript SDK"
Opensearch
OpenSearch is a community-driven, open-source search and analytics suite derived from Elasticsearch. It's ideal for powering full-text search, log analytics, security monitoring, and real-time application monitoring, offering powerful scalability and flexibility for diverse data needs.