Skip to content

Commit cf92e2d

Browse files
Merge pull request #12 from ginkelsoft-development/develop
docs: expand README with auto-indexing, attributes, and Elasticsearch…
2 parents 7d9dcd1 + db9f52e commit cf92e2d

File tree

6 files changed

+216
-82
lines changed

6 files changed

+216
-82
lines changed

README.md

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
[![Latest Version on Packagist](https://img.shields.io/packagist/v/ginkelsoft/laravel-encrypted-search-index.svg?style=flat-square)](https://packagist.org/packages/ginkelsoft/laravel-encrypted-search-index)
55
[![Total Downloads](https://img.shields.io/packagist/dt/ginkelsoft/laravel-encrypted-search-index.svg?style=flat-square)](https://packagist.org/packages/ginkelsoft/laravel-encrypted-search-index)
66
[![License](https://img.shields.io/github/license/ginkelsoft-development/laravel-encrypted-search-index.svg?style=flat-square)](LICENSE.md)
7-
[![Laravel](https://img.shields.io/badge/Laravel-8--12-brightgreen?style=flat-square\&logo=laravel)](https://laravel.com)
8-
[![PHP](https://img.shields.io/badge/PHP-8.1%20--%208.4-blue?style=flat-square\&logo=php)](https://php.net)
7+
[![Laravel](https://img.shields.io/badge/Laravel-8--12-brightgreen?style=flat-square&logo=laravel)](https://laravel.com)
8+
[![PHP](https://img.shields.io/badge/PHP-8.1%20--%208.4-blue?style=flat-square&logo=php)](https://php.net)
9+
[![Elasticsearch](https://img.shields.io/badge/Search-DB%20or%20Elasticsearch-ff9900?style=flat-square&logo=elasticsearch)](#elasticsearch-integration)
910

1011
## Overview
1112

@@ -39,7 +40,8 @@ This package removes that trade-off by introducing a **detached searchable index
3940
* **High scalability** — Efficient for millions of records through database indexing or Elasticsearch.
4041
* **Elasticsearch integration** — Optionally store and query search tokens directly in an Elasticsearch index.
4142
* **Laravel-native integration** — Works directly with Eloquent models, query scopes, and model events.
42-
43+
* **Automatic field detection** — Automatically indexes fields that use an encrypted cast when enabled.
44+
* **Fine-grained configuration** — Supports attributes (`#[EncryptedSearch]`) and `$encryptedSearch` arrays for per-field behavior.
4345
---
4446

4547
## How It Works
@@ -124,6 +126,9 @@ curl -X GET "http://localhost:9200/encrypted_search/_search?pretty" \
124126
}'
125127
```
126128

129+
Both the database and Elasticsearch drivers use the same search scopes —
130+
your application code remains identical regardless of which backend is active.
131+
127132
For prefix-based queries, you can match multiple tokens:
128133

129134
```bash
@@ -166,6 +171,8 @@ php artisan vendor:publish --provider="Ginkelsoft\EncryptedSearch\EncryptedSearc
166171
php artisan migrate
167172
```
168173

174+
If you plan to use the Elasticsearch integration, make sure an Elasticsearch instance (version **8.x or newer**) is running and accessible at the host defined in your `.env` file.
175+
169176
Then add a unique pepper to your `.env` file:
170177

171178
```
@@ -196,21 +203,20 @@ return [
196203

197204
### Model Setup
198205

206+
If `auto_index_encrypted_casts` is enabled in the configuration (default: **true**),
207+
all model fields that use an `encrypted:` cast will be automatically indexed for exact search,
208+
even if they are not explicitly listed in `$encryptedSearch`.
209+
210+
You can also use PHP attributes to control search behavior per field:
211+
199212
```php
200-
use Illuminate\Database\Eloquent\Model;
201-
use Ginkelsoft\EncryptedSearch\Traits\HasEncryptedSearchIndex;
213+
use Ginkelsoft\EncryptedSearch\Attributes\EncryptedSearch;
202214

203215
class Client extends Model
204216
{
205-
use HasEncryptedSearchIndex;
206-
207-
protected array $encryptedSearch = [
208-
'first_names' => ['exact' => true, 'prefix' => true],
209-
'last_names' => ['exact' => true, 'prefix' => true],
210-
'bsn' => ['exact' => true],
211-
];
217+
#[EncryptedSearch(exact: true, prefix: true)]
218+
public string $last_names;
212219
}
213-
```
214220

215221
When a record is saved, searchable tokens are automatically generated in `encrypted_search_index` or synced to Elasticsearch.
216222

@@ -223,8 +229,17 @@ $clients = Client::encryptedExact('last_names', 'Vermeer')->get();
223229
// Prefix match
224230
$clients = Client::encryptedPrefix('first_names', 'Wie')->get();
225231
```
232+
Attributes always override global or $encryptedSearch configuration for the same field.
226233

227-
### Rebuilding the Index
234+
---
235+
236+
#### ✅ 3. **Configuration block (insert this before `'elasticsearch' => [...]`)**
237+
```php
238+
'auto_index_encrypted_casts' => true,
239+
240+
## Rebuilding or Syncing the Search Index
241+
This command automatically detects whether you are using the database or Elasticsearch driver,
242+
and rebuilds the appropriate index accordingly.
228243

229244
Rebuild indexes via Artisan:
230245

@@ -269,6 +284,16 @@ The package is continuously tested across all supported combinations using GitHu
269284

270285
---
271286

287+
## Troubleshooting
288+
289+
**ConnectionException (cURL error 7)**
290+
Ensure your Elasticsearch container or service is running and reachable at the configured `ELASTICSEARCH_HOST`.
291+
292+
**Missing index mappings**
293+
If you haven’t created the Elasticsearch index yet, initialize it manually:
294+
```bash
295+
curl -X PUT http://localhost:9200/encrypted_search
296+
272297
## License
273298

274299
MIT License

config/encrypted-search.php

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,82 @@
11
<?php
22

3+
/**
4+
* -----------------------------------------------------------------------------
5+
* Ginkelsoft Laravel Encrypted Search Index - Configuration
6+
* -----------------------------------------------------------------------------
7+
*
8+
* This configuration file defines how the encrypted search indexing system
9+
* behaves. It supports both local database indexing and Elasticsearch as
10+
* a backend. It also provides automatic detection of encrypted model casts,
11+
* customizable prefix depth, and peppering for secure token hashing.
12+
*
13+
* @package Ginkelsoft\EncryptedSearch
14+
*/
15+
316
return [
17+
418
/*
519
|--------------------------------------------------------------------------
620
| Search Pepper
721
|--------------------------------------------------------------------------
8-
| Een geheime “pepper” die wordt mee-gehasht met genormaliseerde tokens.
9-
| Dit voorkomt dat een gelekte index triviaal terug te rekenen is.
10-
| Zet dit in .env als SEARCH_PEPPER="random-string".
22+
|
23+
| A secret “pepper” value that is concatenated with the normalized text
24+
| before hashing. This ensures that even if the token index is leaked,
25+
| it cannot be reversed or correlated with plaintext values.
26+
|
27+
| Define this in your `.env` file, for example:
28+
| SEARCH_PEPPER="random-secret-string"
29+
|
1130
*/
1231
'search_pepper' => env('SEARCH_PEPPER', ''),
1332

1433
/*
1534
|--------------------------------------------------------------------------
16-
| Prefix token lengte
35+
| Maximum Prefix Depth
1736
|--------------------------------------------------------------------------
18-
| Maximaal aantal prefix-niveaus voor prefix-zoekopdrachten.
19-
| Bijv. "wietse" -> ["w","wi","wie"]
37+
|
38+
| The maximum number of prefix levels to generate for prefix-based search.
39+
| For example, the term “wietse” would generate:
40+
| ["w", "wi", "wie", "wiet", "wiets", "wietse"]
41+
|
42+
| Increasing this value improves search precision for short terms, but
43+
| slightly increases the number of stored tokens per record.
44+
|
2045
*/
2146
'max_prefix_depth' => 6,
22-
47+
48+
/*
49+
|--------------------------------------------------------------------------
50+
| Automatic Indexing of Encrypted Casts
51+
|--------------------------------------------------------------------------
52+
|
53+
| When enabled, the package automatically includes any model attributes
54+
| that use Laravel’s encrypted cast types (e.g. AsEncryptedString,
55+
| AsEncryptedArrayObject, etc.) in the search index.
56+
|
57+
| You can still override or refine behavior per field via:
58+
| - Attributes: #[EncryptedSearch(exact: true, prefix: false)]
59+
| - Model property: protected array $encryptedSearch
60+
|
61+
*/
62+
'auto_index_encrypted_casts' => true,
63+
64+
/*
65+
|--------------------------------------------------------------------------
66+
| Elasticsearch Integration
67+
|--------------------------------------------------------------------------
68+
|
69+
| Configure Elasticsearch as the backend for storing and querying
70+
| encrypted search tokens. When enabled, the package will skip all
71+
| database writes to `encrypted_search_index` and instead sync tokens
72+
| directly to Elasticsearch using the internal ElasticsearchService.
73+
|
74+
| Example environment configuration:
75+
| ENCRYPTED_SEARCH_ELASTIC_ENABLED=true
76+
| ELASTICSEARCH_HOST=http://localhost:9200
77+
| ELASTICSEARCH_INDEX=encrypted_search
78+
|
79+
*/
2380
'elasticsearch' => [
2481
'enabled' => env('ENCRYPTED_SEARCH_ELASTIC_ENABLED', false),
2582
'host' => env('ELASTICSEARCH_HOST', 'http://elasticsearch:9200'),

src/Attributes/EncryptedSearch.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Ginkelsoft\EncryptedSearch\Attributes;
4+
5+
use Attribute;
6+
7+
/**
8+
* Marks a model property as searchable in the encrypted search index.
9+
*
10+
* Example:
11+
* #[EncryptedSearch(exact: true, prefix: true)]
12+
* public string $first_name;
13+
*/
14+
#[Attribute(Attribute::TARGET_PROPERTY)]
15+
class EncryptedSearch
16+
{
17+
public function __construct(
18+
public bool $exact = true,
19+
public bool $prefix = false
20+
) {}
21+
22+
public function toArray(): array
23+
{
24+
return [
25+
'exact' => $this->exact,
26+
'prefix' => $this->prefix,
27+
];
28+
}
29+
}

0 commit comments

Comments
 (0)