James Brooks
9 years ago
40 changed files with 753 additions and 723 deletions
-
8.gitattributes
-
4composer.json
-
25config/searchy.php
-
21src/Facades/Searchy.php
-
16src/Interfaces/MatcherInterface.php
-
12src/Interfaces/SearchDriverInterface.php
-
30src/Matchers/AcronymMatcher.php
-
36src/Matchers/BaseMatcher.php
-
44src/Matchers/ConsecutiveCharactersMatcher.php
-
38src/Matchers/ExactMatcher.php
-
28src/Matchers/InStringMatcher.php
-
24src/Matchers/LevenshteinMatcher.php
-
28src/Matchers/StartOfStringMatcher.php
-
28src/Matchers/StartOfWordsMatcher.php
-
35src/Matchers/StudlyCaseMatcher.php
-
58src/Matchers/TimesInStringMatcher.php
-
111src/SearchBuilder.php
-
154src/SearchDrivers/BaseSearchDriver.php
-
20src/SearchDrivers/FuzzySearchDriver.php
-
13src/SearchDrivers/LevenshteinSearchDriver.php
-
15src/SearchDrivers/SimpleSearchDriver.php
-
56src/SearchyServiceProvider.php
-
19src/TomLingham/Searchy/Facades/Searchy.php
-
14src/TomLingham/Searchy/Interfaces/MatcherInterface.php
-
12src/TomLingham/Searchy/Interfaces/SearchDriverInterface.php
-
28src/TomLingham/Searchy/Matchers/AcronymMatcher.php
-
34src/TomLingham/Searchy/Matchers/BaseMatcher.php
-
40src/TomLingham/Searchy/Matchers/ConsecutiveCharactersMatcher.php
-
28src/TomLingham/Searchy/Matchers/InStringMatcher.php
-
25src/TomLingham/Searchy/Matchers/LevenshteinMatcher.php
-
28src/TomLingham/Searchy/Matchers/StartOfStringMatcher.php
-
28src/TomLingham/Searchy/Matchers/StartOfWordsMatcher.php
-
34src/TomLingham/Searchy/Matchers/StudlyCaseMatcher.php
-
110src/TomLingham/Searchy/SearchBuilder.php
-
147src/TomLingham/Searchy/SearchDrivers/BaseSearchDriver.php
-
19src/TomLingham/Searchy/SearchDrivers/FuzzySearchDriver.php
-
12src/TomLingham/Searchy/SearchDrivers/LevenshteinSearchDriver.php
-
14src/TomLingham/Searchy/SearchDrivers/SimpleSearchDriver.php
-
52src/TomLingham/Searchy/SearchyServiceProvider.php
-
28src/config/config.php
@ -0,0 +1,8 @@ |
|||||
|
* text=auto |
||||
|
|
||||
|
/tests export-ignore |
||||
|
/.gitattributes export-ignore |
||||
|
/.gitignore export-ignore |
||||
|
/.travis.yml export-ignore |
||||
|
/phpunit.xml export-ignore |
||||
|
/README.md export-ignore |
@ -0,0 +1,25 @@ |
|||||
|
<?php |
||||
|
|
||||
|
return [ |
||||
|
|
||||
|
'default' => 'fuzzy', |
||||
|
|
||||
|
'fieldName' => 'relevance', |
||||
|
|
||||
|
'drivers' => [ |
||||
|
|
||||
|
'fuzzy' => [ |
||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\FuzzySearchDriver', |
||||
|
], |
||||
|
|
||||
|
'simple' => [ |
||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\SimpleSearchDriver', |
||||
|
], |
||||
|
|
||||
|
'levenshtein' => [ |
||||
|
'class' => 'TomLingham\Searchy\SearchDrivers\LevenshteinSearchDriver', |
||||
|
], |
||||
|
|
||||
|
], |
||||
|
|
||||
|
]; |
@ -0,0 +1,21 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Facades; |
||||
|
|
||||
|
use Illuminate\Support\Facades\Facade; |
||||
|
|
||||
|
/** |
||||
|
* Searchy facade for the Laravel framework. |
||||
|
*/ |
||||
|
class Searchy extends Facade |
||||
|
{ |
||||
|
/** |
||||
|
* Get the registered component. |
||||
|
* |
||||
|
* @return object |
||||
|
*/ |
||||
|
protected static function getFacadeAccessor() |
||||
|
{ |
||||
|
return 'searchy'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Interfaces; |
||||
|
|
||||
|
interface MatcherInterface |
||||
|
{ |
||||
|
/** |
||||
|
* Builds the string to add to the SELECT statement for the Matcher. |
||||
|
* |
||||
|
* @param $column |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function buildQueryString($column, $searchString); |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Interfaces; |
||||
|
|
||||
|
interface SearchDriverInterface |
||||
|
{ |
||||
|
public function query($searchString); |
||||
|
|
||||
|
public function select(/* $columns */); |
||||
|
|
||||
|
public function get(); |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches. |
||||
|
* |
||||
|
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'. |
||||
|
* |
||||
|
* Class AcronymMatcher |
||||
|
*/ |
||||
|
class AcronymMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return mixed|string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
||||
|
|
||||
|
return implode('% ', str_split(strtoupper($searchString))).'%'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
use TomLingham\Searchy\Interfaces\MatcherInterface; |
||||
|
|
||||
|
/** |
||||
|
* @property mixed multiplier |
||||
|
* @property mixed operator |
||||
|
*/ |
||||
|
abstract class BaseMatcher implements MatcherInterface |
||||
|
{ |
||||
|
protected $multiplier; |
||||
|
|
||||
|
public function __construct($multiplier) |
||||
|
{ |
||||
|
$this->multiplier = $multiplier; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* The default process for building the Query string. |
||||
|
* |
||||
|
* @param $column |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return mixed|string |
||||
|
*/ |
||||
|
public function buildQueryString($column, $searchString) |
||||
|
{ |
||||
|
if (method_exists($this, 'formatSearchString')) { |
||||
|
$searchString = $this->formatSearchString($searchString); |
||||
|
} |
||||
|
|
||||
|
return "IF($column {$this->operator} '$searchString', {$this->multiplier}, 0)"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches strings that include all the characters in the search relatively position within the string. |
||||
|
* It also calculates the percentage of characters in the string that are matched and applies the multiplier accordingly. |
||||
|
* |
||||
|
* For Example, a search for 'fba' would match; 'Foo Bar' or 'Afraid of bats' |
||||
|
* |
||||
|
* Class ConsecutiveCharactersMatcher |
||||
|
*/ |
||||
|
class ConsecutiveCharactersMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
||||
|
|
||||
|
return '%'.implode('%', str_split($searchString)).'%'; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $column |
||||
|
* @param $rawString |
||||
|
* |
||||
|
* @return mixed|string |
||||
|
*/ |
||||
|
public function buildQueryString($column, $rawString) |
||||
|
{ |
||||
|
$searchString = $this->formatSearchString($rawString); |
||||
|
|
||||
|
return "IF(REPLACE($column, '\.', '') {$this->operator} '$searchString', ROUND({$this->multiplier} * (CHAR_LENGTH( '$rawString' ) / CHAR_LENGTH( REPLACE($column, ' ', '') ))), 0)"; |
||||
|
} |
||||
|
} |
@ -1,20 +1,18 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches an exact string and applies a high multiplier to bring any exact matches to the top |
|
||||
* When sanitize is on, if the expression strips some of the characters from the search query |
|
||||
* then this may not be able to match against a string despite entering in an exact match. |
|
||||
* |
|
||||
* Class ExactMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class ExactMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = '='; |
|
||||
|
|
||||
} |
|
||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches an exact string and applies a high multiplier to bring any exact matches to the top |
||||
|
* When sanitize is on, if the expression strips some of the characters from the search query |
||||
|
* then this may not be able to match against a string despite entering in an exact match. |
||||
|
* |
||||
|
* Class ExactMatcher |
||||
|
*/ |
||||
|
class ExactMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = '='; |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches against any occurrences of a string within a string and is case-insensitive. |
||||
|
* |
||||
|
* For example, a search for 'smi' would match; 'John Smith' or 'Smiley Face' |
||||
|
* |
||||
|
* Class InStringMatcher |
||||
|
*/ |
||||
|
class InStringMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
return "%$searchString%"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches. |
||||
|
* |
||||
|
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'. |
||||
|
* |
||||
|
* Class AcronymMatcher |
||||
|
*/ |
||||
|
class LevenshteinMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @param $column |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return mixed|string |
||||
|
*/ |
||||
|
public function buildQueryString($column, $searchString) |
||||
|
{ |
||||
|
return "levenshtein($column, '$searchString')"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches Strings that begin with the search string. |
||||
|
* |
||||
|
* For example, a search for 'hel' would match; 'Hello World' or 'helping hand' |
||||
|
* |
||||
|
* Class StartOfStringMatcher |
||||
|
*/ |
||||
|
class StartOfStringMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
return "$searchString%"; |
||||
|
} |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches the start of each word against each word in a search. |
||||
|
* |
||||
|
* For example, a search for 'jo ta' would match; 'John Taylor' or 'Joshua B. Takashi' |
||||
|
* |
||||
|
* Class StartOfWordsMatcher |
||||
|
*/ |
||||
|
class StartOfWordsMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
return implode('% ', explode(' ', $searchString)).'%'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches Studly Case strings using the first letters of the words only. |
||||
|
* |
||||
|
* For example a search for 'hp' would match; 'HtmlServiceProvider' or 'HashParser' but not 'hasProvider' |
||||
|
* |
||||
|
* Class StudlyCaseMatcher |
||||
|
*/ |
||||
|
class StudlyCaseMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @var string |
||||
|
*/ |
||||
|
protected $operator = 'LIKE BINARY'; |
||||
|
|
||||
|
/** |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
public function formatSearchString($searchString) |
||||
|
{ |
||||
|
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
||||
|
|
||||
|
return implode('%', str_split(strtoupper($searchString))).'%'; |
||||
|
} |
||||
|
|
||||
|
public function buildQueryString($column, $searchString) |
||||
|
{ |
||||
|
return "IF( CHAR_LENGTH( TRIM($column)) = CHAR_LENGTH( REPLACE( TRIM($column), ' ', '')) AND $column {$this->operator} '{$this->formatSearchString($searchString)}', {$this->multiplier}, 0)"; |
||||
|
} |
||||
|
} |
@ -1,29 +1,29 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches a string based on how many times the search string appears inside the string |
|
||||
* it then applies the multiplier for each occurrence. |
|
||||
* |
|
||||
* For example, a search for 'tha' would match; 'I hope that that cat has caught that mouse' (3 x multiplier) or 'Thanks, it was great!' (1 x multiplier) |
|
||||
* |
|
||||
* Class TimesInStringMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class TimesInStringMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @param $column |
|
||||
* @param $searchString |
|
||||
* @return mixed|string |
|
||||
*/ |
|
||||
public function buildQueryString( $column, $searchString ) |
|
||||
{ |
|
||||
$query = "{$this->multiplier} * ROUND ((
|
|
||||
CHAR_LENGTH($column) - CHAR_LENGTH( REPLACE ( LOWER($column), lower('$searchString'), '')) |
|
||||
) / LENGTH('$searchString'))";
|
|
||||
|
|
||||
return $query; |
|
||||
} |
|
||||
} |
|
||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\Matchers; |
||||
|
|
||||
|
/** |
||||
|
* Matches a string based on how many times the search string appears inside the string |
||||
|
* it then applies the multiplier for each occurrence. |
||||
|
* |
||||
|
* For example, a search for 'tha' would match; 'I hope that that cat has caught that mouse' (3 x multiplier) or 'Thanks, it was great!' (1 x multiplier) |
||||
|
* |
||||
|
* Class TimesInStringMatcher |
||||
|
*/ |
||||
|
class TimesInStringMatcher extends BaseMatcher |
||||
|
{ |
||||
|
/** |
||||
|
* @param $column |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return mixed|string |
||||
|
*/ |
||||
|
public function buildQueryString($column, $searchString) |
||||
|
{ |
||||
|
$query = "{$this->multiplier} * ROUND ((
|
||||
|
CHAR_LENGTH($column) - CHAR_LENGTH( REPLACE ( LOWER($column), lower('$searchString'), '')) |
||||
|
) / LENGTH('$searchString'))";
|
||||
|
|
||||
|
return $query; |
||||
|
} |
||||
|
} |
@ -0,0 +1,111 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy; |
||||
|
|
||||
|
use Illuminate\Config\Repository; |
||||
|
use TomLingham\Searchy\SearchDrivers\FuzzySearchDriver; |
||||
|
|
||||
|
/** |
||||
|
* @property mixed driverName |
||||
|
*/ |
||||
|
class SearchBuilder |
||||
|
{ |
||||
|
/** |
||||
|
* @var |
||||
|
*/ |
||||
|
private $table; |
||||
|
|
||||
|
/** |
||||
|
* @var |
||||
|
*/ |
||||
|
private $searchFields; |
||||
|
|
||||
|
/** |
||||
|
* @var |
||||
|
*/ |
||||
|
private $driverName; |
||||
|
|
||||
|
/** |
||||
|
* @var |
||||
|
*/ |
||||
|
private $config; |
||||
|
|
||||
|
public function __construct(Repository $config) |
||||
|
{ |
||||
|
$this->config = $config; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $searchable |
||||
|
* |
||||
|
* @return $this |
||||
|
*/ |
||||
|
public function search($searchable) |
||||
|
{ |
||||
|
if (is_object($searchable) && method_exists($searchable, 'getTable')) { |
||||
|
$this->table = $searchable->getTable(); |
||||
|
} else { |
||||
|
$this->table = $searchable; |
||||
|
} |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return FuzzySearchDriver |
||||
|
*/ |
||||
|
public function fields(/* $fields */) |
||||
|
{ |
||||
|
$this->searchFields = func_get_args(); |
||||
|
|
||||
|
return $this->makeDriver(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $driverName |
||||
|
* |
||||
|
* @return $this |
||||
|
*/ |
||||
|
public function driver($driverName) |
||||
|
{ |
||||
|
$this->driverName = $driverName; |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $table |
||||
|
* @param $searchFields |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function __call($table, $searchFields) |
||||
|
{ |
||||
|
return call_user_func_array([$this->search($table), 'fields'], $searchFields); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
private function makeDriver() |
||||
|
{ |
||||
|
$relevanceFieldName = $this->config->get('searchy.fieldName'); |
||||
|
|
||||
|
// Check if default driver is being overridden, otherwise
|
||||
|
// load the default
|
||||
|
if ($this->driverName) { |
||||
|
$driverName = $this->driverName; |
||||
|
} else { |
||||
|
$driverName = $this->config->get('searchy.default'); |
||||
|
} |
||||
|
|
||||
|
// Gets the details for the selected driver from the configuration file
|
||||
|
$driver = $this->config->get("searchy.drivers.$driverName")['class']; |
||||
|
|
||||
|
// Create a new instance of the selected drivers 'class' and pass
|
||||
|
// through table and fields to search
|
||||
|
$driverInstance = new $driver($this->table, $this->searchFields, $relevanceFieldName); |
||||
|
|
||||
|
return $driverInstance; |
||||
|
} |
||||
|
} |
@ -0,0 +1,154 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\SearchDrivers; |
||||
|
|
||||
|
use TomLingham\Searchy\Interfaces\SearchDriverInterface; |
||||
|
|
||||
|
abstract class BaseSearchDriver implements SearchDriverInterface |
||||
|
{ |
||||
|
protected $table; |
||||
|
protected $columns; |
||||
|
protected $searchFields; |
||||
|
protected $searchString; |
||||
|
protected $relevanceFieldName; |
||||
|
protected $query; |
||||
|
|
||||
|
/** |
||||
|
* @param null $table |
||||
|
* @param array $searchFields |
||||
|
* @param $relevanceFieldName |
||||
|
* @param array $columns |
||||
|
* |
||||
|
* @internal param $relevanceField |
||||
|
*/ |
||||
|
public function __construct($table = null, $searchFields = [], $relevanceFieldName, $columns = ['*']) |
||||
|
{ |
||||
|
$this->searchFields = $searchFields; |
||||
|
$this->table = $table; |
||||
|
$this->columns = $columns; |
||||
|
$this->relevanceFieldName = $relevanceFieldName; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Specify which columns to return. |
||||
|
* |
||||
|
* @return $this |
||||
|
*/ |
||||
|
public function select() |
||||
|
{ |
||||
|
$this->columns = func_get_args(); |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Specify the string that is is being searched for. |
||||
|
* |
||||
|
* @param $searchString |
||||
|
* |
||||
|
* @return \Illuminate\Database\Query\Builder|mixed|static |
||||
|
*/ |
||||
|
public function query($searchString) |
||||
|
{ |
||||
|
$this->searchString = trim(\DB::connection()->getPdo()->quote($searchString), "'"); |
||||
|
|
||||
|
return $this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the results of the search as an Array. |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function get() |
||||
|
{ |
||||
|
return $this->run()->get(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns an instance of the Laravel Fluent Database Query Object with the search |
||||
|
* queries applied. |
||||
|
* |
||||
|
* @return array |
||||
|
*/ |
||||
|
public function getQuery() |
||||
|
{ |
||||
|
return $this->run(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Runs the 'having' method directly on the Laravel Fluent Database Query Object |
||||
|
* and returns the instance of the object. |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
public function having() |
||||
|
{ |
||||
|
return call_user_func_array([$this->run(), 'having'], func_get_args()); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @return $this |
||||
|
*/ |
||||
|
protected function run() |
||||
|
{ |
||||
|
$this->query = \DB::table($this->table) |
||||
|
->select($this->columns) |
||||
|
->addSelect($this->buildSelectQuery($this->searchFields)) |
||||
|
->orderBy($this->relevanceFieldName, 'desc') |
||||
|
->having($this->relevanceFieldName, '>', 0); |
||||
|
|
||||
|
return $this->query; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param array $searchFields |
||||
|
* |
||||
|
* @return array|\Illuminate\Database\Query\Expression |
||||
|
*/ |
||||
|
protected function buildSelectQuery(array $searchFields) |
||||
|
{ |
||||
|
$query = []; |
||||
|
|
||||
|
foreach ($searchFields as $searchField) { |
||||
|
if (strpos($searchField, '::')) { |
||||
|
$concatString = str_replace('::', ", ' ', ", $searchField); |
||||
|
$query[] = $this->buildSelectCriteria("CONCAT({$concatString})"); |
||||
|
} else { |
||||
|
$query[] = $this->buildSelectCriteria($searchField); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return \DB::raw(implode(' + ', $query).' AS '.$this->relevanceFieldName); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param null $searchField |
||||
|
* |
||||
|
* @return string |
||||
|
*/ |
||||
|
protected function buildSelectCriteria($searchField = null) |
||||
|
{ |
||||
|
$criteria = []; |
||||
|
|
||||
|
foreach ($this->matchers as $matcher => $multiplier) { |
||||
|
$criteria[] = $this->makeMatcher($searchField, $matcher, $multiplier); |
||||
|
} |
||||
|
|
||||
|
return implode(' + ', $criteria); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @param $searchField |
||||
|
* @param $matcherClass |
||||
|
* @param $multiplier |
||||
|
* |
||||
|
* @return mixed |
||||
|
*/ |
||||
|
protected function makeMatcher($searchField, $matcherClass, $multiplier) |
||||
|
{ |
||||
|
$matcher = new $matcherClass($multiplier); |
||||
|
|
||||
|
return $matcher->buildQueryString($searchField, $this->searchString); |
||||
|
} |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\SearchDrivers; |
||||
|
|
||||
|
class FuzzySearchDriver extends BaseSearchDriver |
||||
|
{ |
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
protected $matchers = [ |
||||
|
'TomLingham\Searchy\Matchers\ExactMatcher' => 100, |
||||
|
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, |
||||
|
'TomLingham\Searchy\Matchers\AcronymMatcher' => 42, |
||||
|
'TomLingham\Searchy\Matchers\ConsecutiveCharactersMatcher' => 40, |
||||
|
'TomLingham\Searchy\Matchers\StartOfWordsMatcher' => 35, |
||||
|
'TomLingham\Searchy\Matchers\StudlyCaseMatcher' => 32, |
||||
|
'TomLingham\Searchy\Matchers\InStringMatcher' => 30, |
||||
|
'TomLingham\Searchy\Matchers\TimesInStringMatcher' => 8, |
||||
|
]; |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\SearchDrivers; |
||||
|
|
||||
|
class LevenshteinSearchDriver extends BaseSearchDriver |
||||
|
{ |
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
protected $matchers = [ |
||||
|
'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100, |
||||
|
]; |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy\SearchDrivers; |
||||
|
|
||||
|
class SimpleSearchDriver extends BaseSearchDriver |
||||
|
{ |
||||
|
/** |
||||
|
* @var array |
||||
|
*/ |
||||
|
protected $matchers = [ |
||||
|
'TomLingham\Searchy\Matchers\ExactMatcher' => 100, |
||||
|
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, |
||||
|
'TomLingham\Searchy\Matchers\InStringMatcher' => 30, |
||||
|
]; |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
<?php |
||||
|
|
||||
|
namespace TomLingham\Searchy; |
||||
|
|
||||
|
use Illuminate\Support\ServiceProvider; |
||||
|
|
||||
|
class SearchyServiceProvider extends ServiceProvider |
||||
|
{ |
||||
|
/** |
||||
|
* Indicates if loading of the provider is deferred. |
||||
|
* |
||||
|
* @var bool |
||||
|
*/ |
||||
|
protected $defer = false; |
||||
|
|
||||
|
/** |
||||
|
* Register the service provider. |
||||
|
*/ |
||||
|
public function register() |
||||
|
{ |
||||
|
//
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Registers searchy. |
||||
|
*/ |
||||
|
public function registerSearchy() |
||||
|
{ |
||||
|
$this->app->bindShared('searchy', function ($app) { |
||||
|
return new SearchBuilder($app['config']); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Loads the configuration file. |
||||
|
*/ |
||||
|
public function setupConfig() |
||||
|
{ |
||||
|
$source = realpath(__DIR__.'/../config/searchy.php'); |
||||
|
|
||||
|
if (class_exists('Illuminate\Foundation\Application', false)) { |
||||
|
$this->publishes([$source => config_path('searchy.php')]); |
||||
|
} |
||||
|
|
||||
|
$this->mergeConfigFrom($source, 'searchy'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Boot the service provider. |
||||
|
*/ |
||||
|
public function boot() |
||||
|
{ |
||||
|
$this->setupConfig(); |
||||
|
$this->registerSearchy(); |
||||
|
} |
||||
|
} |
@ -1,19 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Facades; |
|
||||
|
|
||||
use Illuminate\Support\Facades\Facade; |
|
||||
|
|
||||
/** |
|
||||
* Searchy facade for the Laravel framework |
|
||||
*/ |
|
||||
class Searchy extends Facade |
|
||||
{ |
|
||||
/** |
|
||||
* Get the registered component. |
|
||||
* |
|
||||
* @return object |
|
||||
*/ |
|
||||
protected static function getFacadeAccessor() |
|
||||
{ |
|
||||
return 'searchy'; |
|
||||
} |
|
||||
} |
|
@ -1,14 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Interfaces; |
|
||||
|
|
||||
interface MatcherInterface |
|
||||
{ |
|
||||
/** |
|
||||
* Builds the string to add to the SELECT statement for the Matcher |
|
||||
* |
|
||||
* @param $column |
|
||||
* @param $searchString |
|
||||
* @return mixed |
|
||||
*/ |
|
||||
public function buildQueryString( $column, $searchString ); |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Interfaces; |
|
||||
|
|
||||
interface SearchDriverInterface |
|
||||
{ |
|
||||
|
|
||||
public function query( $searchString ); |
|
||||
|
|
||||
public function select( /* $columns */ ); |
|
||||
|
|
||||
public function get(); |
|
||||
|
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches |
|
||||
* |
|
||||
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'. |
|
||||
* |
|
||||
* Class AcronymMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class AcronymMatcher extends BaseMatcher |
|
||||
{ |
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return mixed|string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
|
||||
return implode( '% ', str_split(strtoupper( $searchString ))) . '%'; |
|
||||
} |
|
||||
} |
|
@ -1,34 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
use TomLingham\Searchy\Interfaces\MatcherInterface; |
|
||||
|
|
||||
/** |
|
||||
* @property mixed multiplier |
|
||||
* @property mixed operator |
|
||||
*/ |
|
||||
abstract class BaseMatcher implements MatcherInterface |
|
||||
{ |
|
||||
|
|
||||
protected $multiplier; |
|
||||
|
|
||||
public function __construct( $multiplier ) |
|
||||
{ |
|
||||
$this->multiplier = $multiplier; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* The default process for building the Query string |
|
||||
* |
|
||||
* @param $column |
|
||||
* @param $searchString |
|
||||
* @return mixed|string |
|
||||
*/ |
|
||||
public function buildQueryString( $column, $searchString ) |
|
||||
{ |
|
||||
if ( method_exists($this, 'formatSearchString') ) |
|
||||
$searchString = $this->formatSearchString( $searchString ); |
|
||||
|
|
||||
return "IF($column {$this->operator} '$searchString', {$this->multiplier}, 0)"; |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,40 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches strings that include all the characters in the search relatively position within the string. |
|
||||
* It also calculates the percentage of characters in the string that are matched and applies the multiplier accordingly. |
|
||||
* |
|
||||
* For Example, a search for 'fba' would match; 'Foo Bar' or 'Afraid of bats' |
|
||||
* |
|
||||
* Class ConsecutiveCharactersMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
class ConsecutiveCharactersMatcher extends BaseMatcher |
|
||||
{ |
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
|
||||
return '%'.implode('%', str_split( $searchString )).'%'; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param $column |
|
||||
* @param $rawString |
|
||||
* @return mixed|string |
|
||||
*/ |
|
||||
public function buildQueryString( $column, $rawString ) |
|
||||
{ |
|
||||
$searchString = $this->formatSearchString( $rawString ); |
|
||||
|
|
||||
return "IF(REPLACE($column, '\.', '') {$this->operator} '$searchString', ROUND({$this->multiplier} * (CHAR_LENGTH( '$rawString' ) / CHAR_LENGTH( REPLACE($column, ' ', '') ))), 0)"; |
|
||||
} |
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches against any occurrences of a string within a string and is case-insensitive. |
|
||||
* |
|
||||
* For example, a search for 'smi' would match; 'John Smith' or 'Smiley Face' |
|
||||
* |
|
||||
* Class InStringMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class InStringMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
return "%$searchString%"; |
|
||||
} |
|
||||
} |
|
@ -1,25 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches strings for Acronym 'like' matches but does NOT return Studly Case Matches |
|
||||
* |
|
||||
* for example, a search for 'fb' would match; 'foo bar' or 'Fred Brown' but not 'FreeBeer'. |
|
||||
* |
|
||||
* Class AcronymMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class LevenshteinMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @param $column |
|
||||
* @param $searchString |
|
||||
* @return mixed|string |
|
||||
*/ |
|
||||
public function buildQueryString( $column, $searchString ) |
|
||||
{ |
|
||||
return "levenshtein($column, '$searchString')"; |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches Strings that begin with the search string. |
|
||||
* |
|
||||
* For example, a search for 'hel' would match; 'Hello World' or 'helping hand' |
|
||||
* |
|
||||
* Class StartOfStringMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class StartOfStringMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
return "$searchString%"; |
|
||||
} |
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches the start of each word against each word in a search |
|
||||
* |
|
||||
* For example, a search for 'jo ta' would match; 'John Taylor' or 'Joshua B. Takashi' |
|
||||
* |
|
||||
* Class StartOfWordsMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class StartOfWordsMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
return implode('% ', explode(' ', $searchString)) . '%'; |
|
||||
} |
|
||||
} |
|
@ -1,34 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\Matchers; |
|
||||
|
|
||||
/** |
|
||||
* Matches Studly Case strings using the first letters of the words only |
|
||||
* |
|
||||
* For example a search for 'hp' would match; 'HtmlServiceProvider' or 'HashParser' but not 'hasProvider' |
|
||||
* |
|
||||
* Class StudlyCaseMatcher |
|
||||
* @package TomLingham\Searchy\Matchers |
|
||||
*/ |
|
||||
|
|
||||
class StudlyCaseMatcher extends BaseMatcher |
|
||||
{ |
|
||||
|
|
||||
/** |
|
||||
* @var string |
|
||||
*/ |
|
||||
protected $operator = 'LIKE BINARY'; |
|
||||
|
|
||||
/** |
|
||||
* @param $searchString |
|
||||
* @return string |
|
||||
*/ |
|
||||
public function formatSearchString( $searchString ) |
|
||||
{ |
|
||||
$searchString = preg_replace('/[^0-9a-zA-Z]/', '', $searchString); |
|
||||
return implode( '%', str_split(strtoupper( $searchString ))) . '%'; |
|
||||
} |
|
||||
|
|
||||
public function buildQueryString( $column, $searchString ) |
|
||||
{ |
|
||||
return "IF( CHAR_LENGTH( TRIM($column)) = CHAR_LENGTH( REPLACE( TRIM($column), ' ', '')) AND $column {$this->operator} '{$this->formatSearchString($searchString)}', {$this->multiplier}, 0)"; |
|
||||
} |
|
||||
} |
|
@ -1,110 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy; |
|
||||
|
|
||||
use Illuminate\Config\Repository; |
|
||||
use TomLingham\Searchy\SearchDrivers\FuzzySearchDriver; |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* @property mixed driverName |
|
||||
*/ |
|
||||
class SearchBuilder { |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* @var |
|
||||
*/ |
|
||||
private $table; |
|
||||
|
|
||||
/** |
|
||||
* @var |
|
||||
*/ |
|
||||
private $searchFields; |
|
||||
|
|
||||
/** |
|
||||
* @var |
|
||||
*/ |
|
||||
private $driverName; |
|
||||
|
|
||||
/** |
|
||||
* @var |
|
||||
*/ |
|
||||
private $config; |
|
||||
|
|
||||
|
|
||||
public function __construct( Repository $config ) |
|
||||
{ |
|
||||
$this->config = $config; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param $searchable |
|
||||
* @return $this |
|
||||
*/ |
|
||||
public function search( $searchable ) |
|
||||
{ |
|
||||
if (is_object( $searchable ) && method_exists($searchable, 'getTable')) { |
|
||||
$this->table = $searchable->getTable(); |
|
||||
} else { |
|
||||
$this->table = $searchable; |
|
||||
} |
|
||||
|
|
||||
return $this; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @return FuzzySearchDriver |
|
||||
*/ |
|
||||
public function fields( /* $fields */ ) |
|
||||
{ |
|
||||
$this->searchFields = func_get_args(); |
|
||||
|
|
||||
return $this->makeDriver(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param $driverName |
|
||||
* @return $this |
|
||||
*/ |
|
||||
public function driver( $driverName ) |
|
||||
{ |
|
||||
$this->driverName = $driverName; |
|
||||
|
|
||||
return $this; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param $table |
|
||||
* @param $searchFields |
|
||||
* @return mixed |
|
||||
*/ |
|
||||
public function __call( $table, $searchFields ) |
|
||||
{ |
|
||||
return call_user_func_array([$this->search( $table ), 'fields'], $searchFields); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @return mixed |
|
||||
*/ |
|
||||
private function makeDriver() |
|
||||
{ |
|
||||
$relevanceFieldName = $this->config->get('searchy.fieldName'); |
|
||||
|
|
||||
// Check if default driver is being overridden, otherwise
|
|
||||
// load the default
|
|
||||
if ( $this->driverName ){ |
|
||||
$driverName = $this->driverName; |
|
||||
} else { |
|
||||
$driverName = $this->config->get('searchy.default'); |
|
||||
} |
|
||||
|
|
||||
// Gets the details for the selected driver from the configuration file
|
|
||||
$driver = $this->config->get("searchy.drivers.$driverName")['class']; |
|
||||
|
|
||||
// Create a new instance of the selected drivers 'class' and pass
|
|
||||
// through table and fields to search
|
|
||||
$driverInstance = new $driver( $this->table, $this->searchFields, $relevanceFieldName ); |
|
||||
return $driverInstance; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,147 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\SearchDrivers; |
|
||||
|
|
||||
use TomLingham\Searchy\Interfaces\SearchDriverInterface; |
|
||||
|
|
||||
|
|
||||
abstract class BaseSearchDriver implements SearchDriverInterface { |
|
||||
|
|
||||
protected $table; |
|
||||
protected $columns; |
|
||||
protected $searchFields; |
|
||||
protected $searchString; |
|
||||
protected $relevanceFieldName; |
|
||||
protected $query; |
|
||||
|
|
||||
/** |
|
||||
* @param null $table |
|
||||
* @param array $searchFields |
|
||||
* @param $relevanceFieldName |
|
||||
* @param array $columns |
|
||||
* @internal param $relevanceField |
|
||||
*/ |
|
||||
public function __construct( $table = null, $searchFields = [], $relevanceFieldName, $columns = ['*'] ) |
|
||||
{ |
|
||||
$this->searchFields = $searchFields; |
|
||||
$this->table = $table; |
|
||||
$this->columns = $columns; |
|
||||
$this->relevanceFieldName = $relevanceFieldName; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Specify which columns to return |
|
||||
* |
|
||||
* @return $this |
|
||||
*/ |
|
||||
public function select() |
|
||||
{ |
|
||||
$this->columns = func_get_args(); |
|
||||
return $this; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Specify the string that is is being searched for |
|
||||
* |
|
||||
* @param $searchString |
|
||||
* @return \Illuminate\Database\Query\Builder|mixed|static |
|
||||
*/ |
|
||||
public function query( $searchString ) |
|
||||
{ |
|
||||
$this->searchString = trim(\DB::connection()->getPdo()->quote( $searchString ), "'"); |
|
||||
return $this; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Get the results of the search as an Array |
|
||||
* |
|
||||
* @return array |
|
||||
*/ |
|
||||
public function get() |
|
||||
{ |
|
||||
return $this->run()->get(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns an instance of the Laravel Fluent Database Query Object with the search |
|
||||
* queries applied |
|
||||
* |
|
||||
* @return array |
|
||||
*/ |
|
||||
public function getQuery() |
|
||||
{ |
|
||||
return $this->run(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Runs the 'having' method directly on the Laravel Fluent Database Query Object |
|
||||
* and returns the instance of the object |
|
||||
* |
|
||||
* @return mixed |
|
||||
*/ |
|
||||
public function having() |
|
||||
{ |
|
||||
return call_user_func_array([$this->run(), 'having'], func_get_args()); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @return $this |
|
||||
*/ |
|
||||
protected function run() |
|
||||
{ |
|
||||
$this->query = \DB::table( $this->table ) |
|
||||
->select( $this->columns ) |
|
||||
->addSelect( $this->buildSelectQuery( $this->searchFields ) ) |
|
||||
->orderBy( $this->relevanceFieldName, 'desc' ) |
|
||||
->having( $this->relevanceFieldName, '>', 0 ); |
|
||||
|
|
||||
return $this->query; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param array $searchFields |
|
||||
* @return array|\Illuminate\Database\Query\Expression |
|
||||
*/ |
|
||||
protected function buildSelectQuery( array $searchFields ) |
|
||||
{ |
|
||||
$query = []; |
|
||||
|
|
||||
foreach ($searchFields as $searchField) { |
|
||||
if (strpos($searchField, '::')){ |
|
||||
$concatString = str_replace('::', ", ' ', ", $searchField); |
|
||||
$query[] = $this->buildSelectCriteria( "CONCAT({$concatString})"); |
|
||||
} else { |
|
||||
$query[] = $this->buildSelectCriteria( $searchField ); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return \DB::raw(implode(' + ', $query) . ' AS ' . $this->relevanceFieldName); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @param null $searchField |
|
||||
* @return string |
|
||||
*/ |
|
||||
protected function buildSelectCriteria( $searchField = null ) |
|
||||
{ |
|
||||
$criteria = []; |
|
||||
|
|
||||
foreach( $this->matchers as $matcher => $multiplier){ |
|
||||
$criteria[] = $this->makeMatcher( $searchField, $matcher, $multiplier ); |
|
||||
} |
|
||||
|
|
||||
return implode(' + ', $criteria); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* @param $searchField |
|
||||
* @param $matcherClass |
|
||||
* @param $multiplier |
|
||||
* @return mixed |
|
||||
*/ |
|
||||
protected function makeMatcher( $searchField, $matcherClass, $multiplier ) |
|
||||
{ |
|
||||
$matcher = new $matcherClass( $multiplier ); |
|
||||
|
|
||||
return $matcher->buildQueryString( $searchField, $this->searchString ); |
|
||||
} |
|
||||
} |
|
@ -1,19 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\SearchDrivers; |
|
||||
|
|
||||
class FuzzySearchDriver extends BaseSearchDriver { |
|
||||
|
|
||||
/** |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $matchers = [ |
|
||||
'TomLingham\Searchy\Matchers\ExactMatcher' => 100, |
|
||||
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, |
|
||||
'TomLingham\Searchy\Matchers\AcronymMatcher' => 42, |
|
||||
'TomLingham\Searchy\Matchers\ConsecutiveCharactersMatcher' => 40, |
|
||||
'TomLingham\Searchy\Matchers\StartOfWordsMatcher' => 35, |
|
||||
'TomLingham\Searchy\Matchers\StudlyCaseMatcher' => 32, |
|
||||
'TomLingham\Searchy\Matchers\InStringMatcher' => 30, |
|
||||
'TomLingham\Searchy\Matchers\TimesInStringMatcher' => 8, |
|
||||
]; |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\SearchDrivers; |
|
||||
|
|
||||
class LevenshteinSearchDriver extends BaseSearchDriver { |
|
||||
|
|
||||
/** |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $matchers = [ |
|
||||
'TomLingham\Searchy\Matchers\LevenshteinMatcher' => 100 |
|
||||
]; |
|
||||
|
|
||||
} |
|
@ -1,14 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy\SearchDrivers; |
|
||||
|
|
||||
class SimpleSearchDriver extends BaseSearchDriver { |
|
||||
|
|
||||
/** |
|
||||
* @var array |
|
||||
*/ |
|
||||
protected $matchers = [ |
|
||||
'TomLingham\Searchy\Matchers\ExactMatcher' => 100, |
|
||||
'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, |
|
||||
'TomLingham\Searchy\Matchers\InStringMatcher' => 30, |
|
||||
]; |
|
||||
|
|
||||
} |
|
@ -1,52 +0,0 @@ |
|||||
<?php namespace TomLingham\Searchy; |
|
||||
|
|
||||
use Illuminate\Config\Repository; |
|
||||
use Illuminate\Support\ServiceProvider; |
|
||||
|
|
||||
class SearchyServiceProvider extends ServiceProvider { |
|
||||
|
|
||||
/** |
|
||||
* Indicates if loading of the provider is deferred. |
|
||||
* |
|
||||
* @var bool |
|
||||
*/ |
|
||||
protected $defer = false; |
|
||||
|
|
||||
/** |
|
||||
* Register the service provider. |
|
||||
* |
|
||||
* @return void |
|
||||
*/ |
|
||||
public function register() |
|
||||
{ |
|
||||
$this->app->bindShared('searchy', function( $app ) |
|
||||
{ |
|
||||
return new SearchBuilder( $app['config'] ); |
|
||||
}); |
|
||||
|
|
||||
$this->mergeConfigFrom( |
|
||||
__DIR__ . '/../../config/config.php', 'searchy' |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* |
|
||||
*/ |
|
||||
public function boot() |
|
||||
{ |
|
||||
$this->publishes([ |
|
||||
__DIR__.'/../../config/config.php' => config_path('searchy.php'), |
|
||||
]); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Get the services provided by the provider. |
|
||||
* |
|
||||
* @return array |
|
||||
*/ |
|
||||
public function provides() |
|
||||
{ |
|
||||
return array(); |
|
||||
} |
|
||||
|
|
||||
} |
|
@ -1,28 +0,0 @@ |
|||||
<?php |
|
||||
|
|
||||
return [ |
|
||||
|
|
||||
'default' => 'fuzzy', |
|
||||
|
|
||||
'fieldName' => 'relevance', |
|
||||
|
|
||||
'drivers' => [ |
|
||||
|
|
||||
'fuzzy' => [ |
|
||||
'class' => 'TomLingham\Searchy\SearchDrivers\FuzzySearchDriver' |
|
||||
], |
|
||||
|
|
||||
'simple' => [ |
|
||||
'class' => 'TomLingham\Searchy\SearchDrivers\SimpleSearchDriver' |
|
||||
], |
|
||||
|
|
||||
'levenshtein' => [ |
|
||||
'class' => 'TomLingham\Searchy\SearchDrivers\LevenshteinSearchDriver' |
|
||||
], |
|
||||
|
|
||||
|
|
||||
], |
|
||||
|
|
||||
|
|
||||
|
|
||||
]; |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue