1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models\Traits\Es;
7
8use App\Libraries\Elasticsearch\Es;
9use Log;
10
11trait BaseDbIndexable
12{
13 use BaseIndexable;
14
15 abstract public static function esIndexingQuery();
16
17 public static function esIndexIntoNew($batchSize = 1000, $name = null, callable $progress = null)
18 {
19 $newIndex = $name ?? static::esTimestampedIndexName();
20 Log::info("Creating new index {$newIndex}");
21 static::esCreateIndex($newIndex);
22
23 $options = [
24 'index' => $newIndex,
25 ];
26
27 static::esReindexAll($batchSize, 0, $options, $progress);
28
29 return $newIndex;
30 }
31
32 public static function esIndexName()
33 {
34 return $GLOBALS['cfg']['osu']['elasticsearch']['prefix'].(new static())->getTable();
35 }
36
37 public static function esSchemaFile()
38 {
39 return config_path('schemas/'.(new static())->getTable().'.json');
40 }
41
42 public static function esReindexAll($batchSize = 1000, $fromId = 0, array $options = [], callable $progress = null)
43 {
44 $dummy = new static();
45 $startTime = time();
46
47 $baseQuery = static::esIndexingQuery()->where($dummy->getKeyName(), '>', $fromId);
48 $count = 0;
49
50 $baseQuery->chunkById($batchSize, function ($models) use ($options, &$count, $progress) {
51 $actions = Es::generateBulkActions($models);
52
53 if ($actions !== []) {
54 $result = Es::getClient()->bulk([
55 'index' => $options['index'] ?? static::esIndexName(),
56 'body' => $actions,
57 'client' => ['timeout' => 0],
58 ]);
59
60 $count += count($result['items']);
61 }
62
63 Log::info(static::class." next: {$models->last()->getKey()}");
64 if ($progress) {
65 $progress($count);
66 }
67 });
68
69 $duration = time() - $startTime;
70 Log::info(static::class." Indexed {$count} records in {$duration} s.");
71 }
72
73 /**
74 * The value for routing.
75 * Override to provide a routing value; null by default.
76 *
77 * @return string|null
78 */
79 public function esRouting()
80 {
81 // null will be omitted when used as routing.
82 }
83
84 public function esDeleteDocument(array $options = [])
85 {
86 $document = array_merge([
87 'index' => static::esIndexName(),
88 'routing' => $this->esRouting(),
89 'id' => $this->getEsId(),
90 'client' => ['ignore' => 404],
91 ], $options);
92
93 return Es::getClient()->delete($document);
94 }
95
96 public function esIndexDocument(array $options = [])
97 {
98 if (!$this->esShouldIndex()) {
99 return $this->esDeleteDocument($options);
100 }
101
102 $document = array_merge([
103 'index' => static::esIndexName(),
104 'routing' => $this->esRouting(),
105 'id' => $this->getEsId(),
106 'body' => $this->toEsJson(),
107 ], $options);
108
109 return Es::getClient()->index($document);
110 }
111
112 public function esShouldIndex()
113 {
114 return true;
115 }
116
117 public function getEsId()
118 {
119 return $this->getKey();
120 }
121
122 abstract public function toEsJson();
123}