feat: add Laravel Scount & TNTSearch

This commit is contained in:
Phan An 2020-12-23 11:53:00 +01:00
parent 094d07fad3
commit 84a72d284c
9 changed files with 422 additions and 22 deletions

View file

@ -9,22 +9,23 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Laravel\Scout\Searchable;
/** /**
* @property string $cover The album cover's file name * @property string $cover The album cover's file name
* @property string|null $cover_path The absolute path to the cover file * @property string|null $cover_path The absolute path to the cover file
* @property bool $has_cover If the album has a non-default cover image * @property bool $has_cover If the album has a non-default cover image
* @property int $id * @property int $id
* @property string $name Name of the album * @property string $name Name of the album
* @property bool $is_compilation If the album is a compilation from multiple artists * @property bool $is_compilation If the album is a compilation from multiple artists
* @property Artist $artist The album's artist * @property Artist $artist The album's artist
* @property int $artist_id * @property int $artist_id
* @property Collection $songs * @property Collection $songs
* @property bool $is_unknown If the album is the Unknown Album * @property bool $is_unknown If the album is the Unknown Album
* @property string|null $thumbnail_name The file name of the album's thumbnail * @property string|null $thumbnail_name The file name of the album's thumbnail
* @property string|null $thumbnail_path The full path to the thumbnail. * @property string|null $thumbnail_path The full path to the thumbnail.
* Notice that this doesn't guarantee the thumbnail exists. * Notice that this doesn't guarantee the thumbnail exists.
* @property string|null $thumbnail The public URL to the album's thumbnail * @property string|null $thumbnail The public URL to the album's thumbnail
* *
* @method static self firstOrCreate(array $where, array $params = []) * @method static self firstOrCreate(array $where, array $params = [])
* @method static self|null find(int $id) * @method static self|null find(int $id)
@ -35,6 +36,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
class Album extends Model class Album extends Model
{ {
use HasFactory; use HasFactory;
use Searchable;
use SupportsDeleteWhereIDsNotIn; use SupportsDeleteWhereIDsNotIn;
public const UNKNOWN_ID = 1; public const UNKNOWN_ID = 1;
@ -144,4 +146,19 @@ class Album extends Model
{ {
return $this->thumbnail_name ? album_cover_url($this->thumbnail_name) : null; return $this->thumbnail_name ? album_cover_url($this->thumbnail_name) : null;
} }
/** @return array<mixed> */
public function toSearchableArray(): array
{
$array = [
'id' => $this->id,
'name' => $this->name,
];
if (!$this->artist->is_unknown && !$this->artist->is_various) {
$array['artist'] = $this->artist->name;
}
return $array;
}
} }

View file

@ -10,15 +10,16 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Laravel\Scout\Searchable;
/** /**
* @property int $id * @property int $id
* @property string $name * @property string $name
* @property string|null $image Public URL to the artist's image * @property string|null $image Public URL to the artist's image
* @property bool $is_unknown If the artist is Unknown Artist * @property bool $is_unknown If the artist is Unknown Artist
* @property bool $is_various If the artist is Various Artist * @property bool $is_various If the artist is Various Artist
* @property Collection $songs * @property Collection $songs
* @property bool $has_image If the artist has a (non-default) image * @property bool $has_image If the artist has a (non-default) image
* @property string|null $image_path Absolute path to the artist's image * @property string|null $image_path Absolute path to the artist's image
* *
* @method static self find(int $id) * @method static self find(int $id)
@ -31,6 +32,7 @@ use Illuminate\Database\Eloquent\Relations\HasManyThrough;
class Artist extends Model class Artist extends Model
{ {
use HasFactory; use HasFactory;
use Searchable;
use SupportsDeleteWhereIDsNotIn; use SupportsDeleteWhereIDsNotIn;
public const UNKNOWN_ID = 1; public const UNKNOWN_ID = 1;
@ -122,4 +124,13 @@ class Artist extends Model
return file_exists(artist_image_path($image)); return file_exists(artist_image_path($image));
} }
/** @return array<mixed> */
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
} }

