I needed to be able to make a glossary that accepts an argument of 1 character and match that 1 character, irregardless of the case.
The provided text-options 'case' and 'pathcase' do not seem to do this.

So, I wrote a patch that allows for case insensitive searches by forcing both sides to be lower case on the database call.

This patch is only done for string arguments passed to views, but I imagine there are other places in views that may need this kind of thing.

Comments

thekevinday’s picture

Version: 6.x-2.3 » 6.x-2.x-dev

bump to latest version

merlinofchaos’s picture

Status: Active » Needs review

Hey there's a patch here. SEtting to proper status so it'll get into my review passes.

Sorry this got lost.

merlinofchaos’s picture

Status: Needs review » Needs work

Ok, a couple of minor comments here.

1) lower( should be LOWER( since we always put database keywords in upper case.
2) "Ignore Case" should be "Ignore case"
3) MySQL is case insensitive by default, and that's 90% of our users, so we should tell the users they don't need to check this since turning it on could reduce performance.

thekevinday’s picture

StatusFileSize
new3.33 KB

Then try this one out.

thekevinday’s picture

Status: Needs work » Needs review
StatusFileSize
new3.36 KB

There was a mistake where LOWER was added in an area where it should not have been added.

Please review this new patch.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

looks fine

merlinofchaos’s picture

Version: 6.x-2.x-dev » 7.x-3.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Applied to 6.x branches; needs work for 7.x still due to dbtng.

thekevinday’s picture

I have more recently been wondering which of the following ways is the better way to do this:
1) use the SQL LOWER as done by this patch
2) use the php strtolower function instead

Which one would have better performance?
Which one would have better scalability?

The php strtolower has the advantage of consistency and not having to worry about differences between the different supported databases.
But...the disadvantage, might that be character encoding issues (ascii, utf8, iso8859-1, etc..)?

dawehner’s picture

Mysql is the suggested way to do this, because you can use it to filter/sort do anything with it. With php you have less possiblites.

dawehner’s picture

Status: Patch (to be ported) » Needs work

Update patch.

So what's the problem

This code produces the sql

        $this->query->add_where(0, "LOWER($field) = LOWER(:views_handler_argument_string)", $placeholders, 'formula');
WHERE (( (node.created = ***CURRENT_TIME***+0) AND (LOWER(node.title) = LOWER(Vulpes Ut)) ))

But when adding '' i get a

number of bound variables does not match number of tokens: 
thekevinday’s picture

Status: Needs work » Needs review
StatusFileSize
new3.38 KB

Here is where I suspect the problem happens.

<?php
    if (empty($this->options['ignorecase'])){
      $this->query->add_where(0, "$field = '%s'", $argument);
    }
    else {
      $this->query->add_where(0, "LOWER($field) = LOWER('%s')", $argument);
    }
?>

Perhaps MySQL does not like the query LOWER('').
I am changing the code to add a php empty() functional call as an additional check before adding the SQL LOWER() function.

If the patch has been applied as mentioned in #7, then should a bug report be opened for the 2.x version?

Also, this patch applies the SQL LOWER() function call to the field name, is this necessary or can the field name be guaranteed lower case?

dawehner’s picture

@thekevinday

This is drupal7, the sql code needs something different here. You cannot use direct sql like LOWER() without any additional problems :(

dawehner’s picture

Status: Needs review » Needs work

Based on #12

thekevinday’s picture

I have looked all over the drupal 7 database api, I cannot seem to find where to call functions against specific fields.
Either I need to be pointed in the right direction or this may not be doable until the drupal 7 database api fully supports the SQL standard. (namely, sql functions)

thekevinday’s picture

I noticed that the date module does the following:

<?php
/**
 *  A helper function to do cross-database concatation of date parts
 *
 *  @param $array - an array of values to be concatonated in sql
 *  @return - correct sql string for database type
 */
function date_sql_concat($array) {
  switch (date_db_type()) {
    case ('mysql'):
    case ('mysqli'):
      return "CONCAT(" . implode(",", $array) . ")";
    case ('pgsql'):
      return implode(" || ", $array);
  }
}
?>

So then does that mean we could do something like this?

<?php
function views_sql_lower($array) {
  switch (date_db_type()) {
    case ('mysql'):
    case ('mysqli'):
    case ('pgsql'):
      return "LOWER(" . implode(",", $array) . ")";
  }
}
?>
thekevinday’s picture

Status: Needs work » Needs review
StatusFileSize
new5.41 KB

I seem to have forgotten about this patch up until I needed it again. Here is the updated coded.

Apparently, views is already adding their own implicit SQL (namely IN()).
I used that as an example on how to properly implement adding LOWER() with the d7 dbapi.

I also noticed that views_plugin_argument_validate_taxonomy_term should support case insensitive searches as well.

Please Review this patch.

merlinofchaos’s picture

+        if ($operator == 'IN'){
+          $field = " IN(LOWER($placeholder))";
+        }

Does this really work? I have my doubts that it works that way.

thekevinday’s picture

I was uncertain of how placeholder was handled in this case.

I know for a fact the following does not work:
IN(LOWER("a", "b", "c"))

Instead the following does work:
IN(LOWER("a"), LOWER("b"), LOWER("c"))

I just don't know how placeholder works in this case.
It seems a little odd for IN to be used with only one argument, so I am going to believe that placeholder is an array.

So then how do we handle properly wrapping the translated placeholder to produce the second example above?

dawehner’s picture

Status: Needs review » Needs work

Well you could need to construct the string and use single placeholders foreach argument.

Update status.

merlinofchaos’s picture

It's an array, and it's a DBTNG thing. I don't honestly know how to handle something like that using DBTNG.

thekevinday’s picture

I believe I may have to manually expand that array.

The following urls are of importance:
- http://drupal.org/node/310072#placeholder-arrays
- http://api.drupal.org/api/drupal/includes--database--query.inc/interface...
- http://api.drupal.org/api/drupal/includes--database--select.inc/function...
- http://api.drupal.org/api/drupal/includes--database--select.inc/interfac...

Now the trick is expanding it properly.
If I expand the placeholders, then I assume I need to expand the actual arguments?
Is this necessary, or is it possible to do a db_select with the following snippet:
.. "SELECT * from node where nid = :nid_1 OR nid = :nid_2", array(':nid' => array(501, 502)) ..

The QueryPlaceholderInterface::nextPlaceholder sounds like the class & function to use.