mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-17 08:49:00 +08:00
Added Bookshelves to search system.
Also cleaned up and made search indexing system a little more efficient. Closes #1023
This commit is contained in:
parent
eebfd8904e
commit
7b32aa163f
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -69,6 +69,7 @@ return [
|
||||
/**
|
||||
* Shelves
|
||||
*/
|
||||
'shelf' => 'Shelf',
|
||||
'shelves' => 'Shelves',
|
||||
'shelves_long' => 'Bookshelves',
|
||||
'shelves_empty' => 'No shelves have been created',
|
||||
|
@ -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>
|
||||
|
@ -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>'));
|
||||
|
Loading…
x
Reference in New Issue
Block a user