1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2024-11-23 11:22:33 +01:00

Further search system refactorings

- Moved search term querying to its own method.
- Updated Large content seeder to be more performant
This commit is contained in:
Dan Brown 2021-11-08 11:29:25 +00:00
parent e1b8fe45b0
commit 9e0164f4f4
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 39 additions and 24 deletions

View File

@ -46,7 +46,7 @@ class SearchIndex
* *
* @param Entity[] $entities * @param Entity[] $entities
*/ */
protected function indexEntities(array $entities) public function indexEntities(array $entities)
{ {
$terms = []; $terms = [];
foreach ($entities as $entity) { foreach ($entities as $entity) {

View File

@ -133,29 +133,14 @@ class SearchRunner
protected function buildQuery(SearchOptions $searchOpts, string $entityType = 'page', string $action = 'view'): EloquentBuilder protected function buildQuery(SearchOptions $searchOpts, string $entityType = 'page', string $action = 'view'): EloquentBuilder
{ {
$entity = $this->entityProvider->get($entityType); $entity = $this->entityProvider->get($entityType);
$entitySelect = $entity->newQuery(); $entityQuery = $entity->newQuery();
// Handle normal search terms // Handle normal search terms
if (count($searchOpts->searches) > 0) { $this->applyTermSearch($entityQuery, $searchOpts->searches, $entity);
$rawScoreSum = DB::raw('SUM(score) as score');
$subQuery = DB::table('search_terms')->select('entity_id', 'entity_type', $rawScoreSum);
$subQuery->where('entity_type', '=', $entity->getMorphClass());
$subQuery->where(function (Builder $query) use ($searchOpts) {
foreach ($searchOpts->searches as $inputTerm) {
$query->orWhere('term', 'like', $inputTerm . '%');
}
})->groupBy('entity_type', 'entity_id');
$entitySelect->join(DB::raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
$join->on('id', '=', 'entity_id');
})->addSelect($entity->getTable() . '.*')
->selectRaw('s.score')
->orderBy('score', 'desc');
$entitySelect->mergeBindings($subQuery);
}
// Handle exact term matching // Handle exact term matching
foreach ($searchOpts->exacts as $inputTerm) { foreach ($searchOpts->exacts as $inputTerm) {
$entitySelect->where(function (EloquentBuilder $query) use ($inputTerm, $entity) { $entityQuery->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
$query->where('name', 'like', '%' . $inputTerm . '%') $query->where('name', 'like', '%' . $inputTerm . '%')
->orWhere($entity->textField, 'like', '%' . $inputTerm . '%'); ->orWhere($entity->textField, 'like', '%' . $inputTerm . '%');
}); });
@ -163,18 +148,47 @@ class SearchRunner
// Handle tag searches // Handle tag searches
foreach ($searchOpts->tags as $inputTerm) { foreach ($searchOpts->tags as $inputTerm) {
$this->applyTagSearch($entitySelect, $inputTerm); $this->applyTagSearch($entityQuery, $inputTerm);
} }
// Handle filters // Handle filters
foreach ($searchOpts->filters as $filterTerm => $filterValue) { foreach ($searchOpts->filters as $filterTerm => $filterValue) {
$functionName = Str::camel('filter_' . $filterTerm); $functionName = Str::camel('filter_' . $filterTerm);
if (method_exists($this, $functionName)) { if (method_exists($this, $functionName)) {
$this->$functionName($entitySelect, $entity, $filterValue); $this->$functionName($entityQuery, $entity, $filterValue);
} }
} }
return $this->permissionService->enforceEntityRestrictions($entity, $entitySelect, $action); return $this->permissionService->enforceEntityRestrictions($entity, $entityQuery, $action);
}
/**
* For the given search query, apply the queries for handling the regular search terms.
*/
protected function applyTermSearch(EloquentBuilder $entityQuery, array $terms, Entity $entity): void
{
if (count($terms) === 0) {
return;
}
$subQuery = DB::table('search_terms')->select([
'entity_id',
'entity_type',
DB::raw('SUM(score) as score'),
]);
$subQuery->where('entity_type', '=', $entity->getMorphClass());
$subQuery->where(function (Builder $query) use ($terms) {
foreach ($terms as $inputTerm) {
$query->orWhere('term', 'like', $inputTerm . '%');
}
})->groupBy('entity_type', 'entity_id');
$entityQuery->join(DB::raw('(' . $subQuery->toSql() . ') as s'), function (JoinClause $join) {
$join->on('id', '=', 'entity_id');
})->addSelect($entity->getTable() . '.*')
->selectRaw('s.score')
->orderBy('score', 'desc');
$entityQuery->mergeBindings($subQuery);
} }
/** /**

View File

@ -33,8 +33,9 @@ class LargeContentSeeder extends Seeder
$largeBook->pages()->saveMany($pages); $largeBook->pages()->saveMany($pages);
$largeBook->chapters()->saveMany($chapters); $largeBook->chapters()->saveMany($chapters);
$all = array_merge([$largeBook], array_values($pages->all()), array_values($chapters->all()));
app()->make(PermissionService::class)->buildJointPermissions(); app()->make(PermissionService::class)->buildJointPermissionsForEntity($largeBook);
app()->make(SearchIndex::class)->indexAllEntities(); app()->make(SearchIndex::class)->indexEntities($all);
} }
} }