PHPackages                             namoshek/laravel-scout-database - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. namoshek/laravel-scout-database

ActiveLibrary[Search &amp; Filtering](/categories/search)

namoshek/laravel-scout-database
===============================

A generic Laravel Scout driver which performs full-text search on indexed model data using an SQL database as storage backend. Indexed data is stored in normalized form, allowing efficient search.

v2.7.1(1y ago)1967.7k↑85.7%7[1 issues](https://github.com/Namoshek/laravel-scout-database/issues)MITPHPPHP ^8.0CI passing

Since Apr 14Pushed 5mo ago1 watchersCompare

[ Source](https://github.com/Namoshek/laravel-scout-database)[ Packagist](https://packagist.org/packages/namoshek/laravel-scout-database)[ Docs](https://github.com/Namoshek/laravel-scout-database)[ RSS](/packages/namoshek-laravel-scout-database/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (8)Versions (27)Used By (0)

Laravel Scout driver for SQL Database based Search Indexing
===========================================================

[](#laravel-scout-driver-for-sql-database-based-search-indexing)

[![Latest Version on Packagist](https://camo.githubusercontent.com/8db0a07f83cb7c7b07a5b9d44421262b1a9359013d073094bf004db802892d25/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e616d6f7368656b2f6c61726176656c2d73636f75742d64617461626173652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/namoshek/laravel-scout-database)[![Total Downloads](https://camo.githubusercontent.com/051030b22487a8fa9601b6c287d3becd101d94d53d288788f919354d8383d683/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e616d6f7368656b2f6c61726176656c2d73636f75742d64617461626173652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/namoshek/laravel-scout-database)[![Tests](https://github.com/Namoshek/laravel-scout-database/workflows/Tests/badge.svg)](https://github.com/Namoshek/laravel-scout-database/actions?query=workflow%3ATests)[![Quality Gate Status](https://camo.githubusercontent.com/1b6758b4b9af2b60ead8e1521af4fa0e3f8dc98f80b18505570fa20576cff1f5/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6e616d6f7368656b5f6c61726176656c2d73636f75742d6461746162617365266d65747269633d616c6572745f737461747573)](https://sonarcloud.io/dashboard?id=namoshek_laravel-scout-database)[![Maintainability Rating](https://camo.githubusercontent.com/2e35538e6a0e47d29cf2e885dff74465f959f84c84062a7a49d215c57c83c395/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6e616d6f7368656b5f6c61726176656c2d73636f75742d6461746162617365266d65747269633d7371616c655f726174696e67)](https://sonarcloud.io/dashboard?id=namoshek_laravel-scout-database)[![Reliability Rating](https://camo.githubusercontent.com/a8491fa7bc7dea55486bf3a80d0daade39f14eb5313f0aaa87bc58d24131e9e8/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6e616d6f7368656b5f6c61726176656c2d73636f75742d6461746162617365266d65747269633d72656c696162696c6974795f726174696e67)](https://sonarcloud.io/dashboard?id=namoshek_laravel-scout-database)[![Security Rating](https://camo.githubusercontent.com/3fccdcf71e611953006c11f2b3817fb6b26798aebf4e0ac612dc56543c85d05b/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6e616d6f7368656b5f6c61726176656c2d73636f75742d6461746162617365266d65747269633d73656375726974795f726174696e67)](https://sonarcloud.io/dashboard?id=namoshek_laravel-scout-database)[![Vulnerabilities](https://camo.githubusercontent.com/7d0a68403211766d197ba040b837cca9d868049aa20375096f0649584b980ae4/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d6e616d6f7368656b5f6c61726176656c2d73636f75742d6461746162617365266d65747269633d76756c6e65726162696c6974696573)](https://sonarcloud.io/dashboard?id=namoshek_laravel-scout-database)[![License](https://camo.githubusercontent.com/f9e065a52fb954cd020259262cb6b1a382080bc6c1683dbfc11bfcb54096c147/68747470733a2f2f706f7365722e707567782e6f72672f6e616d6f7368656b2f6c61726176656c2d73636f75742d64617461626173652f6c6963656e7365)](https://packagist.org/packages/namoshek/laravel-scout-database)

This package provides a generic Laravel Scout driver which performs full-text search on indexed model data using an SQL database as storage backend. Indexed data is stored in normalized form, allowing efficient and fuzzy search which does not require a full and/or exact match.

This driver is an alternative to [`teamtnt/laravel-scout-tntsearch-driver`](https://github.com/teamtnt/laravel-scout-tntsearch-driver). The primary difference is that this driver provides fewer features (like geo search). Instead, it works with all database systems supported by Laravel itself (which are basically all PDO drivers). Also, the search algorithm is slightly different.

All tests are run through GitHub Actions for PHP 8.0, 8.1 and 8.2 on the following database systems:

- SQLite 3
- MySQL 8.0
- PostgreSQL 13.1
- SQL Server 2017

Actual limitations regarding supported database systems are mostly related to the use of *Common Table Expression* using [staudenmeir/laravel-cte](https://github.com/staudenmeir/laravel-cte). Please make sure your database system is supported before using the package, or you might run into database errors.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require namoshek/laravel-scout-database
```

After installing the package, the configuration file as well as the migrations need to be published:

```
php artisan vendor:publish --provider="Namoshek\Scout\Database\ScoutDatabaseServiceProvider" --tag="config"
php artisan vendor:publish --provider="Namoshek\Scout\Database\ScoutDatabaseServiceProvider" --tag="migrations"
```

If you would like to use a different table prefix than `scout_` for the tables created by this package, you should change the configuration as well as the copied migrations accordingly. When you have done so, you can then apply the database migrations:

```
php artisan migrate
```

### Upgrading from `v0.x` to `v1.x`

[](#upgrading-from-v0x-to-v1x)

#### Migrations

[](#migrations)

With the new version, the database schema has changed and new migrations need to be published using:

```
php artisan vendor:publish --provider="Namoshek\Scout\Database\ScoutDatabaseServiceProvider" --tag="migrations"
```

The same hint as mentioned above in the *Installation* section applies to the newly published migrations as well.

#### Config

[](#config)

The configuration has been reduced slightly and you might want to compare the new configuration file with the old one to remove obsolete settings. Skipping this part has no negative impact on the performance of the Scout driver, though.

#### Commands

[](#commands)

The `\Namoshek\Scout\Database\Commands\CleanWordsTable::class` command has been removed and you should un-schedule it, if you added it previously.

#### Noteworthy

[](#noteworthy)

Most occurrences of `protected` fields and methods have been changed to `private` to simplify development in regard to backwards-compatibility breaking changes in the future. If you have not been actively overriding parts of the implementation, this does not affect you at all.

Configuration
-------------

[](#configuration)

In order to instruct Scout to use the driver provided by this package, you need to change the `driver` option in `config/scout.php`to `database`. If you did not change the Scout configuration file, you can also set the `SCOUT_DRIVER` environment variable to `database` instead.

All available configuration options of the package itself can be found in `config/scout-database.php`. The options are described thoroughly in the file itself. By default, the package uses the [`UnicodeTokenizer`](src/Tokenizer/UnicodeTokenizer.php)and the [`PorterStemmer`](src/Stemmer/PorterStemmer.php) which is suitable for the English language. The search adds a trailing wildcard to the last token and not all search terms need to be found in order for a document to show up in the results (there must be at least one match though).

You may also add a wildcard to each search token by enabling `wildcard_all_tokens` in the config file altough this is not recommended for performance reasons.

*A basic installation most likely does not require you to change any of these settings. Just to make sure, you should have a look at the `connection` option though. If you want to change this, do so before running the migrations or the tables will be created using the wrong database connection.*

### Supported Tokenizers

[](#supported-tokenizers)

Currently, only a [`UnicodeTokenizer`](src/Tokenizer/UnicodeTokenizer.php) is available. It will split strings at any character which is neither a letter, nor a number according to the `\p{L}` and `\p{N}` regex patterns. This means that dots, colons, dashes, whitespace, etc. are split criteria.

If you have different requirements for a tokenizer, you can provide your own implementation via the configuration. Just make sure it implements the [`Tokenizer`](src/Contracts/Tokenizer.php) interface.

### Supported Stemmers

[](#supported-stemmers)

Currently, all stemmers implemented by the [`wamania/php-stemmer`](https://github.com/wamania/php-stemmer) package are available. A wrapper class has been added for each of them:

- [`DanishStemmer`](src/Stemmer/DanishStemmer.php)
- [`DutchStemmer`](src/Stemmer/DutchStemmer.php)
- [`EnglishStemmer`](src/Stemmer/EnglishStemmer.php)
- [`FrenchStemmer`](src/Stemmer/FrenchStemmer.php)
- [`GermanStemmer`](src/Stemmer/GermanStemmer.php)
- [`ItalianStemmer`](src/Stemmer/ItalianStemmer.php)
- [`NorwegianStemmer`](src/Stemmer/NorwegianStemmer.php)
- [`NullStemmer`](src/Stemmer/NullStemmer.php) *(can be used to disable stemming)*
- [`PorterStemmer`](src/Stemmer/PorterStemmer.php) *(default, same as [`EnglishStemmer`](src/Stemmer/EnglishStemmer.php))*
- [`PortugueseStemmer`](src/Stemmer/PortugueseStemmer.php)
- [`RomanianStemmer`](src/Stemmer/RomanianStemmer.php)
- [`RussianStemmer`](src/Stemmer/RussianStemmer.php)
- [`SpanishStemmer`](src/Stemmer/SpanishStemmer.php)
- [`SwedishStemmer`](src/Stemmer/SwedishStemmer.php)

If you have different requirements for a stemmer, you can provide your own implementation via the configuration. Just make sure it implements the [`Stemmer`](src/Contracts/Stemmer.php) interface.

Usage
-----

[](#usage)

The package follows the available use cases described in the [official Scout documentation](https://laravel.com/docs/9.x/scout). Please be aware of the listed [limitations](#limitations) though.

### How does it work?

[](#how-does-it-work)

#### The Indexing

[](#the-indexing)

The search driver internally uses a single table, which contains terms and the association to documents. When indexing documents (i.e. adding or updating models in the search index) the engine will use the configured tokenizer to split the input of each column into tokens. The tokenizer configured by default simply splits inputs into words consisting of any unicode letter or number, which means any other character like `,`, `.`, `-`, `_`, `!`, `?`, `/`, whitespace and all other special characters are considered separators for the tokens and will be removed by the tokenizer. This way such characters will never end up in the search index itself.

After the inputs have been tokenized, each token (and at this point we actually expect our tokens to be words) is run through the configured stemmer to retrieve the stem (i.e. *root word*). Performing this action allows us to search for similar words later. The [`PorterStemmer`](src/Stemmer/PorterStemmer.php) for example will produce `intellig` as output for both `intelligent` as well as `intelligence` as input. How this helps when searching will be clear in a moment.

Finally, the results of this process are stored in the database. The *index* table is filled with the results of the stemming process and the associations to the indexed models (model type and identifier). On top of that, for each row in the index, the database also contains the number of occurences in a document. We use this information for scoring within the search part of our engine.

#### The Search

[](#the-search)

When executing a search query, the same tokenizing and stemming process as used for indexing is applied to the search query string. The result of this process is a list of stems (or *root words*) which are then used to perform the actual search. Depending on the configuration of the package, the search will return documents which contain at least one or all of the stems. This is done by calculating a score for each match in the index based on the inverse document frequency (i.e. the ratio between indexed documents and documents containing one of the searched words), the term frequency (i.e. the number of occurrences of a search term within a document) and the term deviation (which is only relevant for the wildcard search). Returned are documents ordered by their score in descending order, until the desired limit is reached.

### Extending the Search Index

[](#extending-the-search-index)

It is possible to extend the search index table (`scout_index`) with custom columns. During indexing, these columns may be filled with custom content and during searching the searches can be scoped to these columns (exact match). This feature is particularly useful when working with a multi-tenancy application where the search index is used by multiple tenants.

#### Example Migration

[](#example-migration)

In our example, we add a mandatory `tenant_id` column to the search index.

```
return new class extends Migration {
    public function up(): void
    {
        Schema::table('scout_index', function (Blueprint $table) {
            $table->uuid('tenant_id');
        });
    }

    public function down(): void
    {
        Schema::table('scout_index', function (Blueprint $table) {
            $table->dropColumn(['tenant_id']);
        });
    }
};
```

#### Indexing Example

[](#indexing-example)

The `tenant_id` is added during indexing for each model:

```
class User extends Model
{
    public function toSearchableArray(): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'tenant_id' => new StandaloneField($this->tenant_id),
        ];
    }
}
```

#### Search Example

[](#search-example)

The `tenant_id` is filtered during search based on the `$tenantId`, which may for example be taken from the HTTP request:

```
User::search('Max Mustermann')
    ->where('tenant_id', $tenantId)
    ->get();
```

Limitations
-----------

[](#limitations)

Obviously, this package does not provide a search engine which (even remotely) brings the performance and quality a professional search engine like Elasticsearch offers. This solution is meant for smaller to medium-sized projects which are in need of a rather simple-to-setup solution.

Also worth noting, the following Scout features are currently not implemented:

- Soft Deletes
- Search custom index using `User::search('Mustermann')->within('users_without_admins')`
- Search with custom order using `User::search('Musterfrau')->orderBy('age', 'desc')`
    - Implementing this feature would be difficult in combination with the scoring algorithm. Only the result of the database query could be ordered, while this could then lead to issues with pagination.

### Known Issues

[](#known-issues)

One issue with this search engine is that it can lead to issues if multiple queue workers work on the indexing of a single document concurrently (database will deadlock). To circumvent this issue, a the number of attempts used for transactions is configurable. By default, each transaction is tried a maximum of three times if a deadlock (or any other error) occurs.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

51

—

FairBetter than 96% of packages

Maintenance60

Regular maintenance activity

Popularity41

Moderate usage in the ecosystem

Community16

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 78.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~72 days

Recently: every ~105 days

Total

26

Last Release

416d ago

Major Versions

v0.6.0 → v1.0.0-rc12020-12-31

v1.0.0 → v2.0.02021-05-15

PHP version history (3 changes)v0.1PHP ^7.2

v1.0.0-rc1PHP ^7.3|^8.0

v2.1.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/38b0b9d27b847d8b05cde5b45dd0a4ae2bd6b798637e433c4c64cc865e1b8341?d=identicon)[Namoshek](/maintainers/Namoshek)

---

Top Contributors

[![Namoshek](https://avatars.githubusercontent.com/u/8877609?v=4)](https://github.com/Namoshek "Namoshek (61 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (12 commits)")[![bernhard-efler](https://avatars.githubusercontent.com/u/1978420?v=4)](https://github.com/bernhard-efler "bernhard-efler (2 commits)")[![marijoo](https://avatars.githubusercontent.com/u/360736?v=4)](https://github.com/marijoo "marijoo (2 commits)")[![dependabot-preview[bot]](https://avatars.githubusercontent.com/in/2141?v=4)](https://github.com/dependabot-preview[bot] "dependabot-preview[bot] (1 commits)")

---

Tags

laravellaravel-scoutlaravel-scout-driversearch-enginesql-databaselaravelscout

###  Code Quality

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/namoshek-laravel-scout-database/health.svg)

```
[![Health](https://phpackages.com/badges/namoshek-laravel-scout-database/health.svg)](https://phpackages.com/packages/namoshek-laravel-scout-database)
```

###  Alternatives

[algolia/scout-extended

Scout Extended extends Laravel Scout adding algolia-specific features

4186.3M6](/packages/algolia-scout-extended)[teamtnt/laravel-scout-tntsearch-driver

Driver for Laravel Scout search package based on https://github.com/teamtnt/tntsearch

1.1k2.5M28](/packages/teamtnt-laravel-scout-tntsearch-driver)[jeroen-g/explorer

Next-gen Elasticsearch driver for Laravel Scout.

397612.3k](/packages/jeroen-g-explorer)[vanry/laravel-scout-tntsearch

包含中文分词的 Laravel Scout TNTSearch 驱动，支持 scws, phpanalysis 和 jieba 分词。

17811.8k1](/packages/vanry-laravel-scout-tntsearch)[romanstruk/manticore-scout-engine

Laravel Manticore Scout Engine

4818.1k](/packages/romanstruk-manticore-scout-engine)[baijunyao/laravel-scout-elasticsearch

Elasticsearch Driver for Laravel Scout

8023.7k1](/packages/baijunyao-laravel-scout-elasticsearch)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
