Getting Started
Indx is a high-performance full-text search engine for JSON data. It uses pattern recognition and BM25 scoring — no tokenizers, stemmers, or analyzers to configure. It handles typos and messy input out of the box.
Two Ways to Run Indx
Embed it — the IndxSearchLib NuGet package. Search runs inside your .NET application; nothing else to deploy. Follow the embedded walkthrough below.
Host it — the Indx server: an HTTP API, a dashboard for teams and datasets, API keys, and an MCP endpoint. For any language (Node.js, Python, Java, …), for several applications sharing one index, or when a .NET team wants the dashboard and team management too. Follow the server walkthrough below.
Embedded Walkthrough (C#)
1. Install
dotnet add package IndxSearchLib2. Prepare Your Data
Indx accepts a JSON array of objects. Nested objects are supported; nested fields use dot notation.
[
{ "id": 1, "name": "Wireless Headphones", "brand": "Acme", "price": 79.99, "category": "audio" },
{ "id": 2, "name": "Bluetooth Speaker", "brand": "Zync", "price": 49.99, "category": "audio" },
{ "id": 3, "name": "USB-C Hub", "brand": "Acme", "price": 29.99, "category": "accessories" }
]3. Init — Discover Fields
Init parses the JSON structure and returns the list of fields it discovers, along with each field's inferred type. The fields start unconfigured — you assign roles (searchable, filterable, sortable, facetable) in the next step.
using Indx.Api;
var engine = new SearchEngine();
using var stream = File.OpenRead("products.json");
engine.Init(stream);After Init, call GetFieldConfiguration() to inspect the discovered fields:
foreach (var field in engine.GetFieldConfiguration())
Console.WriteLine(quot;{field.FieldName} type={field.FieldType}");
// id type=Number
// name type=String
// brand type=String
// price type=Number
// category type=String4. Configure Fields
Nothing is searchable by default. Mark each field's role explicitly:
var name = engine.GetField("name")!;
var brand = engine.GetField("brand")!;
var price = engine.GetField("price")!;
var category = engine.GetField("category")!;
name.Searchable = true;
name.Weight = 2.0f; // higher weight = more influence on score
brand.Searchable = true;
brand.Weight = 1.0f;
price.Filterable = true;
price.Sortable = true;
category.Filterable = true;
category.Facetable = true;Weight is a float (default 1.0, i.e. neutral). Higher means stronger influence on BM25 scoring relative to other searchable fields; below 1.0 de-emphasizes the field.
5. Load and Index
Stream the same JSON again for loading, then build the index:
stream.Position = 0;
engine.Load(stream);
engine.Index();The engine is now in Ready state.
6. Search
var result = engine.Search(new Query("wireless", 20));
foreach (var entry in result.Entries)
Console.WriteLine(quot;key={entry.DocumentKey} score={entry.Score}");To retrieve the full JSON of the matching documents:
var keys = result.Entries.Select(e => e.DocumentKey).ToArray();
var docs = engine.GetJsonDataOfKeys(keys);7. Add a Filter
Filters are evaluated server-side against the full dataset — never filter the result array yourself.
// Match only the "audio" category
var audioFilter = engine.CreateValueFilter("category", "audio", out _);
var query = new Query("wireless", 20) { Filter = audioFilter };
var result = engine.Search(query);For numeric ranges:
var affordable = engine.CreateRangeFilter("price", 0, 50, out _);Combine with AND or OR:
var combined = audioFilter & affordable; // AND; use | for OR, ! to negate8. Sort Results
Sorting is secondary to score. However, an empty search (Query.Text = "", see Empty Search) results in all scores being 0, in which case the sort order behaves as primary.
var query = new Query("", 20) { SortBy = "price", SortAscending = true };9. Facets
Facets return distinct value counts for a field — useful for sidebar filters.
var query = new Query("wireless", 20) { EnableFacets = true };
var result = engine.Search(query);
foreach (var (value, count) in result.Facets["category"])
Console.WriteLine(quot;{value}: {count}");
// audio: 2
// accessories: 0Server Walkthrough (HTTP)
Run the server:
git clone https://github.com/indxSearch/Indx
cd Indx
dotnet runRequires .NET 10 SDK. See Server Setup for registration, API keys, and deployment.
Load Data
# Analyze structure
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/analyze \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
--data-binary @products.json
# Configure fields (204 No Content)
curl -X PUT https://localhost:5001/api/teams/acme/datasets/products/fields/configuration \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '[
{ "fieldName": "name", "searchable": true, "weight": 2.0 },
{ "fieldName": "brand", "searchable": true, "weight": 1.0 },
{ "fieldName": "price", "filterable": true, "sortable": true },
{ "fieldName": "category", "filterable": true, "facetable": true }
]'
# Load documents (204 No Content)
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/load \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
--data-binary @products.json
# Build index — returns 202 Accepted; the build runs in the background
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/index \
-H "Authorization: Bearer <token>"
# Poll status until systemState is 4 (Ready)
curl https://localhost:5001/api/teams/acme/datasets/products/status \
-H "Authorization: Bearer <token>"Search
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/search \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "text": "wireless", "maxNumberOfRecordsToReturn": 20 }'Filter and Search
# Create a filter
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/filters/value \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "fieldName": "category", "value": "audio" }'
# → { "hashString": "abc123..." }
# Use it in search
curl -X POST https://localhost:5001/api/teams/acme/datasets/products/search \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "text": "wireless", "maxNumberOfRecordsToReturn": 20, "filter": { "hashString": "abc123..." } }'Next Steps
- Core Concepts — Pattern matching, coverage, and scoring explained
- C# API Reference — Full method and type reference
- HTTP API Reference — All endpoints, request/response schemas
- How To guides — Specific recipes: facets, boosts, deep coverage, and more