X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/1903422113a3962faf29d60c6ddf240164af7d4a..refs/pull/139/head:/app/Repos/EntityRepo.php diff --git a/app/Repos/EntityRepo.php b/app/Repos/EntityRepo.php index 6b4076e6e..012a64967 100644 --- a/app/Repos/EntityRepo.php +++ b/app/Repos/EntityRepo.php @@ -6,6 +6,7 @@ use BookStack\Entity; use BookStack\Page; use BookStack\Services\PermissionService; use BookStack\User; +use Illuminate\Support\Facades\Log; class EntityRepo { @@ -30,6 +31,12 @@ class EntityRepo */ protected $permissionService; + /** + * Acceptable operators to be used in a query + * @var array + */ + protected $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!=']; + /** * EntityService constructor. */ @@ -163,6 +170,7 @@ class EntityRepo */ protected function prepareSearchTerms($termString) { + $termString = $this->cleanSearchTermString($termString); preg_match_all('/"(.*?)"/', $termString, $matches); if (count($matches[1]) > 0) { $terms = $matches[1]; @@ -174,5 +182,93 @@ class EntityRepo return $terms; } + /** + * Removes any special search notation that should not + * be used in a full-text search. + * @param $termString + * @return mixed + */ + protected function cleanSearchTermString($termString) + { + // Strip tag searches + $termString = preg_replace('/\[.*?\]/', '', $termString); + // Reduced multiple spacing into single spacing + $termString = preg_replace("/\s{2,}/", " ", $termString); + return $termString; + } + + /** + * Get the available query operators as a regex escaped list. + * @return mixed + */ + protected function getRegexEscapedOperators() + { + $escapedOperators = []; + foreach ($this->queryOperators as $operator) { + $escapedOperators[] = preg_quote($operator); + } + return join('|', $escapedOperators); + } + + /** + * Parses advanced search notations and adds them to the db query. + * @param $query + * @param $termString + * @return mixed + */ + protected function addAdvancedSearchQueries($query, $termString) + { + $escapedOperators = $this->getRegexEscapedOperators(); + // Look for tag searches + preg_match_all("/\[(.*?)((${escapedOperators})(.*?))?\]/", $termString, $tags); + if (count($tags[0]) > 0) { + $this->applyTagSearches($query, $tags); + } + + return $query; + } + + /** + * Apply extracted tag search terms onto a entity query. + * @param $query + * @param $tags + * @return mixed + */ + protected function applyTagSearches($query, $tags) { + $query->where(function($query) use ($tags) { + foreach ($tags[1] as $index => $tagName) { + $query->whereHas('tags', function($query) use ($tags, $index, $tagName) { + $tagOperator = $tags[3][$index]; + $tagValue = $tags[4][$index]; + if (!empty($tagOperator) && !empty($tagValue) && in_array($tagOperator, $this->queryOperators)) { + if (is_numeric($tagValue) && $tagOperator !== 'like') { + // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will + // search the value as a string which prevents being able to do number-based operations + // on the tag values. We ensure it has a numeric value and then cast it just to be sure. + $tagValue = (float) trim($query->getConnection()->getPdo()->quote($tagValue), "'"); + $query->where('name', '=', $tagName)->whereRaw("value ${tagOperator} ${tagValue}"); + } else { + $query->where('name', '=', $tagName)->where('value', $tagOperator, $tagValue); + } + } else { + $query->where('name', '=', $tagName); + } + }); + } + }); + return $query; + } + +} + + + + + + + + + + + -} \ No newline at end of file