Skip to content

Commit 3e9959f

Browse files
[6.x] Improve search indexing performance (#13126)
1 parent 64f3e0e commit 3e9959f

17 files changed

Lines changed: 369 additions & 81 deletions

File tree

config/search.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,31 @@
7979
'fields' => ['title'],
8080
],
8181

82+
/*
83+
|--------------------------------------------------------------------------
84+
| Indexing Queue
85+
|--------------------------------------------------------------------------
86+
|
87+
| Here you may configure the queue name and connection used when indexing
88+
| documents.
89+
|
90+
*/
91+
92+
'queue' => env('STATAMIC_SEARCH_QUEUE'),
93+
94+
'queue_connection' => env('STATAMIC_SEARCH_QUEUE_CONNECTION'),
95+
96+
/*
97+
|--------------------------------------------------------------------------
98+
| Chunk Size
99+
|--------------------------------------------------------------------------
100+
|
101+
| Here you can configure the chunk size used when indexing documents.
102+
| The higher you make it, the more memory it will use, but the quicker
103+
| the indexing process will be.
104+
|
105+
*/
106+
107+
'chunk_size' => 100,
108+
82109
];

src/Search/Algolia/Index.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public function search($query)
3939
return (new Query($this))->query($query);
4040
}
4141

42-
protected function insertDocuments(Documents $documents)
42+
public function insertDocuments(Documents $documents)
4343
{
4444
$documents = $documents->map(function ($item, $id) {
4545
$item['objectID'] = $id;

src/Search/Comb/Index.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function delete($document)
9999
$this->save($data);
100100
}
101101

102-
protected function insertDocuments(Documents $documents)
102+
public function insertDocuments(Documents $documents)
103103
{
104104
try {
105105
$data = $this->data();

src/Search/Index.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use Closure;
66
use Statamic\Contracts\Search\Searchable;
7-
use Statamic\Support\Arr;
87
use Statamic\Support\Str;
98

109
abstract class Index
@@ -20,7 +19,7 @@ abstract public function delete($document);
2019

2120
abstract public function exists();
2221

23-
abstract protected function insertDocuments(Documents $documents);
22+
abstract public function insertDocuments(Documents $documents);
2423

2524
abstract protected function deleteIndex();
2625

@@ -84,20 +83,27 @@ public function ensureExists()
8483

8584
public function insert($document)
8685
{
87-
return $this->insertMultiple(Arr::wrap($document));
86+
return $this->insertMultiple(collect($document));
8887
}
8988

9089
public function insertMultiple($documents)
9190
{
92-
$documents = (new Documents($documents))->mapWithKeys(function (Searchable $item) {
93-
return [$item->getSearchReference() => $this->searchables()->fields($item)];
94-
});
95-
96-
$this->insertDocuments($documents);
91+
$documents
92+
->chunk(config('statamic.search.chunk_size'))
93+
->each(fn ($documents) => InsertMultipleJob::dispatch(
94+
name: Str::beforeLast($this->name, '_'),
95+
locale: $this->locale,
96+
documents: $documents
97+
));
9798

9899
return $this;
99100
}
100101

102+
public function fields(Searchable $searchable)
103+
{
104+
return $this->searchables()->fields($searchable);
105+
}
106+
101107
public function shouldIndex($searchable)
102108
{
103109
return $this->searchables()->contains($searchable);

src/Search/InsertMultipleJob.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Statamic\Search;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Illuminate\Foundation\Queue\Queueable;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Support\LazyCollection;
9+
use Statamic\Contracts\Search\Searchable;
10+
use Statamic\Facades\Search;
11+
use Statamic\Search\Searchables\Providers;
12+
use Statamic\Support\Str;
13+
14+
class InsertMultipleJob implements ShouldQueue
15+
{
16+
use Queueable;
17+
18+
/**
19+
* Create a new job instance.
20+
*/
21+
public function __construct(
22+
public string $name,
23+
public ?string $locale,
24+
public Collection|LazyCollection $documents,
25+
) {
26+
$this->onConnection($connection = config('statamic.search.queue_connection', config('queue.default')));
27+
$this->onQueue(config('statamic.search.queue', config("queue.connections.{$connection}.queue")));
28+
}
29+
30+
/**
31+
* Execute the job.
32+
*/
33+
public function handle(): void
34+
{
35+
$providers = app(Providers::class);
36+
$index = Search::index($this->name, $this->locale);
37+
38+
$documents = $this->documents
39+
->groupBy(fn ($document) => explode('::', $document)[0])
40+
->flatMap(function ($documents, $prefix) use ($providers) {
41+
return $providers
42+
->getByPrefix($prefix)
43+
->find($documents->map(fn ($reference) => Str::after($reference, '::'))->all())
44+
->all();
45+
})
46+
->mapWithKeys(function (Searchable $item) use ($index) {
47+
return [$item->getSearchReference() => $index->fields($item)];
48+
});
49+
50+
$index->insertDocuments(new Documents($documents));
51+
}
52+
}

src/Search/Null/NullIndex.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function exists()
2222
return true;
2323
}
2424

25-
protected function insertDocuments(Documents $documents)
25+
public function insertDocuments(Documents $documents)
2626
{
2727
//
2828
}

src/Search/Search.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function updateWithinIndexes(Searchable $searchable)
5151
$exists = $index->exists();
5252

5353
if ($shouldIndex && $exists) {
54-
$index->insert($searchable);
54+
$index->insert($searchable->getSearchReference());
5555
} elseif ($shouldIndex && ! $exists) {
5656
$index->update();
5757
} elseif ($exists) {

src/Search/Searchables/Assets.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ public function provide(): Collection
2626
: AssetCollection::make($this->keys)
2727
->flatMap(fn ($key) => Asset::whereContainer($key));
2828

29-
return $assets->filter($this->filter())->values();
29+
// TODO: query scope support?
30+
31+
return $assets->filter($this->filter())->values()->map->reference();
3032
}
3133

3234
public function contains($searchable): bool

src/Search/Searchables/Entries.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,19 @@ public function provide(): Collection|LazyCollection
3131
$query->where('site', $site);
3232
}
3333

34-
return $query->lazy(100)->filter($this->filter())->values();
34+
$this->applyQueryScope($query);
35+
36+
if ($this->hasFilter()) {
37+
return $query
38+
->lazy(config('statamic.search.chunk_size'))
39+
->filter($this->filter())
40+
->values()
41+
->map->reference();
42+
}
43+
44+
$query->where('status', 'published');
45+
46+
return $query->pluck('reference');
3547
}
3648

3749
public function contains($searchable): bool
@@ -48,7 +60,17 @@ public function contains($searchable): bool
4860
return false;
4961
}
5062

51-
return $this->filter()($searchable);
63+
if ($this->hasFilter()) {
64+
return $this->filter()($searchable);
65+
}
66+
67+
$query = Entry::query()
68+
->where('status', 'published')
69+
->where('id', $searchable->id());
70+
71+
$this->applyQueryScope($query);
72+
73+
return $query->exists();
5274
}
5375

5476
public function find(array $ids): Collection

src/Search/Searchables/Provider.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace Statamic\Search\Searchables;
44

5+
use Statamic\Facades\Scope;
56
use Statamic\Facades\Search;
67
use Statamic\Search\Index;
78
use Statamic\Search\ProvidesSearchables;
9+
use Statamic\Support\Arr;
810

911
abstract class Provider implements ProvidesSearchables
1012
{
@@ -55,6 +57,20 @@ protected function usesWildcard()
5557
return in_array('*', $this->keys);
5658
}
5759

60+
protected function applyQueryScope($query)
61+
{
62+
if (! $scope = $this->index->config()['query_scope'] ?? null) {
63+
return;
64+
}
65+
66+
Scope::find($scope)->apply($query, []);
67+
}
68+
69+
protected function hasFilter()
70+
{
71+
return Arr::has($this->index->config(), 'filter');
72+
}
73+
5874
protected function filter()
5975
{
6076
$filter = $this->index->config()['filter'] ?? null;

0 commit comments

Comments
 (0)