Skip to content

Instantly share code, notes, and snippets.

@jonhassall
Last active January 26, 2024 13:29
Show Gist options
  • Save jonhassall/80924255f555342eb475fd1d04dba4a2 to your computer and use it in GitHub Desktop.
Save jonhassall/80924255f555342eb475fd1d04dba4a2 to your computer and use it in GitHub Desktop.
Laravel Fulltext Search Trait with Weighting
<?php
namespace App\Traits;
/**
* Fulltext search trait. Requires column to have a FULLTEXT index
* Customizable weighted relevance per column
*
* Add to model:
*
* use Traits\FullTextSearch;
* protected $searchable = [
* ['column_name' => 'column1', 'weight' => 3],
* ['column_name' => 'column2', 'weight' => 1]
* ];
*
* \App\Model::search('searchterm', ['id', 'column1', 'column2', 'column3'])
* ->orderByDesc('weighted_relevance_score')
*
*/
trait FullTextSearch
{
/**
* Replaces spaces with full text search wildcards
*
* @param string $term
* @return string
*/
protected function fullTextWildcards($term)
{
//Remove symbols used by MySQL
$reservedSymbols = ['-', '+', '<', '>', '@', '(', ')', '~', '*'];
$term = str_replace($reservedSymbols, '', $term);
$words = explode(' ', $term);
foreach($words as $key => $word) {
/*
* applying + operator (required word) only big words
* because smaller ones are not indexed by mysql
*/
if(strlen($word) >= 3) {
$words[$key] = '+' . $word . '*';
}
}
$searchTerm = implode(' ', $words);
return $searchTerm;
}
/**
* Scope a query that matches a full text search of term.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $term Search term
* @param array $columns Array of columns to request. Leave it empty to request all columns. Unavoidable in fulltext query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeSearch($query, $term, $columns = [])
{
//List of columns to perform search on
$search_columns = implode(',', array_column($this->searchable, 'column_name'));
$searchableTerm = $this->fullTextWildcards($term);
//Columns to select
$columns_select_sql = '*';
if (count($columns) > 0)
{
$columns_select_sql = implode(',', $columns);
}
//Initial fulltext query
$query->selectRaw($columns_select_sql . ", MATCH ({$search_columns}) AGAINST (? IN BOOLEAN MODE) AS relevance_score", [$searchableTerm]);
//Weighted relevance query
$weighted_column_array = [];
foreach ($this->searchable as $weighted_column) {
array_push($weighted_column_array, $weighted_column['weight'] . ' * (MATCH (' . $weighted_column['column_name'] . ') AGAINST (?))');
}
$query->selectRaw( '(' . implode(' + ', $weighted_column_array) . ') as weighted_relevance_score', array_fill(0, count($this->searchable), $searchableTerm));
//Fulltext match
$query->whereRaw("MATCH ({$search_columns}) AGAINST (? IN BOOLEAN MODE)", $searchableTerm);
return $query;
}
}
@midsonlajeanty
Copy link

can you help me to fix this error : "Can't find FULLTEXT index matching the column list"

@jonhassall
Copy link
Author

can you help me to fix this error : "Can't find FULLTEXT index matching the column list"

Try this tutorial:
https://www.mysqltutorial.org/mysql-full-text-search/mysql-full-text-index/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment