]> BookStack Code Mirror - bookstack/commitdiff
Improved tag suggestion handling
authorDan Brown <redacted>
Wed, 28 Sep 2022 12:50:40 +0000 (13:50 +0100)
committerDan Brown <redacted>
Wed, 28 Sep 2022 12:50:40 +0000 (13:50 +0100)
- 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

app/Actions/TagRepo.php
app/Http/Controllers/TagController.php
resources/js/components/auto-suggest.js

index 172d8ec6ec0cfecb0b7059bfa693bbc91359d628..2618ed2e902329512defd05545e355bc22c9c286 100644 (file)
@@ -57,21 +57,21 @@ class TagRepo
      * Get tag name suggestions from scanning existing tag names.
      * 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()
             ->select('*', DB::raw('count(*) as count'))
             ->groupBy('name');
 
         if ($searchTerm) {
-            $query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'desc');
+            $query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'asc');
         } else {
             $query = $query->orderBy('count', 'desc')->take(50);
         }
 
         $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.
      * 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()
             ->select('*', DB::raw('count(*) as count'))
@@ -97,7 +97,7 @@ class TagRepo
 
         $query = $this->permissions->restrictEntityRelationQuery($query, 'tags', 'entity_id', 'entity_type');
 
-        return $query->get(['value'])->pluck('value');
+        return $query->pluck('value');
     }
 
     /**
index e59580b6065a2a816f3a2b6fade8526e62d80ef4..056cc9902d564f4204899a08e0b4b7af3bd8725c 100644 (file)
@@ -7,11 +7,8 @@ use Illuminate\Http\Request;
 
 class TagController extends Controller
 {
-    protected $tagRepo;
+    protected TagRepo $tagRepo;
 
-    /**
-     * TagController constructor.
-     */
     public function __construct(TagRepo $tagRepo)
     {
         $this->tagRepo = $tagRepo;
@@ -46,7 +43,7 @@ class TagController extends Controller
      */
     public function getNameSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', null);
+        $searchTerm = $request->get('search', '');
         $suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
 
         return response()->json($suggestions);
@@ -57,8 +54,8 @@ class TagController extends Controller
      */
     public function getValueSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', null);
-        $tagName = $request->get('name', null);
+        $searchTerm = $request->get('search', '');
+        $tagName = $request->get('name', '');
         $suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
 
         return response()->json($suggestions);
index d1c15c00a7e604742d566cdd80b280fd11f82135..80857cbe5c5cb20d121bfbcb6d40dd17ff786285 100644 (file)
@@ -88,14 +88,12 @@ class AutoSuggest {
         }
 
         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);
-        let toShow = suggestions.slice(0, 6);
-        if (search.length > 0) {
-            toShow = suggestions.filter(val => {
-                return val.toLowerCase().includes(search);
-            }).slice(0, 6);
-        }
+
+        const toShow = suggestions.filter(val => {
+            return search === '' || val.toLowerCase().startsWith(search);
+        }).slice(0, 10);
 
         this.displaySuggestions(toShow);
     }
@@ -111,6 +109,9 @@ class AutoSuggest {
      * @returns {Promise<Object|String|*>}
      */
     async loadSuggestions(search, nameFilter = null) {
+        // Truncate search to prevent over numerous lookups
+        search = search.slice(0, 4);
+
         const params = {search, name: nameFilter};
         const cacheKey = `${this.url}:${JSON.stringify(params)}`;