Skip to main content

Introduction

Almost every application needs search. Whether your users are searching a knowledge base for relevant articles, exploring a product catalog, or asking natural-language questions against a corpus of documents, Laravel provides built-in tools to handle each of these scenarios — and you often don’t need any external services to get there.

Feature comparison

FeatureExternal serviceDescription
whereFullTextNone requiredNative full-text indexes on MariaDB / MySQL / PostgreSQL
whereVectorSimilarToNone (PostgreSQL + pgvector)Similarity search by meaning. Requires the AI SDK
RerankingAI providerRe-order any result set by semantic relevance
Laravel ScoutNone (database engine) / optionalAutomatic index sync for Eloquent models

While LIKE queries work well for simple substring matching, they don’t understand language. Full-text search uses specialised indexes that understand word boundaries, stemming, and relevance scoring, so the database can return the most relevant results first. Fast full-text search is built into MariaDB, MySQL, and PostgreSQL — no external search service is required.
Full-text search is supported by MariaDB, MySQL, and PostgreSQL.

Adding full-text indexes

To use full-text search, first add a full-text index to the columns you want to search. You may pass an array of columns to create a composite index that searches across multiple fields at once:
Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');
    $table->timestamps();

    $table->fullText(['title', 'body']); // composite index
});
On PostgreSQL you may specify a language configuration for the index, which controls how words are stemmed:
$table->fullText('body')->language('english');
For more information on creating indexes, consult the migrations documentation.

Running full-text queries

Once the index is in place, use the whereFullText query builder method to search against it. Laravel generates the appropriate SQL for your database driver — MATCH(...) AGAINST(...) on MariaDB and MySQL, and to_tsvector(...) @@ plainto_tsquery(...) on PostgreSQL:
$articles = Article::whereFullText('body', 'web developer')->get();
When using a composite full-text index, pass the same array of columns to whereFullText:
$articles = Article::whereFullText(
    ['title', 'body'], 'web developer'
)->get();
On MariaDB and MySQL, results are automatically ordered by relevance score. On PostgreSQL, whereFullText filters matching records but does not order them by relevance. If you need automatic relevance ordering on PostgreSQL, consider using Scout’s database engine, which handles this for you.
The orWhereFullText method adds a full-text search clause as an “or” condition. For complete details, consult the query builder documentation.
Full-text search relies on keyword matching — the words in the query must appear in the data. Semantic search takes a fundamentally different approach: it uses AI-generated vector embeddings to represent the meaning of text as arrays of numbers, and then finds results whose meaning is closest to the query. For example, a search for “best wineries in Napa Valley” can surface an article titled “Top Vineyards to Visit” — even though the words don’t overlap at all.
Vector search requires the Laravel AI SDK and is supported by PostgreSQL (requires the pgvector extension) and MongoDB (requires the Laravel MongoDB package). All Postgres databases on Laravel Cloud already have pgvector installed.

Generating embeddings

An embedding is a high-dimensional numeric array that represents the semantic meaning of a piece of text. You may generate embeddings using the toEmbeddings method on Laravel’s Stringable class:
use Illuminate\Support\Str;

$embedding = Str::of('Napa Valley has great wine.')->toEmbeddings();
To generate embeddings for multiple inputs at once — which requires only a single API call — use the Embeddings class:
use Laravel\Ai\Embeddings;

$response = Embeddings::for([
    'Napa Valley has great wine.',
    'Laravel is a PHP framework.',
])->generate();

$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
For more details on configuring embedding providers and customising dimensions, consult the AI SDK documentation.

Storing and indexing vectors

Define a vector column in your migration, specifying the number of dimensions that matches your embedding provider’s output (for example, 1536 for OpenAI’s text-embedding-3-small). Call index() to create an HNSW index, which dramatically speeds up similarity searches on large datasets:
Schema::ensureVectorExtensionExists(); // enable the pgvector extension

Schema::create('documents', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->vector('embedding', dimensions: 1536)->index();
    $table->timestamps();
});
On your Eloquent model, cast the vector column to array so that Laravel automatically handles conversion between PHP arrays and the database’s vector format:
protected function casts(): array
{
    return [
        'embedding' => 'array',
    ];
}

Querying by similarity

Once you have stored embeddings, use whereVectorSimilarTo to find similar records. This method compares the given embedding against stored vectors using cosine similarity, filters out results below the minSimilarity threshold, and orders results by relevance. The threshold is a value between 0.0 and 1.0, where 1.0 means the vectors are identical:
$documents = Document::query()
    ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
    ->limit(10)
    ->get();
