Added search term parsing and exact term matches

This commit is contained in:
Dan Brown 2017-03-27 11:57:33 +01:00
parent 0651eae7ec
commit 331305333d
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
3 changed files with 87 additions and 16 deletions

View File

@ -4,7 +4,7 @@
class Entity extends Ownable
{
protected $textField = 'description';
public $textField = 'description';
/**
* Compares this entity to another given entity.

View File

@ -8,7 +8,7 @@ class Page extends Entity
protected $simpleAttributes = ['name', 'id', 'slug'];
protected $with = ['book'];
protected $textField = 'text';
public $textField = 'text';
/**
* Converts this page into a simplified array.

View File

@ -6,7 +6,9 @@ use BookStack\Entity;
use BookStack\Page;
use BookStack\SearchTerm;
use Illuminate\Database\Connection;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
class SearchService
{
@ -43,11 +45,18 @@ class SearchService
$this->permissionService = $permissionService;
}
/**
* Search all entities in the system.
* @param $searchString
* @param string $entityType
* @param int $page
* @param int $count
* @return Collection
*/
public function searchEntities($searchString, $entityType = 'all', $page = 0, $count = 20)
{
// TODO - Add Tag Searches
// TODO - Add advanced custom column searches
// TODO - Add exact match searches ("")
// TODO - Check drafts don't show up in results
// TODO - Move search all page to just /search?term=cat
@ -59,27 +68,89 @@ class SearchService
return collect($bookSearch)->merge($chapterSearch)->merge($pageSearch)->sortByDesc('score');
}
/**
* Search across a particular entity type.
* @param string $searchString
* @param string $entityType
* @param int $page
* @param int $count
* @return \Illuminate\Database\Eloquent\Collection|static[]
*/
public function searchEntityTable($searchString, $entityType = 'page', $page = 0, $count = 20)
{
$termArray = explode(' ', $searchString);
$subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
$subQuery->where(function($query) use ($termArray) {
foreach ($termArray as $inputTerm) {
$query->orWhere('term', 'like', $inputTerm .'%');
}
});
$searchTerms = $this->parseSearchString($searchString);
$entity = $this->getEntity($entityType);
$subQuery = $subQuery->groupBy('entity_type', 'entity_id');
$entitySelect = $entity->newQuery()->join(\DB::raw('(' . $subQuery->toSql() . ') as s'), function(JoinClause $join) {
$join->on('id', '=', 'entity_id');
})->selectRaw($entity->getTable().'.*, s.score')->orderBy('score', 'desc')->skip($page * $count)->take($count);
$entitySelect->mergeBindings($subQuery);
$entitySelect = $entity->newQuery();
// Handle normal search terms
if (count($searchTerms['search']) > 0) {
$subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
$subQuery->where(function(Builder $query) use ($searchTerms) {
foreach ($searchTerms['search'] 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');
})->selectRaw($entity->getTable().'.*, s.score')->orderBy('score', 'desc');
$entitySelect->mergeBindings($subQuery);
}
// Handle exact term matching
if (count($searchTerms['exact']) > 0) {
$entitySelect->where(function(\Illuminate\Database\Eloquent\Builder $query) use ($searchTerms, $entity) {
foreach ($searchTerms['exact'] as $inputTerm) {
$query->where(function (\Illuminate\Database\Eloquent\Builder $query) use ($inputTerm, $entity) {
$query->where('name', 'like', '%'.$inputTerm .'%')
->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
});
}
});
}
$entitySelect->skip($page * $count)->take($count);
$query = $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, 'view');
return $query->get();
}
/**
* Parse a search string into components.
* @param $searchString
* @return array
*/
public function parseSearchString($searchString)
{
$terms = [
'search' => [],
'exact' => [],
'tags' => [],
'filters' => []
];
$patterns = [
'exact' => '/"(.*?)"/',
'tags' => '/\[(.*?)\]/',
'filters' => '/\{(.*?)\}/'
];
foreach ($patterns as $termType => $pattern) {
$matches = [];
preg_match_all($pattern, $searchString, $matches);
if (count($matches) > 0) {
$terms[$termType] = $matches[1];
$searchString = preg_replace($pattern, '', $searchString);
}
}
foreach (explode(' ', trim($searchString)) as $searchTerm) {
if ($searchTerm !== '') $terms['search'][] = $searchTerm;
}
return $terms;
}
/**
* Get an entity instance via type.
* @param $type