mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-05-02 15:30:06 +00:00
Improved tag suggestion handling
- Aligned prefix-type filtering with back-end. - Increased suggestion search cut-off from 3 to 4. - Increased amount of suggestions shown. - Ordered suggestions to be name asc, as you'd expect on search. - Updated front-end filtering to use full search query, instead of truncated version, for further front-end filtering capability. Related to #3720
This commit is contained in:
parent
1ac1cf0c78
commit
8f3430d386
3 changed files with 17 additions and 19 deletions
|
@ -57,21 +57,21 @@ class TagRepo
|
||||||
* Get tag name suggestions from scanning existing tag names.
|
* Get tag name suggestions from scanning existing tag names.
|
||||||
* If no search term is given the 50 most popular tag names are provided.
|
* If no search term is given the 50 most popular tag names are provided.
|
||||||
*/
|
*/
|
||||||
public function getNameSuggestions(?string $searchTerm): Collection
|
public function getNameSuggestions(string $searchTerm): Collection
|
||||||
{
|
{
|
||||||
$query = Tag::query()
|
$query = Tag::query()
|
||||||
->select('*', DB::raw('count(*) as count'))
|
->select('*', DB::raw('count(*) as count'))
|
||||||
->groupBy('name');
|
->groupBy('name');
|
||||||
|
|
||||||
if ($searchTerm) {
|
if ($searchTerm) {
|
||||||
$query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'desc');
|
$query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'asc');
|
||||||
} else {
|
} else {
|
||||||
$query = $query->orderBy('count', 'desc')->take(50);
|
$query = $query->orderBy('count', 'desc')->take(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
|
$query = $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
|
||||||
|
|
||||||
return $query->get(['name'])->pluck('name');
|
return $query->pluck('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +79,7 @@ class TagRepo
|
||||||
* If no search is given the 50 most popular values are provided.
|
* If no search is given the 50 most popular values are provided.
|
||||||
* Passing a tagName will only find values for a tags with a particular name.
|
* Passing a tagName will only find values for a tags with a particular name.
|
||||||
*/
|
*/
|
||||||
public function getValueSuggestions(?string $searchTerm, ?string $tagName): Collection
|
public function getValueSuggestions(string $searchTerm, string $tagName): Collection
|
||||||
{
|
{
|
||||||
$query = Tag::query()
|
$query = Tag::query()
|
||||||
->select('*', DB::raw('count(*) as count'))
|
->select('*', DB::raw('count(*) as count'))
|
||||||
|
@ -97,7 +97,7 @@ class TagRepo
|
||||||
|
|
||||||
$query = $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
|
$query = $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
|
||||||
|
|
||||||
return $query->get(['value'])->pluck('value');
|
return $query->pluck('value');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,11 +7,8 @@ use Illuminate\Http\Request;
|
||||||
|
|
||||||
class TagController extends Controller
|
class TagController extends Controller
|
||||||
{
|
{
|
||||||
protected $tagRepo;
|
protected TagRepo $tagRepo;
|
||||||
|
|
||||||
/**
|
|
||||||
* TagController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(TagRepo $tagRepo)
|
public function __construct(TagRepo $tagRepo)
|
||||||
{
|
{
|
||||||
$this->tagRepo = $tagRepo;
|
$this->tagRepo = $tagRepo;
|
||||||
|
@ -46,7 +43,7 @@ class TagController extends Controller
|
||||||
*/
|
*/
|
||||||
public function getNameSuggestions(Request $request)
|
public function getNameSuggestions(Request $request)
|
||||||
{
|
{
|
||||||
$searchTerm = $request->get('search', null);
|
$searchTerm = $request->get('search', '');
|
||||||
$suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
|
$suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
|
||||||
|
|
||||||
return response()->json($suggestions);
|
return response()->json($suggestions);
|
||||||
|
@ -57,8 +54,8 @@ class TagController extends Controller
|
||||||
*/
|
*/
|
||||||
public function getValueSuggestions(Request $request)
|
public function getValueSuggestions(Request $request)
|
||||||
{
|
{
|
||||||
$searchTerm = $request->get('search', null);
|
$searchTerm = $request->get('search', '');
|
||||||
$tagName = $request->get('name', null);
|
$tagName = $request->get('name', '');
|
||||||
$suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
|
$suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
|
||||||
|
|
||||||
return response()->json($suggestions);
|
return response()->json($suggestions);
|
||||||
|
|
|
@ -88,14 +88,12 @@ class AutoSuggest {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nameFilter = this.getNameFilterIfNeeded();
|
const nameFilter = this.getNameFilterIfNeeded();
|
||||||
const search = this.input.value.slice(0, 3).toLowerCase();
|
const search = this.input.value.toLowerCase();
|
||||||
const suggestions = await this.loadSuggestions(search, nameFilter);
|
const suggestions = await this.loadSuggestions(search, nameFilter);
|
||||||
let toShow = suggestions.slice(0, 6);
|
|
||||||
if (search.length > 0) {
|
const toShow = suggestions.filter(val => {
|
||||||
toShow = suggestions.filter(val => {
|
return search === '' || val.toLowerCase().startsWith(search);
|
||||||
return val.toLowerCase().includes(search);
|
}).slice(0, 10);
|
||||||
}).slice(0, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displaySuggestions(toShow);
|
this.displaySuggestions(toShow);
|
||||||
}
|
}
|
||||||
|
@ -111,6 +109,9 @@ class AutoSuggest {
|
||||||
* @returns {Promise<Object|String|*>}
|
* @returns {Promise<Object|String|*>}
|
||||||
*/
|
*/
|
||||||
async loadSuggestions(search, nameFilter = null) {
|
async loadSuggestions(search, nameFilter = null) {
|
||||||
|
// Truncate search to prevent over numerous lookups
|
||||||
|
search = search.slice(0, 4);
|
||||||
|
|
||||||
const params = {search, name: nameFilter};
|
const params = {search, name: nameFilter};
|
||||||
const cacheKey = `${this.url}:${JSON.stringify(params)}`;
|
const cacheKey = `${this.url}:${JSON.stringify(params)}`;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue