Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Case Insensitive search (again) #38

Open
DiegoMax opened this issue Jul 27, 2017 · 2 comments
Open

Case Insensitive search (again) #38

DiegoMax opened this issue Jul 27, 2017 · 2 comments

Comments

@DiegoMax
Copy link

DiegoMax commented Jul 27, 2017

Hello there @ypnos-web , i have been working with this code for a while, trying to implement case insensitive search in an elegant way. I think i have found it (finally).

private function _addCondition($column, $value, $type = 'and')
  {
    $right = $this->config('search.prefix') ? "{$value}%" : "%{$value}%";
    if ($this->config('search.ignoreCase')) {
      $query = TableRegistry::get($this->_tableName)->find(); // Get table reference
      $lower = $query->newExpr()->add("CONCAT(LOWER('{$right}'))");
      $condition = ["LOWER(CONCAT({$column})) LIKE" => $lower];
    } else {
      $condition = ["{$column} LIKE" => $right];
    }
    if ($type === 'or') {
      $this->config('conditionsOr', $condition); // merges
      return;
    }

    list($association, $field) = explode('.', $column);
    if ($this->_tableName == $association) {
      $this->config('conditionsAnd', $condition); // merges
    } else {
      $this->config('matching', [$association => $condition]); // merges
    }
  }

I think its elegant because its pure SQL and should work with all DB engines, as a plus, CONCAT() implies a cast to string, so it will not explode when searching on non string/varchar fields.
I have only tested this on PostgreSQL, but i think it should work in other engines too.

Let me know what you think, and i might send you a PR if you are interested, or you can just copy paste from here.

Thanks!

// Diego

@ypnos-web
Copy link
Owner

Hello Diego,

thanks for your thoughts on this. I think it is a great solution. However please have a look at the devel branch where we progressed a bit, especially with the option to set a custom operator. Have a look:

$comparison = trim($this->_getComparison($table, $column));
// wrap value for LIKE and NOT LIKE
if (strpos(strtolower($comparison), 'like') !== false) {
$value = $this->config('prefixSearch') ? "{$value}%" : "%{$value}%";
}
$condition = ["{$column} {$comparison}" => $value];

What you see there is a special case for 'like' comparisons. I think this is where your addition of ignoreCase would fit best.

The problem with CONCAT() is that it makes comparisons of non-string types in the database very inefficient (indexes become unusable etc.), so I believe it is best to introduce this functionality only for LIKE-type comparisons. Or we could think of making case-insensitivity a per-column option, as is done with the custom operator. What do you think?

@DiegoMax
Copy link
Author

I will take a look, and yeah im aware of the performance penalty when casting in that way, but at least in my case it was much cleaner than having to add a lot of conditions to do different things depending on the field type.
I will take a look and i will see if i can somehow improve what you have already (or maybe just use what you did) ;)
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants