# Solr compatibility

WarpSearch serves the Solr HTTP API for each collection at `https://api.warpsearch.io/compat/solr/{collection}`. The collection name replaces the Solr core or collection name in your existing URLs. Requests use Solr parameters, and responses come back in Solr's JSON shapes.

As with the other compatibility endpoints, a request is translated exactly where possible. If only scores would differ, it runs and a `Warning: 299` header and `responseHeader.warnings` say what was approximated. Anything else fails with `400` in Solr's error shape, with a metadata entry naming `unsupported_feature`. Parameters that only tune performance or routing (`cache`, `cost`, `facet.method`, `distrib`, `shards.*`, `indent`) are accepted silently. Other parameters WarpSearch does not read are reported as warnings.

## Connect

Create an API key in the [management console](/console/). Send it as `Authorization: Bearer ws_live_...`, `Authorization: ApiKey ws_live_...` or `X-API-Key`.

> **Not supported:** HTTP Basic authentication with a console API key as the password. SolrJ and other clients that default to Basic credentials need a custom header instead.

```bash
curl -s "https://api.warpsearch.io/compat/solr/books/select" \
  -H "Authorization: Bearer ws_live_..." \
  --data-urlencode "q=title:dune AND year:[1960 TO *]" \
  --data-urlencode "fq=genre:scifi" \
  --data-urlencode "rows=10" \
  --data-urlencode "fl=id,title,score"
```

pysolr takes a prepared `requests` session:

```python
import pysolr, requests

session = requests.Session()
session.headers["Authorization"] = "Bearer ws_live_..."

solr = pysolr.Solr(
    "https://api.warpsearch.io/compat/solr/books",
    session=session,
    always_commit=True,
)
results = solr.search("title:dune", rows=10)
```

In SolrJ, build the client on an HTTP client that adds the `Authorization` header to every request, and set the base URL to `https://api.warpsearch.io/compat/solr/books`. Query through the `select` handler (`QueryRequest` with the default path, or `/query`).

## Endpoints

| Endpoint | Methods | Notes |
|---|---|---|
| `/select`, `/query` | GET, POST | Search. Parameters or a JSON Request API body |
| `/get` | GET | Lookup by `id` or `ids`, with `fl` |
| `/update`, `/update/json` | POST | JSON documents, one document, or command objects |
| `/update/json/docs` | POST | One document. `split` and `f` mappings are rejected |
| `/schema/fields`, `/schema/uniquekey` | GET | Read only |
| `/admin/ping` | GET | Health check |

Role needs follow Solr's split: a viewer searches and gets, an editor also updates and commits, an admin also runs `optimize`.

## Query parsers and parameters

| Feature | Support |
|---|---|
| Standard (Lucene) `q` | Fields, `AND OR NOT`, `+ -`, phrases with slop, `^boost`, `^=score`, `~` fuzzy, wildcards, `/regex/`, ranges, Solr date math, `[* TO *]` |
| `df`, `q.op`, `defType`, `q.alt` | Exact |
| Local params and `$param` references | `{!type k=v}`, `_query_`, `_val_` |
| `fq` | Several filters, with `{!tag=t}`, `cache`, `cost` |
| `dismax` and `edismax` | `qf`, `tie`, `mm`, `pf`, `pf2`, `pf3`, `ps`, `qs`, `bq`, `bf`, `boost`. Same documents, summed field scores |
| `field`, `term`, `raw`, `terms`, `prefix`, `bool`, `simple` | Supported |
| `func`, `frange`, `boost` | Supported |
| `geofilt`, `bbox` | Exact. `score=` is rejected |
| `knn` | `{!knn f=embedding topK=10}[0.1, 0.2, ...]`, with `fq` filtering candidates first |
| `rq={!rerank ...}` | The rerank query is added as a boosted clause. Whole result set is rescored, not just the top `reRankDocs` |
| `sort` | Fields, `score`, functions, `geodist()`, `field(f,min\|max)`. Documents with no value sort last |
| `start`, `rows`, `fl` | Names, globs, `score`, `alias:field` |
| `timeAllowed`, `TZ`, `NOW` | Exact. A timeout sets `partialResults` |
| `wt` | `json` only |

Fields are the collection's fields. A field that is not mapped must carry a type suffix in its name such as `_t` for text, `_s` for string, `_i` for integer, `_f` for float, `_d` for double or `_dt` for date, as in the Solr default configset. See [Schemas and field types](schemas-and-fields.md). Queries on the catch-all `_text_` field need a real field behind it; set the collection's default fields instead.

Function queries support arithmetic, `log`, `ln`, `exp`, `pow`, `sqrt`, `abs`, `min`, `max`, `floor`, `ceil`, `recip`, `linear`, `map`, `def`, `field`, `if`, comparisons, logic, `ms()` and `geodist()`. `rint` rounds half up, where Solr rounds half to even, and warns.

