Started implementation of new search system · c0debrain/BookStack@070d4ae · GitHub
Skip to content

Commit 070d4ae

Browse files
committed
Started implementation of new search system
1 parent 668ce26 commit 070d4ae

13 files changed

Lines changed: 374 additions & 53 deletions

File tree

app/Book.php

Lines changed: 9 additions & 0 deletions

app/Chapter.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,13 @@ public function getExcerpt($length = 100)
5151
return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
5252
}
5353

54+
/**
55+
* Return a generalised, common raw query that can be 'unioned' across entities.
56+
* @return string
57+
*/
58+
public function entityRawQuery()
59+
{
60+
return "'BookStack\\\\Chapter' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, '' as html, book_id, priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at";
61+
}
62+
5463
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace BookStack\Console\Commands;
4+
5+
use BookStack\Services\SearchService;
6+
use Illuminate\Console\Command;
7+
8+
class RegenerateSearch extends Command
9+
{
10+
/**
11+
* The name and signature of the console command.
12+
*
13+
* @var string
14+
*/
15+
protected $signature = 'bookstack:regenerate-search';
16+
17+
/**
18+
* The console command description.
19+
*
20+
* @var string
21+
*/
22+
protected $description = 'Command description';
23+
24+
protected $searchService;
25+
26+
/**
27+
* Create a new command instance.
28+
*
29+
* @param SearchService $searchService
30+
*/
31+
public function __construct(SearchService $searchService)
32+
{
33+
parent::__construct();
34+
$this->searchService = $searchService;
35+
}
36+
37+
/**
38+
* Execute the console command.
39+
*
40+
* @return mixed
41+
*/
42+
public function handle()
43+
{
44+
$this->searchService->indexAllEntities();
45+
}
46+
}

app/Console/Kernel.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
<?php
2-
3-
namespace BookStack\Console;
1+
<?php namespace BookStack\Console;
42

53
use Illuminate\Console\Scheduling\Schedule;
64
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -13,10 +11,11 @@ class Kernel extends ConsoleKernel
1311
* @var array
1412
*/
1513
protected $commands = [
16-
\BookStack\Console\Commands\ClearViews::class,
17-
\BookStack\Console\Commands\ClearActivity::class,
18-
\BookStack\Console\Commands\ClearRevisions::class,
19-
\BookStack\Console\Commands\RegeneratePermissions::class,
14+
Commands\ClearViews::class,
15+
Commands\ClearActivity::class,
16+
Commands\ClearRevisions::class,
17+
Commands\RegeneratePermissions::class,
18+
Commands\RegenerateSearch::class
2019
];
2120

2221
/**

app/Entity.php

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
class Entity extends Ownable
55
{
66

7-
protected $fieldsToSearch = ['name', 'description'];
7+
protected $textField = 'description';
88

99
/**
1010
* Compares this entity to another given entity.
@@ -65,6 +65,15 @@ public function tags()
6565
return $this->morphMany(Tag::class, 'entity')->orderBy('order', 'asc');
6666
}
6767

68+
/**
69+
* Get the related search terms.
70+
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
71+
*/
72+
public function searchTerms()
73+
{
74+
return $this->morphMany(SearchTerm::class, 'entity');
75+
}
76+
6877
/**
6978
* Get this entities restrictions.
7079
*/
@@ -152,11 +161,26 @@ public function getShortName($length = 25)
152161
return substr($this->name, 0, $length - 3) . '...';
153162
}
154163

164+
/**
165+
* Get the body text of this entity.
166+
* @return mixed
167+
*/
168+
public function getText()
169+
{
170+
return $this->{$this->textField};
171+
}
172+
173+
/**
174+
* Return a generalised, common raw query that can be 'unioned' across entities.
175+
* @return string
176+
*/
177+
public function entityRawQuery(){return '';}
178+
155179
/**
156180
* Perform a full-text search on this entity.
157-
* @param string[] $fieldsToSearch
158181
* @param string[] $terms
159182
* @param string[] array $wheres
183+
* TODO - REMOVE
160184
* @return mixed
161185
*/
162186
public function fullTextSearchQuery($terms, $wheres = [])
@@ -178,21 +202,21 @@ public function fullTextSearchQuery($terms, $wheres = [])
178202
}
179203

