From f101c1a622fbce7e22ad94bc93b17f234cc841cb Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 24 Mar 2018 18:46:31 +0000
Subject: [PATCH] Made search more efficient and tweaked weighting

Added per-entity weighting changes.
Now Books score higher than chapters which score higher than pages.

Reduced queries required on search by only searching once but at a
higher count to see if there's another page.
---
 app/Book.php                              |  1 +
 app/Chapter.php                           |  2 ++
 app/Entity.php                            |  9 +++++++++
 app/Http/Controllers/SearchController.php |  3 +--
 app/Services/SearchService.php            | 17 +++++++++--------
 5 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/app/Book.php b/app/Book.php
index 457a4c928..51ea226b4 100644
--- a/app/Book.php
+++ b/app/Book.php
@@ -2,6 +2,7 @@
 
 class Book extends Entity
 {
+    public $searchFactor = 2;
 
     protected $fillable = ['name', 'description', 'image_id'];
 
diff --git a/app/Chapter.php b/app/Chapter.php
index 6dab9dc47..3726c57f4 100644
--- a/app/Chapter.php
+++ b/app/Chapter.php
@@ -2,6 +2,8 @@
 
 class Chapter extends Entity
 {
+    public $searchFactor = 1.3;
+
     protected $fillable = ['name', 'description', 'priority', 'book_id'];
 
     protected $with = ['book'];
diff --git a/app/Entity.php b/app/Entity.php
index 1ea4e8dac..aeeab4960 100644
--- a/app/Entity.php
+++ b/app/Entity.php
@@ -5,8 +5,16 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
 class Entity extends Ownable
 {
 
+    /**
+     * @var string - Name of property where the main text content is found
+     */
     public $textField = 'description';
 
+    /**
+     * @var float - Multiplier for search indexing.
+     */
+    public $searchFactor = 1.0;
+
     /**
      * Compares this entity to another given entity.
      * Matches by comparing class and id.
@@ -193,4 +201,5 @@ class Entity extends Ownable
     {
         return '/';
     }
+
 }
diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php
index 0827eeb71..ddc92f705 100644
--- a/app/Http/Controllers/SearchController.php
+++ b/app/Http/Controllers/SearchController.php
@@ -40,13 +40,12 @@ class SearchController extends Controller
         $nextPageLink = baseUrl('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
 
         $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
-        $hasNextPage = $this->searchService->searchEntities($searchTerm, 'all', $page+1, 20)['count'] > 0;
 
         return view('search/all', [
             'entities'   => $results['results'],
             'totalResults' => $results['total'],
             'searchTerm' => $searchTerm,
-            'hasNextPage' => $hasNextPage,
+            'hasNextPage' => $results['has_more'],
             'nextPageLink' => $nextPageLink
         ]);
     }
diff --git a/app/Services/SearchService.php b/app/Services/SearchService.php
index 6786c5cf4..cb445eed1 100644
--- a/app/Services/SearchService.php
+++ b/app/Services/SearchService.php
@@ -72,7 +72,6 @@ class SearchService
         $terms = $this->parseSearchString($searchString);
         $entityTypes = array_keys($this->entities);
         $entityTypesToSearch = $entityTypes;
-        $results = collect();
 
         if ($entityType !== 'all') {
             $entityTypesToSearch = $entityType;
@@ -80,21 +79,23 @@ class SearchService
             $entityTypesToSearch = explode('|', $terms['filters']['type']);
         }
 
+        $results = collect();
         $total = 0;
 
         foreach ($entityTypesToSearch as $entityType) {
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
-            $search = $this->searchEntityTable($terms, $entityType, $page, $count);
-            $total += $this->searchEntityTable($terms, $entityType, $page, $count, true);
+            $search = $this->searchEntityTable($terms, $entityType, $page, $count + 1);
+            $total += $this->searchEntityTable($terms, $entityType, $page, $count + 1, true);
             $results = $results->merge($search);
         }
 
         return [
             'total' => $total,
             'count' => count($results),
-            'results' => $results->sortByDesc('score')->values()
+            'has_more' => $results->count() > $count,
+            'results' => $results->sortByDesc('score')->slice(0, $count)->values()
         ];
     }
 
@@ -322,8 +323,8 @@ class SearchService
     public function indexEntity(Entity $entity)
     {
         $this->deleteEntityTerms($entity);
-        $nameTerms = $this->generateTermArrayFromText($entity->name, 5);
-        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
+        $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+        $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
         $terms = array_merge($nameTerms, $bodyTerms);
         foreach ($terms as $index => $term) {
             $terms[$index]['entity_type'] = $entity->getMorphClass();
@@ -340,8 +341,8 @@ class SearchService
     {
         $terms = [];
         foreach ($entities as $entity) {
-            $nameTerms = $this->generateTermArrayFromText($entity->name, 5);
-            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1);
+            $nameTerms = $this->generateTermArrayFromText($entity->name, 5 * $entity->searchFactor);
+            $bodyTerms = $this->generateTermArrayFromText($entity->getText(), 1 * $entity->searchFactor);
             foreach (array_merge($nameTerms, $bodyTerms) as $term) {
                 $term['entity_id'] = $entity->id;
                 $term['entity_type'] = $entity->getMorphClass();