> **Not supported:** term and index statistics in functions (`tf`, `idf`, `termfreq`, `docfreq`, `norm`, `maxdoc`), `query()`, `ord`, `scale`, trigonometric functions, string distance, and string literals. `{!ltr}`, `maxscore`, join, graph and block-join parsers, and `{!collapse}`.

> **Coming soon:** exact `dismax` and `edismax` scoring (the maximum across fields instead of the sum) and highlighting.

## Faceting and statistics

Classic facets and the JSON Facet API both work.

| Feature | Support |
|---|---|
| `facet.field` | `facet.limit`, `facet.mincount`, `facet.missing`, `facet.offset`, per-field `f.<field>.*`, `{!key ex}` exclusions |
| `facet.sort=index` | Exact, up to your plan's facet size. Fails if the field has more values than that |
| `facet.query`, `facet.interval` | Exact |
| `facet.range` | `start`, `end`, `gap` as number or date math, `hardend`, `other`, `include`, `mincount` |
| `facet.pivot` | Nested facets with per-bucket stats. Levels must be string or id fields |
| `stats.field` | `min max sum count missing sumOfSquares mean stddev percentiles`, tags and exclusions |
| JSON Facet API | `terms`, `query`, `range`, `domain` (`filter`, `query`, `excludeTags`), nested `facet`, `sort`, `numBuckets`, `allBuckets`, statistics such as `avg`, `sum`, `unique`, `hll`, `percentile` |
| `group.field`, `group.query` | One field sort, `group.limit`, `group.offset`, `group.format=simple`, `group.main`, `group.ngroups` (approximate, with a warning) |

Counts such as `unique()`, `countDistinct`, `numBuckets` and `group.ngroups` are approximate, with a warning where Solr would be exact. On numeric and date fields, zero-count terms are not listed, so `facet.mincount=0` behaves like `1` with a warning.

> **Not supported:** `facet.prefix`, `facet.contains`, `facet.matches`, `facet.excludeTerms`, heatmaps, `relatedness()`, `uniqueBlock()`, sub-facets under numeric or text facets, and sorting facets by derived or sketch statistics. Grouping by relevance (the default sort), several sort keys, `group.func`, `group.facet` and `group.truncate` are rejected, so set an explicit field `sort` when you use `group=true`.

## Updating documents

Send a JSON array of documents, a single document, or command objects:

```bash
curl -s "https://api.warpsearch.io/compat/solr/books/update?commit=true" \
  -H "Authorization: Bearer ws_live_..." \
  -H "Content-Type: application/json" \
  -d '[{"id": "1", "title_t": "Dune", "year_i": 1965}]'
```

```json
{
  "add": {"doc": {"id": "2", "title_t": "Dune Messiah"}},
  "delete": {"id": "1"},
  "commit": {}
}
```

Supported: `add`, `delete` by id, `commit`, `softCommit`, `commitWithin`, `overwrite=false` and `optimize`. A document with an existing `id` replaces the old one. If a document in a request fails, the request fails after the earlier documents were written, as in Solr without a tolerant update chain.

> **Not supported:** delete by query, atomic and in-place updates (`set`, `inc`, `add`, `remove`), `_version_` concurrency, child documents, XML, CSV and javabin bodies, update processor chains, `rollback`, and document boosts.

## Schema and ping

`/schema/fields` and `/schema/uniquekey` are read only. Field types are reported as Solr names: `text_general`, `string`, `plong`, `pfloat`, `pdouble`, `pdate`, `location` and `knn_vector`. There is no Schema API for adding fields. Create the collection and its mapping through the console, the REST API, or the Elasticsearch-compatible `PUT /{index}` call, then point Solr clients at it. `/admin/ping` returns a Solr-style ping response.

## Response shapes

Search responses follow Solr's JSON: `responseHeader` (with `status`, `QTime` and `warnings` when there are any), `response` with `numFound`, `start` and `docs`, and `facet_counts`, `facets`, `stats` and `grouped` blocks when you ask for them. `fl` aliases are applied.

## Not supported, summarized

> **Not supported:** highlighting (`hl` is accepted with a warning and returns no snippets), spellcheck, suggest, more like this, the terms component, term vectors, clustering, query elevation, `cursorMark` deep paging, `expand`, joins and nested documents, computed fields and document transformers in `fl` (`[docid]`, `[explain]`, `[child]`, `geodist()`), shape and polygon spatial queries, and `debug` output (it warns).

For features that have no Solr equivalent here, such as vector and hybrid search, use `{!knn}` or the other query languages. See [Vector search](vector-search.md), [Hybrid search](hybrid-search.md) and [Query languages](query-languages.md). For limits and error codes, see [Limits and errors](limits-and-errors.md). If you are migrating, see [Migrate from Elasticsearch, OpenSearch or Solr](migrate-from-elasticsearch-opensearch-solr.md).
