]> BookStack Code Mirror - bookstack/commitdiff
Added Bookshelves to search system.
authorDan Brown <redacted>
Sun, 23 Sep 2018 11:34:30 +0000 (12:34 +0100)
committerDan Brown <redacted>
Sun, 23 Sep 2018 11:34:30 +0000 (12:34 +0100)
Also cleaned up and made search indexing system a little more efficient.
Closes #1023

app/Services/SearchService.php
resources/assets/js/vues/search.js
resources/lang/en/entities.php
resources/views/search/all.blade.php
tests/Entity/EntitySearchTest.php

index 6390b8bc4582f3c348cf6a534544c25499ffb5d0..dfde645a1727bcd2c97a60a5090f82919bfd4fcd 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Services;
 
 use BookStack\Book;
+use BookStack\Bookshelf;
 use BookStack\Chapter;
 use BookStack\Entity;
 use BookStack\Page;
@@ -13,11 +14,16 @@ use Illuminate\Support\Collection;
 class SearchService
 {
     protected $searchTerm;
+    protected $bookshelf;
     protected $book;
     protected $chapter;
     protected $page;
     protected $db;
     protected $permissionService;
+
+    /**
+     * @var Entity[]
+     */
     protected $entities;
 
     /**
@@ -29,20 +35,23 @@ class SearchService
     /**
      * SearchService constructor.
      * @param SearchTerm $searchTerm
+     * @param Bookshelf $bookshelf
      * @param Book $book
      * @param Chapter $chapter
      * @param Page $page
      * @param Connection $db
      * @param PermissionService $permissionService
      */
-    public function __construct(SearchTerm $searchTerm, Book $book, Chapter $chapter, Page $page, Connection $db, PermissionService $permissionService)
+    public function __construct(SearchTerm $searchTerm, Bookshelf $bookshelf, Book $book, Chapter $chapter, Page $page, Connection $db, PermissionService $permissionService)
     {
         $this->searchTerm = $searchTerm;
+        $this->bookshelf = $bookshelf;
         $this->book = $book;
         $this->chapter = $chapter;
         $this->page = $page;
         $this->db = $db;
         $this->entities = [
+            'bookshelf' => $this->bookshelf,
             'page' => $this->page,
             'chapter' => $this->chapter,
             'book' => $this->book
@@ -65,6 +74,7 @@ class SearchService
      * @param string $entityType
      * @param int $page
      * @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
+     * @param string $action
      * @return array[int, Collection];
      */
     public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view')
@@ -370,20 +380,12 @@ class SearchService
     {
         $this->searchTerm->truncate();
 
-        // Chunk through all books
-        $this->book->chunk(1000, function ($books) {
-            $this->indexEntities($books);
-        });
-
-        // Chunk through all chapters
-        $this->chapter->chunk(1000, function ($chapters) {
-            $this->indexEntities($chapters);
-        });
-
-        // Chunk through all pages
-        $this->page->chunk(1000, function ($pages) {
-            $this->indexEntities($pages);
-        });
+        foreach ($this->entities as $entityModel) {
+            $selectFields = ['id', 'name', $entityModel->textField];
+            $entityModel->newQuery()->select($selectFields)->chunk(1000, function ($entities) {
+                $this->indexEntities($entities);
+            });
+        }
     }
 
     /**
index c0410463323014d4f94d00de30a118bbe8b2b896..2224e8dddd4121763df56f47a65c59bff192cf65 100644 (file)
@@ -7,7 +7,8 @@ let data = {
         type: {
             page: true,
             chapter: true,
-            book: true
+            book: true,
+            bookshelf: true,
         },
         exactTerms: [],
         tagTerms: [],
@@ -46,11 +47,7 @@ let methods = {
     exactChange() {
         let exactFilter = /"(.+?)"/g;
         this.termString = this.termString.replace(exactFilter, '');
-        let matchesTerm = this.search.exactTerms.filter(term => {
-            return term.trim() !== '';
-        }).map(term => {
-            return `"${term}"`
-        }).join(' ');
+        let matchesTerm = this.search.exactTerms.filter(term =>  term.trim() !== '').map(term => `"${term}"`).join(' ');
         this.appendTerm(matchesTerm);
     },
 
@@ -105,23 +102,24 @@ let methods = {
         let match = searchString.match(typeFilter);
         let type = this.search.type;
         if (!match) {
-            type.page = type.book = type.chapter = true;
+            type.page = type.book = type.chapter = type.bookshelf = true;
             return;
         }
         let splitTypes = match[1].replace(/ /g, '').split('|');
         type.page = (splitTypes.indexOf('page') !== -1);
         type.chapter = (splitTypes.indexOf('chapter') !== -1);
         type.book = (splitTypes.indexOf('book') !== -1);
+        type.bookshelf = (splitTypes.indexOf('bookshelf') !== -1);
     },
 
     typeChange() {
         let typeFilter = /{\s?type:\s?(.*?)\s?}/;
         let type = this.search.type;
-        if (type.page === type.chapter && type.page === type.book) {
+        if (type.page === type.chapter === type.book === type.bookshelf) {
             this.termString = this.termString.replace(typeFilter, '');
             return;
         }
-        let selectedTypes = Object.keys(type).filter(type => {return this.search.type[type];}).join('|');
+        let selectedTypes = Object.keys(type).filter(type => this.search.type[type]).join('|');
         let typeTerm = '{type:'+selectedTypes+'}';
         if (this.termString.match(typeFilter)) {
             this.termString = this.termString.replace(typeFilter, typeTerm);
index 55fac5d8fc0ada0757520a416a7a07a7ac4b9b30..810b25f456d1b7a6d9962b336298abaef757f265 100644 (file)
@@ -69,6 +69,7 @@ return [
     /**
      * Shelves
      */
+    'shelf' => 'Shelf',
     'shelves' => 'Shelves',
     'shelves_long' => 'Bookshelves',
     'shelves_empty' => 'No shelves have been created',
index 8274187e11c97f03701b3b0c891347095ab3ff7d..a952079d566453b3893603cbfc81fd39ddce4103 100644 (file)
@@ -22,7 +22,9 @@
                 <div class="form-group">
                     <label class="inline checkbox text-page"><input type="checkbox" v-on:change="typeChange" v-model="search.type.page" value="page">{{ trans('entities.page') }}</label>
                     <label class="inline checkbox text-chapter"><input type="checkbox" v-on:change="typeChange" v-model="search.type.chapter" value="chapter">{{ trans('entities.chapter') }}</label>
+                    <br>
                     <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
+                    <label class="inline checkbox text-bookshelf"><input type="checkbox" v-on:change="typeChange" v-model="search.type.bookshelf" value="bookshelf">{{ trans('entities.shelf') }}</label>
                 </div>
 
                 <h6 class="text-muted">{{ trans('entities.search_exact_matches') }}</h6>
index 28c0f17646a2b874de14aa2c99927f796fbecf98..4b2fd56630c703ca40d8fb657cfd76c548a9627d 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace Tests;
 
 
+use BookStack\Bookshelf;
 use BookStack\Chapter;
 use BookStack\Page;
 
@@ -17,6 +18,14 @@ class EntitySearchTest extends TestCase
         $search->assertSee($page->name);
     }
 
+    public function test_bookshelf_search()
+    {
+        $shelf = Bookshelf::first();
+        $search = $this->asEditor()->get('/search?term=' . urlencode(mb_substr($shelf->name, 0, 3)) . '  {type:bookshelf}');
+        $search->assertStatus(200);
+        $search->assertSee($shelf->name);
+    }
+
     public function test_invalid_page_search()
     {
         $resp = $this->asEditor()->get('/search?term=' . urlencode('<p>test</p>'));