3 namespace BookStack\Entities\Tools;
5 use BookStack\Entities\EntityProvider;
6 use BookStack\Entities\Models\Entity;
7 use BookStack\Entities\Models\SearchTerm;
8 use Illuminate\Support\Collection;
16 protected $entityProvider;
18 public function __construct(EntityProvider $entityProvider)
20 $this->entityProvider = $entityProvider;
24 * Index the given entity.
26 public function indexEntity(Entity $entity)
28 $this->deleteEntityTerms($entity);
29 $terms = $this->entityToTermDataArray($entity);
30 SearchTerm::query()->insert($terms);
34 * Index multiple Entities at once.
36 * @param Entity[] $entities
38 public function indexEntities(array $entities)
41 foreach ($entities as $entity) {
42 $entityTerms = $this->entityToTermDataArray($entity);
43 array_push($terms, ...$entityTerms);
46 $chunkedTerms = array_chunk($terms, 500);
47 foreach ($chunkedTerms as $termChunk) {
48 SearchTerm::query()->insert($termChunk);
53 * Delete and re-index the terms for all entities in the system.
55 public function indexAllEntities()
57 SearchTerm::query()->truncate();
59 foreach ($this->entityProvider->all() as $entityModel) {
60 $selectFields = ['id', 'name', $entityModel->textField];
61 $entityModel->newQuery()
63 ->select($selectFields)
64 ->chunk(1000, function (Collection $entities) {
65 $this->indexEntities($entities->all());
71 * Delete related Entity search terms.
73 public function deleteEntityTerms(Entity $entity)
75 $entity->searchTerms()->delete();
79 * Create a scored term array from the given text.
81 * @returns array{term: string, score: float}
83 protected function generateTermArrayFromText(string $text, int $scoreAdjustment = 1): array
85 $tokenMap = []; // {TextToken => OccurrenceCount}
86 $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
87 $token = strtok($text, $splitChars);
89 while ($token !== false) {
90 if (!isset($tokenMap[$token])) {
91 $tokenMap[$token] = 0;
94 $token = strtok($splitChars);
98 foreach ($tokenMap as $token => $count) {
101 'score' => $count * $scoreAdjustment,
109 * For the given entity, Generate an array of term data details.
110 * Is the raw term data, not instances of SearchTerm models.
112 * @returns array{term: string, score: float}[]
114 protected function entityToTermDataArray(Entity $entity): array
116 $nameTerms = $this->generateTermArrayFromText($entity->name, 40 * $entity->searchFactor);
117 $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
118 $termData = array_merge($nameTerms, $bodyTerms);
120 foreach ($termData as $index => $term) {
121 $termData[$index]['entity_type'] = $entity->getMorphClass();
122 $termData[$index]['entity_id'] = $entity->id;