As a convenience, when a plain string is given instead of an embedding array, Laravel will automatically generate the embedding for you. This means you can pass the user’s search query directly:
$documents = Document::query()
    ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
    ->limit(10)
    ->get();
For lower-level control, the whereVectorDistanceLessThan, selectVectorDistance, and orderByVectorDistance methods are also available. For complete details, consult the query builder documentation and the AI SDK documentation.

Reranking

Reranking is a technique where an AI model reorders a set of results by how semantically relevant each result is to a given query. Unlike vector search, which requires pre-computed stored embeddings, reranking works on any collection of text — it takes raw content and a query as input and returns the items sorted by relevance. Reranking is especially powerful as a second stage after a fast initial retrieval step. Use full-text search to quickly narrow thousands of records down to the top 50 candidates, then use reranking to put the most relevant results at the top. This “retrieve then rerank” pattern gives you both speed and semantic accuracy.
use Laravel\Ai\Reranking;

$response = Reranking::of([
    'Django is a Python web framework.',
    'Laravel is a PHP web application framework.',
    'React is a JavaScript library for building user interfaces.',
])->rerank('PHP frameworks');

$response->first()->document; // "Laravel is a PHP web application framework."
Laravel collections also have a rerank macro that accepts a field name (or closure) and a query, making it easy to rerank Eloquent results:
$articles = Article::all()
    ->rerank('body', 'Laravel tutorials');
For complete details on configuring reranking providers and available options, consult the AI SDK documentation.

Laravel Scout

The search techniques described above are all query builder methods that you call directly in your code. Laravel Scout takes a different approach: it provides a Searchable trait that you add to your Eloquent models, and Scout automatically keeps your search indexes in sync as records are created, updated, and deleted.

Database engine

Scout’s built-in database engine performs full-text and LIKE searches against your existing database — no external service or extra infrastructure required. Add the Searchable trait to your model and define a toSearchableArray method that returns the columns you want to be searchable. You may use PHP attributes to control the search strategy for each column:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;
use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;

    #[SearchUsingPrefix(['id'])]
    #[SearchUsingFullText(['title', 'body'])]
    public function toSearchableArray(): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
        ];
    }
}
AttributeSearch strategy
SearchUsingFullTextUses the database’s full-text index (MATCH...AGAINST / to_tsvector)
SearchUsingPrefixPrefix match (example%)
NoneWildcard on both sides (%example%)
Before specifying that a column should use full-text query constraints, ensure that the column has been assigned a full-text index.
Once the trait is added, search your model using Scout’s search method. Scout’s database engine automatically orders results by relevance, even on PostgreSQL:
$articles = Article::search('Laravel')->get();

Third-party engines

Scout also supports third-party search engines such as Algolia, Meilisearch, and Typesense. These dedicated search services offer advanced features like typo tolerance, faceted filtering, geo-search, and custom ranking rules. Since Scout provides a unified API across all of its drivers, switching from the database engine to a third-party engine requires minimal code changes.
Many applications never need an external search engine. The built-in techniques described on this page cover the vast majority of use cases.
For complete details on configuring Scout and third-party engines, consult the Laravel Scout guide.

Combining techniques

The search techniques described on this page are not mutually exclusive — combining them often produces the best results.

Full-text retrieval + reranking

Use full-text search to quickly narrow a large dataset down to a candidate set, then apply reranking to sort those candidates by semantic relevance. This gives you the speed of database-native full-text search with the accuracy of AI-powered relevance scoring:
$articles = Article::query()
    ->whereFullText('body', $request->input('query'))
    ->limit(50)
    ->get()
    ->rerank('body', $request->input('query'), limit: 10);

Vector search + traditional filters

Combine vector similarity with standard where clauses to scope semantic search to a subset of records. This is useful when you need meaning-based search but want to restrict results by ownership, category, or any other attribute:
$documents = Document::query()
    ->where('team_id', $user->team_id)
    ->whereVectorSimilarTo('embedding', $request->input('query'))
    ->limit(10)
    ->get();

Laravel Scout

Complete guide to automatic index syncing with the Searchable trait.

Laravel AI SDK

Configure vector embeddings and reranking with the AI SDK.

Query Builder

Detailed reference for whereFullText, whereVectorSimilarTo, and more.

Migrations

How to create full-text indexes and vector columns.
Last modified on June 27, 2026