180204
$isFuzzy = count($exactTerms) === 0 && count($fuzzyTerms) > 0;
181-
205+
$fieldsToSearch = ['name', $this->textField];
182206

183207
// Perform fulltext search if relevant terms exist.
184208
if ($isFuzzy) {
185209
$termString = implode(' ', $fuzzyTerms);
186-
$fields = implode(',', $this->fieldsToSearch);
210+
187211
$search = $search->selectRaw('*, MATCH(name) AGAINST(? IN BOOLEAN MODE) AS title_relevance', [$termString]);
188-
$search = $search->whereRaw('MATCH(' . $fields . ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
212+
$search = $search->whereRaw('MATCH(' . implode(',', $fieldsToSearch ). ') AGAINST(? IN BOOLEAN MODE)', [$termString]);
189213
}
190214

191215
// Ensure at least one exact term matches if in search
192216
if (count($exactTerms) > 0) {
193-
$search = $search->where(function ($query) use ($exactTerms) {
217+
$search = $search->where(function ($query) use ($exactTerms, $fieldsToSearch) {
194218
foreach ($exactTerms as $exactTerm) {
195-
foreach ($this->fieldsToSearch as $field) {
219+
foreach ($fieldsToSearch as $field) {
196220
$query->orWhere($field, 'like', $exactTerm);
197221
}
198222
}

app/Http/Controllers/SearchController.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
<?php namespace BookStack\Http\Controllers;
22

33
use BookStack\Repos\EntityRepo;
4+
use BookStack\Services\SearchService;
45
use BookStack\Services\ViewService;
56
use Illuminate\Http\Request;
67

78
class SearchController extends Controller
89
{
910
protected $entityRepo;
1011
protected $viewService;
12+
protected $searchService;
1113

1214
/**
1315
* SearchController constructor.
1416
* @param EntityRepo $entityRepo
1517
* @param ViewService $viewService
18+
* @param SearchService $searchService
1619
*/
17-
public function __construct(EntityRepo $entityRepo, ViewService $viewService)
20+
public function __construct(EntityRepo $entityRepo, ViewService $viewService, SearchService $searchService)
1821
{
1922
$this->entityRepo = $entityRepo;
2023
$this->viewService = $viewService;
24+
$this->searchService = $searchService;
2125
parent::__construct();
2226
}
2327

@@ -33,15 +37,13 @@ public function searchAll(Request $request)
3337
return redirect()->back();
3438
}
3539
$searchTerm = $request->get('term');
36-
$paginationAppends = $request->only('term');
37-
$pages = $this->entityRepo->getBySearch('page', $searchTerm, [], 20, $paginationAppends);
38-
$books = $this->entityRepo->getBySearch('book', $searchTerm, [], 10, $paginationAppends);
39-
$chapters = $this->entityRepo->getBySearch('chapter', $searchTerm, [], 10, $paginationAppends);
40+
// $paginationAppends = $request->only('term'); TODO - Check pagination
4041
$this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
42+
43+
$entities = $this->searchService->searchEntities($searchTerm);
44+
4145
return view('search/all', [
42-
'pages' => $pages,
43-
'books' => $books,
44-
'chapters' => $chapters,
46+
'entities' => $entities,
4547
'searchTerm' => $searchTerm
4648
]);
4749
}

app/Page.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ class Page extends Entity
88
protected $simpleAttributes = ['name', 'id', 'slug'];
99

1010
protected $with = ['book'];
11-
12-
protected $fieldsToSearch = ['name', 'text'];
11+
protected $textField = 'text';
1312

1413
/**
1514
* Converts this page into a simplified array.
@@ -96,4 +95,14 @@ public function getExcerpt($length = 100)
9695
return mb_convert_encoding($text, 'UTF-8');
9796
}
9897

98+
/**
99+
* Return a generalised, common raw query that can be 'unioned' across entities.
100+
* @param bool $withContent
101+
* @return string
102+
*/
103+
public function entityRawQuery($withContent = false)
104+
{ $htmlQuery = $withContent ? 'html' : "'' as html";
105+
return "'BookStack\\\\Page' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, {$htmlQuery}, book_id, priority, chapter_id, draft, created_by, updated_by, updated_at, created_at";
106+
}
107+
99108
}

app/Repos/EntityRepo.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use BookStack\PageRevision;
99
use BookStack\Services\AttachmentService;
1010
use BookStack\Services\PermissionService;
11+
use BookStack\Services\SearchService;
1112
use BookStack\Services\ViewService;
1213
use Carbon\Carbon;
1314
use DOMDocument;
@@ -58,25 +59,32 @@ class EntityRepo
5859
*/
5960
protected $tagRepo;
6061

62+
/**
63+
* @var SearchService
64+
*/
65+
protected $searchService;
66+
6167
/**
6268
* Acceptable operators to be used in a query
6369
* @var array
6470
*/
6571
protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
6672

6773
/**
68-
* EntityService constructor.
74+
* EntityRepo constructor.
6975
* @param Book $book
7076
* @param Chapter $chapter
7177
* @param Page $page
7278
* @param PageRevision $pageRevision
7379
* @param ViewService $viewService
7480
* @param PermissionService $permissionService
7581
* @param TagRepo $tagRepo
82+
* @param SearchService $searchService
7683
*/
7784
public function __construct(
7885
Book $book, Chapter $chapter, Page $page, PageRevision $pageRevision,
79-
ViewService $viewService, PermissionService $permissionService, TagRepo $tagRepo
86+
ViewService $viewService, PermissionService $permissionService,
87+
TagRepo $tagRepo, SearchService $searchService
8088
)
8189
{
8290
$this->book = $book;
@@ -91,6 +99,7 @@ public function __construct(
9199
$this->viewService = $viewService;
92100
$this->permissionService = $permissionService;
93101
$this->tagRepo = $tagRepo;
102+
$this->searchService = $searchService;
94103
}
95104

96105
/**
@@ -608,6 +617,7 @@ public function createFromInput($type, $input = [], $book = false)
608617
$entity->updated_by = user()->id;
609618
$isChapter ? $book->chapters()->save($entity) : $entity->save();
610619
$this->permissionService->buildJointPermissionsForEntity($entity);
620+
$this->searchService->indexEntity($entity);
611621
return $entity;
612622
}
613623

@@ -628,6 +638,7 @@ public function updateFromInput($type, Entity $entityModel, $input = [])
628638
$entityModel->updated_by = user()->id;
629639
$entityModel->save();
630640
$this->permissionService->buildJointPermissionsForEntity($entityModel);
641+
$this->searchService->indexEntity($entityModel);
631642
return $entityModel;
632643
}
633644

@@ -961,6 +972,8 @@ public function updatePage(Page $page, $book_id, $input)
961972
$this->savePageRevision($page, $input['summary']);
962973
}
963974

975+
$this->searchService->indexEntity($page);
976+
964977
return $page;
965978
}
966979

@@ -1064,6 +1077,7 @@ public function restorePageRevision(Page $page, Book $book, $revisionId)
10641077
$page->text = strip_tags($page->html);
10651078
$page->updated_by = user()->id;
10661079
$page->save();
1080+
$this->searchService->indexEntity($page);
10671081
return $page;
10681082
}
10691083

@@ -1156,6 +1170,7 @@ public function destroyBook(Book $book)
11561170
$book->views()->delete();
11571171
$book->permissions()->delete();
11581172
$this->permissionService->deleteJointPermissionsForEntity($book);
1173+
$this->searchService->deleteEntityTerms($book);
11591174
$book->delete();
11601175
}
11611176

@@ -1175,6 +1190,7 @@ public function destroyChapter(Chapter $chapter)
11751190
$chapter->views()->delete();
11761191
$chapter->permissions()->delete();
11771192
$this->permissionService->deleteJointPermissionsForEntity($chapter);
1193+
$this->searchService->deleteEntityTerms($chapter);
11781194
$chapter->delete();
11791195
}
11801196

@@ -1190,6 +1206,7 @@ public function destroyPage(Page $page)
11901206
$page->revisions()->delete();
11911207
$page->permissions()->delete();
11921208
$this->permissionService->deleteJointPermissionsForEntity($page);
1209+
$this->searchService->deleteEntityTerms($page);
11931210

11941211
// Delete Attached Files
11951212
$attachmentService = app(AttachmentService::class);

app/SearchTerm.php

Lines changed: 20 additions & 0 deletions

0 commit comments

Comments
 (0)