Skip to main content
Technology & EngineeringSearch Services219 lines

Zinc Search

"Zinc Search: lightweight Elasticsearch alternative, full-text search, log search, REST API, minimal resource usage"

Quick Summary19 lines
You are an expert in integrating ZincSearch for full-text search functionality.

## Key Points

- **Use `_bulkv2` for batch indexing** — inserting documents one at a time is significantly slower; always batch when loading data or syncing from a database.
- **Choose `disk` storage for production** — the default in-memory storage type (`""`) loses data on restart; set `storage_type: "disk"` for any persistent use case.
- **Use the ES-compatible endpoints for complex queries** — the native `/api/_search` endpoint supports simple searches, but `/es/<index>/_search` supports bool queries, filters, and aggregations.

## Quick Example

```typescript
// Delete a document
await zinc.delete(`/products/_doc/${docId}`);

// Delete an entire index
await zinc.delete(`/index/products`);
```
skilldb get search-services-skills/Zinc SearchFull skill: 219 lines
Paste into your CLAUDE.md or agent config

Zinc Search — Search Integration

You are an expert in integrating ZincSearch for full-text search functionality.

Core Philosophy

Overview

ZincSearch (formerly Zinc) is a lightweight, open-source search engine written in Go. It provides an Elasticsearch-compatible API with dramatically lower resource requirements — typically using 10x less RAM than Elasticsearch for equivalent workloads. It stores data using bluge as its underlying indexing library and supports full-text search, aggregations, and log search out of the box. ZincSearch is ideal for small-to-medium datasets, log analysis, and projects that need search without the operational overhead of Elasticsearch.

Setup & Configuration

Running ZincSearch with Docker

docker run -d \
  --name zincsearch \
  -p 4080:4080 \
  -e ZINC_FIRST_ADMIN_USER=admin \
  -e ZINC_FIRST_ADMIN_PASSWORD=Complexpass#123 \
  -v zinc-data:/data \
  public.ecr.aws/zinclabs/zincsearch:latest

Binary Installation

# Download the latest release from GitHub
# https://github.com/zincsearch/zincsearch/releases

# Set required environment variables
export ZINC_FIRST_ADMIN_USER=admin
export ZINC_FIRST_ADMIN_PASSWORD=Complexpass#123
export ZINC_DATA_PATH=./data

# Start the server
./zincsearch

Client Setup in Node.js

ZincSearch exposes a REST API compatible with parts of the Elasticsearch API. Use any HTTP client:

import axios from "axios";

const zinc = axios.create({
  baseURL: "http://localhost:4080/api",
  auth: {
    username: "admin",
    password: "Complexpass#123",
  },
  headers: { "Content-Type": "application/json" },
});

Client Setup in Python

import requests

ZINC_URL = "http://localhost:4080/api"
AUTH = ("admin", "Complexpass#123")

def zinc_request(method: str, path: str, json=None):
    resp = requests.request(method, f"{ZINC_URL}{path}", auth=AUTH, json=json)
    resp.raise_for_status()
    return resp.json()

Core Patterns

Creating an Index

// Create an index with explicit mappings
await zinc.put("/index", {
  name: "products",
  storage_type: "disk",
  shard_num: 1,
  mappings: {
    properties: {
      title: { type: "text", index: true, highlightable: true },
      description: { type: "text", index: true, highlightable: true },
      price: { type: "numeric", index: true },
      category: { type: "keyword", index: true },
      created_at: { type: "date", format: "2006-01-02T15:04:05Z07:00" },
    },
  },
});

Indexing Documents

// Single document
await zinc.put(`/products/_doc/${docId}`, {
  title: "Wireless Mouse",
  description: "Ergonomic wireless mouse with USB-C receiver",
  price: 29.99,
  category: "electronics",
  created_at: new Date().toISOString(),
});

// Bulk indexing — use the _bulkv2 endpoint
await zinc.post("/_bulkv2", {
  index: "products",
  records: [
    { title: "Keyboard", description: "Mechanical keyboard", price: 79.99, category: "electronics" },
    { title: "Monitor", description: "27-inch 4K display", price: 349.99, category: "electronics" },
  ],
});

Searching Documents

// Full-text search with highlighting
const result = await zinc.post("/products/_search", {
  search_type: "match",
  query: {
    term: "wireless mouse",
    field: "title",
  },
  sort_fields: ["-price"],
  from: 0,
  max_results: 20,
  _source: ["title", "price", "category"],
  highlight: {
    fields: {
      title: {},
      description: {},
    },
    pre_tags: ["<mark>"],
    post_tags: ["</mark>"],
  },
});

// result.hits.hits contains the matching documents
for (const hit of result.hits.hits) {
  console.log(hit._source.title, hit._score);
}

Elasticsearch-Compatible Search

// ZincSearch supports a subset of Elasticsearch's query DSL
const result = await zinc.post("/es/products/_search", {
  query: {
    bool: {
      must: [
        { match: { title: "mouse" } },
      ],
      filter: [
        { range: { price: { gte: 10, lte: 50 } } },
        { term: { category: "electronics" } },
      ],
    },
  },
  sort: [{ price: { order: "asc" } }],
  size: 10,
});

Aggregations

const result = await zinc.post("/es/products/_search", {
  query: { match_all: {} },
  aggs: {
    categories: {
      terms: { field: "category", size: 10 },
    },
    avg_price: {
      avg: { field: "price" },
    },
  },
  size: 0, // Only return aggregation results
});

Deleting Documents and Indices

// Delete a document
await zinc.delete(`/products/_doc/${docId}`);

// Delete an entire index
await zinc.delete(`/index/products`);

Best Practices

  • Use _bulkv2 for batch indexing — inserting documents one at a time is significantly slower; always batch when loading data or syncing from a database.
  • Choose disk storage for production — the default in-memory storage type ("") loses data on restart; set storage_type: "disk" for any persistent use case.
  • Use the ES-compatible endpoints for complex queries — the native /api/_search endpoint supports simple searches, but /es/<index>/_search supports bool queries, filters, and aggregations.

Common Pitfalls

  • Assuming full Elasticsearch compatibility — ZincSearch implements a subset of the ES API. Features like nested queries, percolator, and scripting are not supported. Always test your query patterns against ZincSearch directly.
  • Not setting auth credentials before first runZINC_FIRST_ADMIN_USER and ZINC_FIRST_ADMIN_PASSWORD are only read on the first startup. If you forget them, ZincSearch starts without authentication and you must recreate the data directory to set them.

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

Get CLI access →