]> BookStack Code Mirror - bookstack/blob - app/Entities/Tools/SearchIndex.php
Update TrashCan.php
[bookstack] / app / Entities / Tools / SearchIndex.php
1 <?php namespace BookStack\Entities\Tools;
2
3 use BookStack\Entities\EntityProvider;
4 use BookStack\Entities\Models\Entity;
5 use BookStack\Entities\Models\SearchTerm;
6 use Illuminate\Support\Collection;
7
8 class SearchIndex
9 {
10     /**
11      * @var SearchTerm
12      */
13     protected $searchTerm;
14
15     /**
16      * @var EntityProvider
17      */
18     protected $entityProvider;
19
20
21     public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider)
22     {
23         $this->searchTerm = $searchTerm;
24         $this->entityProvider = $entityProvider;
25     }
26
27
28     /**
29      * Index the given entity.
30      */
31     public function indexEntity(Entity $entity)
32     {
33         $this->deleteEntityTerms($entity);
34         $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
35         $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
36         $terms = array_merge($nameTerms, $bodyTerms);
37         foreach ($terms as $index => $term) {
38             $terms[$index]['entity_type'] = $entity->getMorphClass();
39             $terms[$index]['entity_id'] = $entity->id;
40         }
41         $this->searchTerm->newQuery()->insert($terms);
42     }
43
44     /**
45      * Index multiple Entities at once
46      * @param Entity[] $entities
47      */
48     protected function indexEntities(array $entities)
49     {
50         $terms = [];
51         foreach ($entities as $entity) {
52             $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
53             $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
54             foreach (array_merge($nameTerms, $bodyTerms) as $term) {
55                 $term['entity_id'] = $entity->id;
56                 $term['entity_type'] = $entity->getMorphClass();
57                 $terms[] = $term;
58             }
59         }
60
61         $chunkedTerms = array_chunk($terms, 500);
62         foreach ($chunkedTerms as $termChunk) {
63             $this->searchTerm->newQuery()->insert($termChunk);
64         }
65     }
66
67     /**
68      * Delete and re-index the terms for all entities in the system.
69      */
70     public function indexAllEntities()
71     {
72         $this->searchTerm->newQuery()->truncate();
73
74         foreach ($this->entityProvider->all() as $entityModel) {
75             $selectFields = ['id', 'name', $entityModel->textField];
76             $entityModel->newQuery()
77                 ->withTrashed()
78                 ->select($selectFields)
79                 ->chunk(1000, function (Collection $entities) {
80                     $this->indexEntities($entities->all());
81                 });
82         }
83     }
84
85     /**
86      * Delete related Entity search terms.
87      */
88     public function deleteEntityTerms(Entity $entity)
89     {
90         $entity->searchTerms()->delete();
91     }
92
93     /**
94      * Create a scored term array from the given text.
95      */
96     protected function generateTermArrayFromText(string $text, int $scoreAdjustment = 1): array
97     {
98         $tokenMap = []; // {TextToken => OccurrenceCount}
99         $splitChars = " \n\t.,!?:;()[]{}<>`'\"";
100         $token = strtok($text, $splitChars);
101
102         while ($token !== false) {
103             if (!isset($tokenMap[$token])) {
104                 $tokenMap[$token] = 0;
105             }
106             $tokenMap[$token]++;
107             $token = strtok($splitChars);
108         }
109
110         $terms = [];
111         foreach ($tokenMap as $token => $count) {
112             $terms[] = [
113                 'term' => $token,
114                 'score' => $count * $scoreAdjustment
115             ];
116         }
117
118         return $terms;
119     }
120 }