Dan Brown

Developed basic search queries.

Updated search & permission regen commands with ability to specify
database.
...@@ -12,7 +12,7 @@ class RegeneratePermissions extends Command ...@@ -12,7 +12,7 @@ class RegeneratePermissions extends Command
12 * 12 *
13 * @var string 13 * @var string
14 */ 14 */
15 - protected $signature = 'bookstack:regenerate-permissions'; 15 + protected $signature = 'bookstack:regenerate-permissions {--database= : The database connection to use.}';
16 16
17 /** 17 /**
18 * The console command description. 18 * The console command description.
...@@ -46,7 +46,14 @@ class RegeneratePermissions extends Command ...@@ -46,7 +46,14 @@ class RegeneratePermissions extends Command
46 */ 46 */
47 public function handle() 47 public function handle()
48 { 48 {
49 + $connection = \DB::getDefaultConnection();
50 + if ($this->option('database') !== null) {
51 + \DB::setDefaultConnection($this->option('database'));
52 + }
53 +
49 $this->permissionService->buildJointPermissions(); 54 $this->permissionService->buildJointPermissions();
55 +
56 + \DB::setDefaultConnection($connection);
50 $this->comment('Permissions regenerated'); 57 $this->comment('Permissions regenerated');
51 } 58 }
52 } 59 }
......
...@@ -12,7 +12,7 @@ class RegenerateSearch extends Command ...@@ -12,7 +12,7 @@ class RegenerateSearch extends Command
12 * 12 *
13 * @var string 13 * @var string
14 */ 14 */
15 - protected $signature = 'bookstack:regenerate-search'; 15 + protected $signature = 'bookstack:regenerate-search {--database= : The database connection to use.}';
16 16
17 /** 17 /**
18 * The console command description. 18 * The console command description.
...@@ -41,6 +41,13 @@ class RegenerateSearch extends Command ...@@ -41,6 +41,13 @@ class RegenerateSearch extends Command
41 */ 41 */
42 public function handle() 42 public function handle()
43 { 43 {
44 + $connection = \DB::getDefaultConnection();
45 + if ($this->option('database') !== null) {
46 + \DB::setDefaultConnection($this->option('database'));
47 + }
48 +
44 $this->searchService->indexAllEntities(); 49 $this->searchService->indexAllEntities();
50 + \DB::setDefaultConnection($connection);
51 + $this->comment('Search index regenerated');
45 } 52 }
46 } 53 }
......
...@@ -513,7 +513,7 @@ class PermissionService ...@@ -513,7 +513,7 @@ class PermissionService
513 * @param string $entityType 513 * @param string $entityType
514 * @param Builder|Entity $query 514 * @param Builder|Entity $query
515 * @param string $action 515 * @param string $action
516 - * @return mixed 516 + * @return Builder
517 */ 517 */
518 public function enforceEntityRestrictions($entityType, $query, $action = 'view') 518 public function enforceEntityRestrictions($entityType, $query, $action = 'view')
519 { 519 {
......
...@@ -16,6 +16,8 @@ class SearchService ...@@ -16,6 +16,8 @@ class SearchService
16 protected $chapter; 16 protected $chapter;
17 protected $page; 17 protected $page;
18 protected $db; 18 protected $db;
19 + protected $permissionService;
20 + protected $entities;
19 21
20 /** 22 /**
21 * SearchService constructor. 23 * SearchService constructor.
...@@ -24,22 +26,41 @@ class SearchService ...@@ -24,22 +26,41 @@ class SearchService
24 * @param Chapter $chapter 26 * @param Chapter $chapter
25 * @param Page $page 27 * @param Page $page
26 * @param Connection $db 28 * @param Connection $db
29 + * @param PermissionService $permissionService
27 */ 30 */
28 - public function __construct(SearchTerm $searchTerm, Book $book, Chapter $chapter, Page $page, Connection $db) 31 + public function __construct(SearchTerm $searchTerm, Book $book, Chapter $chapter, Page $page, Connection $db, PermissionService $permissionService)
29 { 32 {
30 $this->searchTerm = $searchTerm; 33 $this->searchTerm = $searchTerm;
31 $this->book = $book; 34 $this->book = $book;
32 $this->chapter = $chapter; 35 $this->chapter = $chapter;
33 $this->page = $page; 36 $this->page = $page;
34 $this->db = $db; 37 $this->db = $db;
38 + $this->entities = [
39 + 'page' => $this->page,
40 + 'chapter' => $this->chapter,
41 + 'book' => $this->book
42 + ];
43 + $this->permissionService = $permissionService;
35 } 44 }
36 45
37 - public function searchEntities($searchString, $entityType = 'all') 46 + public function searchEntities($searchString, $entityType = 'all', $page = 0, $count = 20)
38 { 47 {
39 // TODO - Add Tag Searches 48 // TODO - Add Tag Searches
40 // TODO - Add advanced custom column searches 49 // TODO - Add advanced custom column searches
41 // TODO - Add exact match searches ("") 50 // TODO - Add exact match searches ("")
51 + // TODO - Check drafts don't show up in results
52 + // TODO - Move search all page to just /search?term=cat
42 53
54 + if ($entityType !== 'all') return $this->searchEntityTable($searchString, $entityType, $page, $count);
55 +
56 + $bookSearch = $this->searchEntityTable($searchString, 'book', $page, $count);
57 + $chapterSearch = $this->searchEntityTable($searchString, 'chapter', $page, $count);
58 + $pageSearch = $this->searchEntityTable($searchString, 'page', $page, $count);
59 + return collect($bookSearch)->merge($chapterSearch)->merge($pageSearch)->sortByDesc('score');
60 + }
61 +
62 + public function searchEntityTable($searchString, $entityType = 'page', $page = 0, $count = 20)
63 + {
43 $termArray = explode(' ', $searchString); 64 $termArray = explode(' ', $searchString);
44 65
45 $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score')); 66 $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
...@@ -49,13 +70,24 @@ class SearchService ...@@ -49,13 +70,24 @@ class SearchService
49 } 70 }
50 }); 71 });
51 72
73 + $entity = $this->getEntity($entityType);
52 $subQuery = $subQuery->groupBy('entity_type', 'entity_id'); 74 $subQuery = $subQuery->groupBy('entity_type', 'entity_id');
53 - $pageSelect = $this->db->table('pages as e')->join(\DB::raw('(' . $subQuery->toSql() . ') as s'), function(JoinClause $join) { 75 + $entitySelect = $entity->newQuery()->join(\DB::raw('(' . $subQuery->toSql() . ') as s'), function(JoinClause $join) {
54 - $join->on('e.id', '=', 's.entity_id'); 76 + $join->on('id', '=', 'entity_id');
55 - })->selectRaw('e.*, s.score')->orderBy('score', 'desc'); 77 + })->selectRaw($entity->getTable().'.*, s.score')->orderBy('score', 'desc')->skip($page * $count)->take($count);
56 - $pageSelect->mergeBindings($subQuery); 78 + $entitySelect->mergeBindings($subQuery);
57 - dd($pageSelect->toSql()); 79 + $query = $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view');
58 - // TODO - Continue from here 80 + return $query->get();
81 + }
82 +
83 + /**
84 + * Get an entity instance via type.
85 + * @param $type
86 + * @return Entity
87 + */
88 + protected function getEntity($type)
89 + {
90 + return $this->entities[strtolower($type)];
59 } 91 }
60 92
61 /** 93 /**
...@@ -86,7 +118,11 @@ class SearchService ...@@ -86,7 +118,11 @@ class SearchService
86 $terms[] = $term; 118 $terms[] = $term;
87 } 119 }
88 } 120 }
89 - $this->searchTerm->insert($terms); 121 +
122 + $chunkedTerms = array_chunk($terms, 500);
123 + foreach ($chunkedTerms as $termChunk) {
124 + $this->searchTerm->insert($termChunk);
125 + }
90 } 126 }
91 127
92 /** 128 /**
...@@ -97,17 +133,17 @@ class SearchService ...@@ -97,17 +133,17 @@ class SearchService
97 $this->searchTerm->truncate(); 133 $this->searchTerm->truncate();
98 134
99 // Chunk through all books 135 // Chunk through all books
100 - $this->book->chunk(500, function ($books) { 136 + $this->book->chunk(1000, function ($books) {
101 $this->indexEntities($books); 137 $this->indexEntities($books);
102 }); 138 });
103 139
104 // Chunk through all chapters 140 // Chunk through all chapters
105 - $this->chapter->chunk(500, function ($chapters) { 141 + $this->chapter->chunk(1000, function ($chapters) {
106 $this->indexEntities($chapters); 142 $this->indexEntities($chapters);
107 }); 143 });
108 144
109 // Chunk through all pages 145 // Chunk through all pages
110 - $this->page->chunk(500, function ($pages) { 146 + $this->page->chunk(1000, function ($pages) {
111 $this->indexEntities($pages); 147 $this->indexEntities($pages);
112 }); 148 });
113 } 149 }
......
...@@ -16,7 +16,7 @@ class DummyContentSeeder extends Seeder ...@@ -16,7 +16,7 @@ class DummyContentSeeder extends Seeder
16 $user->attachRole($role); 16 $user->attachRole($role);
17 17
18 18
19 - $books = factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id]) 19 + factory(\BookStack\Book::class, 20)->create(['created_by' => $user->id, 'updated_by' => $user->id])
20 ->each(function($book) use ($user) { 20 ->each(function($book) use ($user) {
21 $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id]) 21 $chapters = factory(\BookStack\Chapter::class, 5)->create(['created_by' => $user->id, 'updated_by' => $user->id])
22 ->each(function($chapter) use ($user, $book){ 22 ->each(function($chapter) use ($user, $book){
...@@ -28,7 +28,7 @@ class DummyContentSeeder extends Seeder ...@@ -28,7 +28,7 @@ class DummyContentSeeder extends Seeder
28 $book->pages()->saveMany($pages); 28 $book->pages()->saveMany($pages);
29 }); 29 });
30 30
31 - $restrictionService = app(\BookStack\Services\PermissionService::class); 31 + app(\BookStack\Services\PermissionService::class)->buildJointPermissions();
32 - $restrictionService->buildJointPermissions(); 32 + app(\BookStack\Services\SearchService::class)->indexAllEntities();
33 } 33 }
34 } 34 }
......