View file

@ -9,15 +9,16 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Laravel\Scout\Searchable;
/** /**
* @property int $user_id * @property int $user_id
* @property Collection $songs * @property Collection $songs
* @property int $id * @property int $id
* @property array $rules * @property array $rules
* @property bool $is_smart * @property bool $is_smart
* @property string $name * @property string $name
* @property user $user * @property user $user
* *
* @method static Builder orderBy(string $field, string $order = 'asc') * @method static Builder orderBy(string $field, string $order = 'asc')
*/ */
@ -25,6 +26,7 @@ class Playlist extends Model
{ {
use CanFilterByUser; use CanFilterByUser;
use HasFactory; use HasFactory;
use Searchable;
protected $hidden = ['user_id', 'created_at', 'updated_at']; protected $hidden = ['user_id', 'created_at', 'updated_at'];
protected $guarded = ['id']; protected $guarded = ['id'];
@ -48,4 +50,13 @@ class Playlist extends Model
{ {
return (bool) $this->rules; return (bool) $this->rules;
} }
/** @return array<mixed> */
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
} }

View file

@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Laravel\Scout\Searchable;
/** /**
* @property string $path * @property string $path
@ -41,6 +42,7 @@ use Illuminate\Support\Collection;
class Song extends Model class Song extends Model
{ {
use HasFactory; use HasFactory;
use Searchable;
use SupportsDeleteWhereIDsNotIn; use SupportsDeleteWhereIDsNotIn;
protected $guarded = []; protected $guarded = [];
@ -241,6 +243,25 @@ class Song extends Model
return compact('bucket', 'key'); return compact('bucket', 'key');
} }
/** @return array<mixed> */
public function toSearchableArray(): array
{
$array = [
'id' => $this->id,
'title' => $this->title,
];
if (!$this->artist->is_unknown && !$this->artist->is_various) {
$array['artist'] = $this->artist->name;
}
if (!$this->album->is_unknown) {
$array['album'] = $this->album->name;
}
return $array;
}
public function __toString(): string public function __toString(): string
{ {
return $this->id; return $this->id;

View file

@ -26,7 +26,8 @@
"intervention/image": "^2.5", "intervention/image": "^2.5",
"laravel/sanctum": "^2.6", "laravel/sanctum": "^2.6",
"doctrine/dbal": "^2.10", "doctrine/dbal": "^2.10",
"lstrojny/functional-php": "^1.14" "lstrojny/functional-php": "^1.14",
"teamtnt/laravel-scout-tntsearch-driver": "^11.1"
}, },
"require-dev": { "require-dev": {
"mockery/mockery": "~1.0", "mockery/mockery": "~1.0",

219
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "becead0effd39fcac75e3efa67c6f3af", "content-hash": "e6435a1c6c0bb1c2e691c892936a2366",
"packages": [ "packages": [
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
@ -1664,6 +1664,75 @@
}, },
"time": "2020-11-24T17:31:19+00:00" "time": "2020-11-24T17:31:19+00:00"
}, },
{
"name": "laravel/scout",
"version": "v8.5.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/scout.git",
"reference": "b8b8a35cc3ac82cbc07dfa83955492d2ef6e237b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/scout/zipball/b8b8a35cc3ac82cbc07dfa83955492d2ef6e237b",
"reference": "b8b8a35cc3ac82cbc07dfa83955492d2ef6e237b",
"shasum": ""
},
"require": {
"illuminate/bus": "^6.0|^7.0|^8.0",
"illuminate/contracts": "^6.0|^7.0|^8.0",
"illuminate/database": "^6.0|^7.0|^8.0",
"illuminate/http": "^6.0|^7.0|^8.0",
"illuminate/pagination": "^6.0|^7.0|^8.0",
"illuminate/queue": "^6.0|^7.0|^8.0",
"illuminate/support": "^6.0|^7.0|^8.0",
"php": "^7.2|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^8.0|^9.3"
},
"suggest": {
"algolia/algoliasearch-client-php": "Required to use the Algolia engine (^2.2)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "8.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Scout\\ScoutServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Scout\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Laravel Scout provides a driver based solution to searching your Eloquent models.",
"keywords": [
"algolia",
"laravel",
"search"
],
"support": {
"issues": "https://github.com/laravel/scout/issues",
"source": "https://github.com/laravel/scout"
},
"time": "2020-12-22T17:29:20+00:00"
},
{ {
"name": "league/commonmark", "name": "league/commonmark",
"version": "1.5.7", "version": "1.5.7",
@ -5482,6 +5551,154 @@
], ],
"time": "2020-12-16T17:02:19+00:00" "time": "2020-12-16T17:02:19+00:00"
}, },
{
"name": "teamtnt/laravel-scout-tntsearch-driver",
"version": "v11.1.0",
"source": {
"type": "git",
"url": "https://github.com/teamtnt/laravel-scout-tntsearch-driver.git",
"reference": "a9c27a68dc2bd74fb354165633520de95708215d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/teamtnt/laravel-scout-tntsearch-driver/zipball/a9c27a68dc2bd74fb354165633520de95708215d",
"reference": "a9c27a68dc2bd74fb354165633520de95708215d",
"shasum": ""
},
"require": {
"illuminate/bus": "~5.4|^6.0|^7.0|^8.0",
"illuminate/contracts": "~5.4|^6.0|^7.0|^8.0",
"illuminate/database": "~5.4|^6.0|^7.0|^8.0",
"illuminate/pagination": "~5.4|^6.0|^7.0|^8.0",
"illuminate/queue": "~5.4|^6.0|^7.0|^8.0",
"illuminate/support": "~5.4|^6.0|^7.0|^8.0",
"laravel/scout": "7.*|^8.0|^8.3",
"php": ">=7.1",
"teamtnt/tntsearch": "2.*"
},
"require-dev": {
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^8.0|^9.3"
},
"suggest": {
"teamtnt/tntsearch": "Required to use the TNTSearch engine."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
},
"laravel": {
"providers": [
"TeamTNT\\Scout\\TNTSearchScoutServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"TeamTNT\\Scout\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "TNT Studio",
"email": "info@tntstudio.hr"
}
],
"description": "Driver for Laravel Scout search package based on https://github.com/teamtnt/tntsearch",
"keywords": [
"laravel",
"scout",
"search",
"tntsearch"
],
"support": {
"issues": "https://github.com/teamtnt/laravel-scout-tntsearch-driver/issues",
"source": "https://github.com/teamtnt/laravel-scout-tntsearch-driver/tree/v11.1.0"
},
"time": "2020-11-11T11:17:48+00:00"
},
{
"name": "teamtnt/tntsearch",
"version": "v2.6.0",
"source": {
"type": "git",
"url": "https://github.com/teamtnt/tntsearch.git",
"reference": "d9b2d764491c87f03ec214ed8dbc27336cf0c0e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/teamtnt/tntsearch/zipball/d9b2d764491c87f03ec214ed8dbc27336cf0c0e4",
"reference": "d9b2d764491c87f03ec214ed8dbc27336cf0c0e4",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-pdo_sqlite": "*",
"ext-sqlite3": "*",
"php": "~7.1|^8"
},
"require-dev": {
"phpunit/phpunit": "7.*"
},
"type": "library",
"autoload": {
"psr-4": {
"TeamTNT\\TNTSearch\\": "src"
},
"files": [
"helper/helpers.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nenad Tičarić",
"email": "nticaric@gmail.com",
"homepage": "http://www.tntstudio.us",
"role": "Developer"
}
],
"description": "A fully featured full text search engine written in PHP",
"homepage": "https://github.com/teamtnt/tntsearch",
"keywords": [
"Fuzzy search",
"bm25",
"fulltext",
"geosearch",
"search",
"stemming",
"teamtnt",
"text classification",
"tntsearch"
],
"support": {
"issues": "https://github.com/teamtnt/tntsearch/issues",
"source": "https://github.com/teamtnt/tntsearch/tree/v2.6.0"
},
"funding": [
{
"url": "https://ko-fi.com/nticaric",
"type": "ko_fi"
},
{
"url": "https://opencollective.com/tntsearch",
"type": "open_collective"
},
{
"url": "https://www.patreon.com/nticaric",
"type": "patreon"
}
],
"time": "2020-12-21T09:11:54+00:00"
},
{ {
"name": "tijsverkoyen/css-to-inline-styles", "name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.3", "version": "2.2.3",

View file

@ -127,6 +127,9 @@ return [
Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class, Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class,
Intervention\Image\ImageServiceProvider::class, Intervention\Image\ImageServiceProvider::class,
Laravel\Scout\ScoutServiceProvider::class,
TeamTNT\Scout\TNTSearchScoutServiceProvider::class,
/* /*
* Application Service Providers... * Application Service Providers...
*/ */

117
config/scout.php Normal file
View file

@ -0,0 +1,117 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Search Engine
|--------------------------------------------------------------------------
|
| This option controls the default search connection that gets used while
| using Laravel Scout. This connection is used when syncing all models
| to the search service. You should adjust this based on your needs.
|
| Supported: "algolia", "null"
|
*/
'driver' => env('SCOUT_DRIVER', 'tntsearch'),
/*
|--------------------------------------------------------------------------
| Index Prefix
|--------------------------------------------------------------------------
|
| Here you may specify a prefix that will be applied to all search index
| names used by Scout. This prefix may be useful if you have multiple
| "tenants" or applications sharing the same search infrastructure.
|
*/
'prefix' => env('SCOUT_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Queue Data Syncing
|--------------------------------------------------------------------------
|
| This option allows you to control if the operations that sync your data
| with your search engines are queued. When this is set to "true" then
| all automatic data syncing will get queued for better performance.
|
*/
'queue' => env('SCOUT_QUEUE', false),
/*
|--------------------------------------------------------------------------
| Chunk Sizes
|--------------------------------------------------------------------------
|
| These options allow you to control the maximum chunk size when you are
| mass importing data into the search engine. This allows you to fine
| tune each of these chunk sizes based on the power of the servers.
|
*/
'chunk' => [
'searchable' => 500,
'unsearchable' => 500,
],
/*
|--------------------------------------------------------------------------
| Soft Deletes
|--------------------------------------------------------------------------
|
| This option allows to control whether to keep soft deleted records in
| the search indexes. Maintaining soft deleted records can be useful
| if your application still needs to search for the records later.
|
*/
'soft_delete' => false,
/*
|--------------------------------------------------------------------------
| Identify User
|--------------------------------------------------------------------------
|
| This option allows you to control whether to notify the search engine
| of the user performing the search. This is sometimes useful if the
| engine supports any analytics based on this application's users.
|
| Supported engines: "algolia"
|
*/
'identify' => env('SCOUT_IDENTIFY', false),
/*
|--------------------------------------------------------------------------
| Algolia Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your Algolia settings. Algolia is a cloud hosted
| search engine which works great with Scout out of the box. Just plug
| in your application ID and admin API key to get started searching.
|
*/
'algolia' => [
'id' => env('ALGOLIA_APP_ID', ''),
'secret' => env('ALGOLIA_SECRET', ''),
],
'tntsearch' => [
'storage' => storage_path('search-indexes'),
'fuzziness' => env('TNTSEARCH_FUZZINESS', true),
'fuzzy' => [
'prefix_length' => 2,
'max_expansions' => 50,
'distance' => 2,
],
'asYouType' => false,
'searchBoolean' => env('TNTSEARCH_BOOLEAN', false),
],
];

2
storage/search-indexes/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore