@@ -65,11 +65,16 @@ class PartSearchFilter implements FilterInterface
6565 /** @var bool Use Internal Part number for searching */
6666 protected bool $ ipn = true ;
6767
68+ /** @var int Helper variable for hacky array_map variable injection */
69+ protected int $ it = 0 ;
70+
6871 public function __construct (
6972 /** @var string The string to query for */
7073 protected string $ keyword
7174 )
7275 {
76+ // Transform keyword and trim excess spaces
77+ $ keyword = trim (str_replace ('+ ' , ' ' , $ keyword ));
7378 }
7479
7580 protected function getFieldsToSearch (): array
@@ -125,26 +130,50 @@ public function apply(QueryBuilder $queryBuilder): void
125130 return ;
126131 }
127132
128- //Convert the fields to search to a list of expressions
129- $ expressions = array_map ( function ( string $ field ): string {
130- if ( $ this -> regex ) {
133+ if ( $ this -> regex ) {
134+ //Convert the fields to search to a list of expressions
135+ $ expressions = array_map ( function ( string $ field ): string {
131136 return sprintf ("REGEXP(%s, :search_query) = TRUE " , $ field );
132- }
133-
134- return sprintf ("ILIKE(%s, :search_query) = TRUE " , $ field );
135- }, $ fields_to_search );
137+ }, $ fields_to_search );
136138
137- //Add Or concatenation of the expressions to our query
138- $ queryBuilder ->andWhere (
139- $ queryBuilder ->expr ()->orX (...$ expressions )
140- );
139+ //Add Or concatenation of the expressions to our query
140+ $ queryBuilder ->andWhere (
141+ $ queryBuilder ->expr ()->orX (...$ expressions )
142+ );
141143
142- //For regex, we pass the query as is, for like we add % to the start and end as wildcards
143- if ($ this ->regex ) {
144+ //For regex, we pass the query as is, save html special chars
144145 $ queryBuilder ->setParameter ('search_query ' , $ this ->keyword );
145- } else {
146- $ queryBuilder ->setParameter ('search_query ' , '% ' . $ this ->keyword . '% ' );
146+ return ;
147147 }
148+
149+ //Split keyword on spaces, but limit token count to not blow up the DB
150+ $ tokens = explode (' ' , $ this ->keyword , 5 );
151+
152+ $ params = new \Doctrine \Common \Collections \ArrayCollection ();
153+
154+ //Perform search of every single token in every selected field, AND the where clauses
155+ for ($ i = 0 ; $ i < sizeof ($ tokens ); $ i ++) {
156+ $ this ->it = $ i ;
157+ $ tokens [$ i ] = trim ($ tokens [$ i ]);
158+
159+ //Skip empty words (e.g. because of multiple spaces)
160+ if ($ tokens [$ i ] === '' ) continue ;
161+
162+ //Convert the fields to search to a list of expressions
163+ $ expressions = array_map (function (string $ field ): string {
164+ return sprintf ("ILIKE(%s, :search_query%u) = TRUE " , $ field , $ this ->it );
165+ }, $ fields_to_search );
166+
167+ //Aggregate the parameters for consolidated commission
168+ $ params [] = new \Doctrine \ORM \Query \Parameter ('search_query ' . $ i , '% ' . $ tokens [$ i ] . '% ' );
169+
170+ //Add Or concatenation of the expressions to our query
171+ $ queryBuilder ->andWhere (
172+ $ queryBuilder ->expr ()->orX (...$ expressions )
173+ );
174+ }
175+ $ queryBuilder ->setParameters ($ params );
176+
148177 }
149178
150179 public function getKeyword (): string
@@ -301,5 +330,4 @@ public function setComment(bool $comment): PartSearchFilter
301330 return $ this ;
302331 }
303332
304-
305333}
0 commit comments