From 44ace92f931b59c5797bd243b273d0656882b30c Mon Sep 17 00:00:00 2001 From: Tom Lingham Date: Mon, 14 Jul 2014 10:35:19 +1000 Subject: [PATCH] First release beta --- src/TomLingham/Searchy/Facades/Searchy.php | 24 +++++ .../Searchy/Interfaces/MatcherInterface.php | 14 +++ .../Searchy/Interfaces/SearchDriverInterface.php | 13 +++ src/TomLingham/Searchy/Matchers/AcronymMatcher.php | 32 ++++++ src/TomLingham/Searchy/Matchers/BaseMatcher.php | 33 +++++++ .../Matchers/ConsecutiveCharactersMatcher.php | 46 +++++++++ src/TomLingham/Searchy/Matchers/ExactMatcher.php | 24 +++++ .../Searchy/Matchers/InStringMatcher.php | 32 ++++++ .../Searchy/Matchers/LevenshteinMatcher.php | 33 +++++++ .../Searchy/Matchers/StartOfStringMatcher.php | 32 ++++++ .../Searchy/Matchers/StartOfWordsMatcher.php | 32 ++++++ .../Searchy/Matchers/StudlyCaseMatcher.php | 39 ++++++++ .../Searchy/Matchers/TimesInStringMatcher.php | 34 +++++++ src/TomLingham/Searchy/SearchBuilder.php | 80 +++++++++++++++ .../Searchy/SearchDrivers/BaseSearchDriver.php | 110 +++++++++++++++++++++ .../Searchy/SearchDrivers/FuzzySearchDriver.php | 19 ++++ .../SearchDrivers/LevenshteinSearchDriver.php | 36 +++++++ .../Searchy/SearchDrivers/SimpleSearchDriver.php | 14 +++ src/TomLingham/Searchy/SearchyServiceProvider.php | 5 +- src/config/config.php | 32 ++++++ 20 files changed, 683 insertions(+), 1 deletion(-) create mode 100644 src/TomLingham/Searchy/Facades/Searchy.php create mode 100644 src/TomLingham/Searchy/Interfaces/MatcherInterface.php create mode 100644 src/TomLingham/Searchy/Interfaces/SearchDriverInterface.php create mode 100644 src/TomLingham/Searchy/Matchers/AcronymMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/BaseMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/ConsecutiveCharactersMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/ExactMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/InStringMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/LevenshteinMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/StartOfStringMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/StartOfWordsMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/StudlyCaseMatcher.php create mode 100644 src/TomLingham/Searchy/Matchers/TimesInStringMatcher.php create mode 100644 src/TomLingham/Searchy/SearchBuilder.php create mode 100644 src/TomLingham/Searchy/SearchDrivers/BaseSearchDriver.php create mode 100644 src/TomLingham/Searchy/SearchDrivers/FuzzySearchDriver.php create mode 100644 src/TomLingham/Searchy/SearchDrivers/LevenshteinSearchDriver.php create mode 100644 src/TomLingham/Searchy/SearchDrivers/SimpleSearchDriver.php create mode 100644 src/config/config.php diff --git a/src/TomLingham/Searchy/Facades/Searchy.php b/src/TomLingham/Searchy/Facades/Searchy.php new file mode 100644 index 0000000..51b25e8 --- /dev/null +++ b/src/TomLingham/Searchy/Facades/Searchy.php @@ -0,0 +1,24 @@ +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)"; + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/Matchers/ConsecutiveCharactersMatcher.php b/src/TomLingham/Searchy/Matchers/ConsecutiveCharactersMatcher.php new file mode 100644 index 0000000..23846cf --- /dev/null +++ b/src/TomLingham/Searchy/Matchers/ConsecutiveCharactersMatcher.php @@ -0,0 +1,46 @@ +formatSearchString( $rawString ); + + $query = "IF($column {$this->operator} '$searchString', ROUND({$this->multiplier} * (CHAR_LENGTH( '$rawString' ) / CHAR_LENGTH( REPLACE($column, ' ', '') ))), 0)"; + + return $query; + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/Matchers/ExactMatcher.php b/src/TomLingham/Searchy/Matchers/ExactMatcher.php new file mode 100644 index 0000000..1eafef7 --- /dev/null +++ b/src/TomLingham/Searchy/Matchers/ExactMatcher.php @@ -0,0 +1,24 @@ +sensitivity = $sensitivity; + } + + /** + * @param $column + * @param $searchString + * @return mixed|string + */ + public function buildQueryString( $column, $searchString ){ + + return "levenshtein($column, '$searchString', {$this->sensitivity})"; + + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/Matchers/StartOfStringMatcher.php b/src/TomLingham/Searchy/Matchers/StartOfStringMatcher.php new file mode 100644 index 0000000..4841777 --- /dev/null +++ b/src/TomLingham/Searchy/Matchers/StartOfStringMatcher.php @@ -0,0 +1,32 @@ +operator} '{$this->formatSearchString($searchString)}', {$this->multiplier}, 0)"; + + return $query; + } +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/Matchers/TimesInStringMatcher.php b/src/TomLingham/Searchy/Matchers/TimesInStringMatcher.php new file mode 100644 index 0000000..c801a0d --- /dev/null +++ b/src/TomLingham/Searchy/Matchers/TimesInStringMatcher.php @@ -0,0 +1,34 @@ +multiplier} * ROUND (( + CHAR_LENGTH($column) - CHAR_LENGTH( REPLACE ( LOWER($column), lower('$searchString'), '')) + ) / LENGTH('$searchString'))"; + + return $query; + } +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchBuilder.php b/src/TomLingham/Searchy/SearchBuilder.php new file mode 100644 index 0000000..de76874 --- /dev/null +++ b/src/TomLingham/Searchy/SearchBuilder.php @@ -0,0 +1,80 @@ +table = $table; + + return $this; + } + + /** + * @return FuzzySearchDriver + */ + public function fields( /* $fields */ ) + { + + $searchFields = func_get_args(); + + $this->searchFields = $searchFields; + + return $this->makeDriver(); + + } + + /** + * @param $driverName + * @return $this + */ + public function driver( $driverName ) + { + $this->driverName = $driverName; + return $this; + } + + /** + * @param $table + * @param $fields + * @return mixed + */ + public function __call( $table, $fields ) + { + + return call_user_func_array([$this->search( $table ), 'fields'], $fields); + + } + + /** + * @return mixed + */ + private function makeDriver() + { + if (! $this->driverName){ + $driverName = \Config::get('searchy::default'); + } else { + $driverName = $this->driverName; + } + $driverMap = \Config::get("searchy::drivers.$driverName"); + + return new $driverMap['class']( $this->table, $this->searchFields ); + + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchDrivers/BaseSearchDriver.php b/src/TomLingham/Searchy/SearchDrivers/BaseSearchDriver.php new file mode 100644 index 0000000..7f3b000 --- /dev/null +++ b/src/TomLingham/Searchy/SearchDrivers/BaseSearchDriver.php @@ -0,0 +1,110 @@ +fields = $fields; + $this->table = $table; + } + + /** + * @param $searchString + * @return \Illuminate\Database\Query\Builder|mixed|static + * @throws \Whoops\Example\Exception + */ + public function query( $searchString ){ + + if(\Config::get('searchy::sanitize')) + $this->searchString = $this->sanitize($searchString); + + $results = \DB::table($this->table) + ->select( \DB::raw('*') ) + ->addSelect($this->buildSelectQuery( $this->fields )) + ->orderBy(\Config::get('searchy::fieldName'), 'desc') + ->having(\Config::get('searchy::fieldName'),'>', 0); + + dd($results->toSql()); + + return $results; + } + + /** + * @param array $fields + * @return array|\Illuminate\Database\Query\Expression + */ + protected function buildSelectQuery( array $fields ){ + $query = []; + + foreach ($fields as $field) { + $query[] = $this->buildSelectCriteria( $field ); + } + + $query = \DB::raw(implode(' + ', $query) . ' AS ' . \Config::get('searchy::fieldName')); + + return $query; + } + + /** + * @param null $field + * @return string + */ + protected function buildSelectCriteria( $field = null ) { + $criteria = []; + + foreach( $this->matchers as $matcher => $multiplier){ + $criteria[] = $this->makeMatcher( $field, $matcher, $multiplier ); + } + + return implode(' + ', $criteria); + } + + + /** + * @param $field + * @param $matcherClass + * @param $multiplier + * @return mixed + */ + protected function makeMatcher( $field, $matcherClass, $multiplier ) + { + + $matcher = new $matcherClass( $multiplier ); + + return $matcher->buildQueryString( $field, $this->searchString ); + + } + + /** + * @param $searchString + * @return mixed + */ + private function sanitize( $searchString ) { + return preg_replace(\Config::get('searchy::sanitizeRegEx'), '', $searchString); + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchDrivers/FuzzySearchDriver.php b/src/TomLingham/Searchy/SearchDrivers/FuzzySearchDriver.php new file mode 100644 index 0000000..86219af --- /dev/null +++ b/src/TomLingham/Searchy/SearchDrivers/FuzzySearchDriver.php @@ -0,0 +1,19 @@ + 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, + ]; + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchDrivers/LevenshteinSearchDriver.php b/src/TomLingham/Searchy/SearchDrivers/LevenshteinSearchDriver.php new file mode 100644 index 0000000..50b198e --- /dev/null +++ b/src/TomLingham/Searchy/SearchDrivers/LevenshteinSearchDriver.php @@ -0,0 +1,36 @@ + 100 + ]; + + + public function setSensitivity( $sensitivity ){ + + $this->sensitivity = $sensitivity; + + return $this; + } + + + /** + * @param $column + * @param $matcherClass + * @param $multiplier + * @return mixed + */ + protected function makeMatcher( $column, $matcherClass, $multiplier ) + { + $matcher = new $matcherClass( $multiplier ); + $matcher->setSensitivity( $this->sensitivity ); + + return $matcher->buildQueryString( $column, $this->searchString ); + + } + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchDrivers/SimpleSearchDriver.php b/src/TomLingham/Searchy/SearchDrivers/SimpleSearchDriver.php new file mode 100644 index 0000000..b5ad99f --- /dev/null +++ b/src/TomLingham/Searchy/SearchDrivers/SimpleSearchDriver.php @@ -0,0 +1,14 @@ + 100, + 'TomLingham\Searchy\Matchers\StartOfStringMatcher' => 50, + 'TomLingham\Searchy\Matchers\InStringMatcher' => 30, + ]; + +} \ No newline at end of file diff --git a/src/TomLingham/Searchy/SearchyServiceProvider.php b/src/TomLingham/Searchy/SearchyServiceProvider.php index 97fd8d8..249b913 100644 --- a/src/TomLingham/Searchy/SearchyServiceProvider.php +++ b/src/TomLingham/Searchy/SearchyServiceProvider.php @@ -18,7 +18,10 @@ class SearchyServiceProvider extends ServiceProvider { */ public function register() { - // + $this->app->bindShared('searchy', function($app) + { + return new SearchBuilder(); + }); } /** diff --git a/src/config/config.php b/src/config/config.php new file mode 100644 index 0000000..82937cb --- /dev/null +++ b/src/config/config.php @@ -0,0 +1,32 @@ + 'fuzzy', + + 'sanitize' => true, + + 'sanitizeRegEx' => '/[%]+/i', + + 'fieldName' => 'relevance', + + 'drivers' => [ + + 'fuzzy' => [ + 'class' => 'TomLingham\Searchy\SearchDrivers\FuzzySearchDriver' + ], + + 'simple' => [ + 'class' => 'TomLingham\Searchy\SearchDrivers\SimpleSearchDriver' + ], + + 'levenshtein' => [ + 'class' => 'TomLingham\Searchy\SearchDrivers\LevenshteinSearchDriver' + ], + + + ], + + + +]; \ No newline at end of file