Developed basic search queries.
Updated search & permission regen commands with ability to specify database.
Showing
5 changed files
with
68 additions
and
18 deletions
| ... | @@ -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 | } | ... | ... |
-
Please register or sign in to post a comment