Model::search() that understands meaning

"Sleepy pets" finds the post about cats napping. Hybrid BM25 + kNN relevance with server-side embeddings, wired straight into Eloquent through Laravel Scout — and one Opensolr index serves all your models.

$ composer require opensolr/laravel-scout-opensolr

Scout, exactly as documented

Four lines of .env, and every Searchable model gets semantic search. No Solr configuration, no schema design, no embedding model.

.env

SCOUT_DRIVER=opensolr
OPENSOLR_EMAIL=you@example.com
OPENSOLR_API_KEY=your-api-key
OPENSOLR_INDEX=myapp__dense

Anywhere in your app

Post::search('budget dining')
    ->where('category', 'restaurants')
    ->paginate(15);

// hybrid BM25 + kNN, embedded
// server-side, real totals

Try it without an account

A public demo account, so the quickstart above runs before you decide anything.

export OPENSOLR_EMAIL=mcp@opensolr.com
export OPENSOLR_API_KEY=420b8b23e7b12dc8ab838932145a5065

The index mcp_demo_d1__dense is already loaded with 300 news articles, so search, filtering and grounded answers work the moment you connect. You also get the write path: create your own index on the account, ingest into it and query it. Deletion is not available on this shared key — no index here can be deleted or reconfigured by hand. Whatever you create is removed automatically after 3 days.

  • Anything you create there is deleted after 3 days. Automatically, without warning or export — indexes you created included.
  • The account is shared with everyone reading this page. Your index is visible to them and they can add documents to it, as you can to theirs. Nobody can delete or reconfigure an index here — that is switched off for this key — but never put anything real, private or client-owned in it.
  • The limits are per index, and deliberately small. 200 MB of bandwidth and 50 MB of disk per index. Bandwidth is the one you will hit first: it covers a demo, a tutorial and a proof of concept, and it will not carry an application.

When you want an index that is private, yours and still there next week, get your own key — the free plan is free forever, no card — and change the two variables above. Nothing else in your code changes.

The whole thing, as an open-source Laravel app

A complete application built on this driver, MIT-licensed, on GitHub and Packagist. Start a project from it with one command, or clone it and read it.

laravel-opensolr-search is a Laravel 13 application with a Vue 3 front end on Vite and exactly one search dependency: opensolr/laravel-scout-opensolr. No Solr client to configure, no schema, no embedding model. The API key stays on the server; the browser only talks to the app's own JSON endpoints.

  • Eloquent search through Scout. An Article model with the Searchable trait, twenty seeded articles, Article::search($q)->where('category', $c)->paginate(). “Sleepy pets” finds the piece about cats napping; the category becomes a Solr filter.
  • Search over the whole index. One call to embed_and_search: hybrid ranking, highlighting, spelling suggestions, facet counts, a “fresh results first” toggle, and the Search Tuning saved for the index applied automatically.
  • A grounded AI answer. An Ask AI button calls aiAnswer(): the platform retrieves the top hits for the question and writes an answer from them, shown next to its sources.
  • Built to be copied. Form request validation on every input, per-address rate limits, a Content Security Policy, highlights rendered as text segments rather than HTML, result links validated server-side, and a CI that runs the Composer and npm audits on every push.

It runs on the demo account above, out of the box: the index search reads the shared news index, and one artisan command creates a private index for your models.

composer create-project opensolr/laravel-opensolr-search my-search
cd my-search
php artisan opensolr:create-index
php artisan scout:import "App\Models\Article"
npm install && npm run build && php artisan serve

Why not Algolia / Meilisearch / Typesense?

The other Scout drivers charge for search features. This one comes with a whole managed Solr platform.

One index, all your models

Documents are scoped per model automatically (meta_model), so a single index — a single plan — covers Posts, Products, Users, everything. No per-collection pricing.

Semantic + keyword, fused

Hybrid scoring per document via Opensolr's native {!hybrid} Solr parser, with a tunable semantic↔lexical balance. Scout's where() / whereIn() map to Solr filters with full operator support.

A platform, not just an engine

Web crawler, analytics, backups, query elevation, the native Solr /select API with facets and highlighting — all included around your Scout index.

Also available for: LangChain · LlamaIndex · Haystack · MCP / AI Agents

Search with a photo

Read a picture into words, then run those words through the ordinary search. Opensolr reads the image three ways — what it depicts, the text printed on it, and any barcode or QR code — and nothing new is stored in Solr; the picture simply becomes a query.

use Opensolr\ScoutOpensolr\OpensolrClient;

$client = new OpensolrClient(config('scout-opensolr.email'), config('scout-opensolr.api_key'));

// What the picture reads as — labels, OCR text, barcodes. Accepts a path or raw bytes:
$read = $client->imageToWords('catalog__dense', storage_path('app/shelf-photo.jpg'));
// ['text' => 'red running shoe', 'mode' => 'clip',
//  'labels' => ['running shoe','sneaker'], 'codes' => ['0123456789012']]

// Feed the reading you want into the normal hybrid search:
$body = $client->embedAndSearch('catalog__dense', $read['text'], 5);
$docs = $body['response']['docs'];

Pick which reading drives the search by choosing the field — text (the engine's choice), labels (what it depicts), or codes (an exact barcode match). imageToText() returns the raw API response when you also want the thumbnails.

Your Search Tuning follows you

Relevance is configured once, in your Control Panel — not re-implemented in every codebase.

  • Saved per index: everything you set in Index Settings → Search Tuning (semantic↔lexical balance, field weights, minimum match, search mode, vector candidate pool, content quality boost) is stored with your index.
  • Applies automatically here: every search and every RAG answer from this integration runs through the same tuned pipeline as your hosted search page — change a slider in the Control Panel and the very next query uses it. No redeploy, no code change.
  • Overridable per call: tuning: ['search_mode' => 'keywords_required', 'fw_title' => 0.2, 'mm' => 'strict'] beats the saved settings for that one request. Defaults match the platform exactly when you send nothing.
  • Fresh Results Bias: pass fresh_bias to rank newer documents higher — scores are multiplied by a recency curve on creation_date. It re-orders and never filters: the hit count is unchanged and undated documents keep their place. Same control visitors get as the Fresh toggle on the hosted search page. Not to be confused with freshness_boost, which is a hard date window in days and does remove results.

Tested on our own production index

Every release runs a live end-to-end suite against real Opensolr infrastructure — no mocks.

  • Full write path through the async Data Ingestion queue: queued → server-side embeddings & enrichment → searchable, with metadata and id round-trips verified. Metadata lands in typed Solr fields; the suffixes and what they enable are in the Vector Search Schema Reference.
  • Real-corpus retrieval against a replica of opensolr.com's own search index: pure-semantic hits with zero keyword overlap („how do I get my data back after a disaster” → backup docs), cross-lingual queries (Romanian → English content), all hybrid modes and the full alpha range.
  • PDF ingestion via rtf:true: server-side text extraction and content-type detection, then semantic retrieval from the extracted content.
  • Grounded RAG answers: one call runs hybrid retrieval and feeds the top hits to the LLM (configurable docs/words, custom instruction) — verified with a question answerable only from the ingested PDF.
  • Scout specifics: real Eloquent models via Orchestra Testbench — searchable() through the queue, where() filters, pagination with real totals, unsearchable() removal.

Open source

This integration is free, MIT licensed and maintained in the open. Browse the code, open an issue, or sponsor the work that keeps it current.

Upgrade your app's search this afternoon

Free forever plan, no credit card. Create a vector-enabled index (us, de, fi) and point Scout at it.