CRM-14811 - FullText - Use boolean mode with InnoDB FTS
authorTim Otten <totten@civicrm.org>
Thu, 12 Jun 2014 02:19:44 +0000 (19:19 -0700)
committerTim Otten <totten@civicrm.org>
Thu, 12 Jun 2014 02:19:44 +0000 (19:19 -0700)
CRM/Contact/Form/Search/Custom/FullText/AbstractPartialQuery.php
CRM/Utils/QueryFormatter.php
tests/phpunit/CRM/Utils/QueryFormatterTest.php

index ff182d1c30fd02143c20dd363e6af85c3c6a4889..a3bd486477575ca3461e3ccb61dddeef58fa2ace 100644 (file)
@@ -252,14 +252,14 @@ GROUP BY {$tableValues['id']}
     $clauses = array();
     if (CRM_Core_InnoDBIndexer::singleton()->hasDeclaredIndex($tableName, $fullTextFields)) {
       $formattedQuery = CRM_Utils_QueryFormatter::singleton()
-        ->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_FTS);
+        ->format($queryText, CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL);
 
       $prefixedFieldNames = array();
       foreach ($fullTextFields as $fieldName) {
         $prefixedFieldNames[] = "$tableAlias.$fieldName";
       }
 
-      $clauses[] = sprintf("MATCH (%s) AGAINST ('%s')",
+      $clauses[] = sprintf("MATCH (%s) AGAINST ('%s' IN BOOLEAN MODE)",
         implode(',', $prefixedFieldNames),
         $strtolower(CRM_Core_DAO::escapeString($formattedQuery))
       );
index 7da213dbb371b1388db63eb23a96919cdf637094..baa8f36f3e98b0046aa4b87e2649a881cbfa20a4 100644 (file)
@@ -38,6 +38,7 @@
 class CRM_Utils_QueryFormatter {
   const LANG_SQL_LIKE = 'like';
   const LANG_SQL_FTS = 'fts';
+  const LANG_SQL_FTSBOOL = 'ftsbool';
   const LANG_SOLR = 'solr';
 
   /**
@@ -122,6 +123,9 @@ class CRM_Utils_QueryFormatter {
       case self::LANG_SQL_FTS:
         $text = $this->_formatFts($text, $this->mode);
         break;
+      case self::LANG_SQL_FTSBOOL:
+        $text = $this->_formatFtsBool($text, $this->mode);
+        break;
       case self::LANG_SQL_LIKE:
         $text = $this->_formatLike($text, $this->mode);
         break;
@@ -143,11 +147,11 @@ class CRM_Utils_QueryFormatter {
     $text = str_replace('%', '*', $text);
 
     if (empty($text)) {
-      $result = '%';
+      $result = '*';
     }
     elseif (strpos($text, '*') !== FALSE) {
       // if user supplies their own wildcards, then don't do any sophisticated changes
-      return $text;
+      $result = $text;
     }
     else {
       switch ($mode) {
@@ -179,6 +183,49 @@ class CRM_Utils_QueryFormatter {
     return $this->dedupeWildcards($result, '%');
   }
 
+  protected function _formatFtsBool($text, $mode) {
+    $result = NULL;
+
+    // normalize user-inputted wildcards
+    $text = str_replace('%', '*', $text);
+
+    if (empty($text)) {
+      $result = '*';
+    }
+    elseif (strpos($text, '*') !== FALSE) {
+      // if user supplies their own wildcards, then don't do any sophisticated changes
+      $result = $this->mapWords($text, '+word');
+    }
+    else {
+      switch ($mode) {
+        case self::MODE_NONE:
+          $result = $this->mapWords($text, '+word');
+          break;
+
+        case self::MODE_PHRASE:
+          $result = '+"' . $text . '"';
+          break;
+
+        case self::MODE_WILDPHRASE:
+          $result = '+"*' . $text . '*"';
+          break;
+
+        case self::MODE_WILDWORDS:
+          $result = $this->mapWords($text, '+*word*');
+          break;
+
+        case self::MODE_WILDWORDS_SUFFIX:
+          $result = $this->mapWords($text, '+word*');
+          break;
+
+        default:
+          $result = NULL;
+      }
+    }
+
+    return $this->dedupeWildcards($result, '%');
+  }
+
   protected function _formatLike($text, $mode) {
     $result = NULL;
 
@@ -262,6 +309,7 @@ class CRM_Utils_QueryFormatter {
     return array(
       self::LANG_SOLR,
       self::LANG_SQL_FTS,
+      self::LANG_SQL_FTSBOOL,
       self::LANG_SQL_LIKE,
     );
   }
index ffe79a53e745c9f4f3a29b1766af383c63ca5f77..1786dbb4b603c34c655e3d6251b5ed280a688d89 100644 (file)
@@ -22,6 +22,12 @@ class CRM_Utils_QueryFormatterTest extends CiviUnitTestCase {
     $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTS, CRM_Utils_QueryFormatter::MODE_WILDWORDS,        '*first* *second*');
     $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTS, CRM_Utils_QueryFormatter::MODE_WILDWORDS_SUFFIX, 'first* second*');
 
+    $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL, CRM_Utils_QueryFormatter::MODE_NONE,             '+first +second');
+    $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL, CRM_Utils_QueryFormatter::MODE_PHRASE,           '+"first second"');
+    $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL, CRM_Utils_QueryFormatter::MODE_WILDPHRASE,       '+"*first second*"');
+    $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL, CRM_Utils_QueryFormatter::MODE_WILDWORDS,        '+*first* +*second*');
+    $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL, CRM_Utils_QueryFormatter::MODE_WILDWORDS_SUFFIX, '+first* +second*');
+
     $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SOLR, CRM_Utils_QueryFormatter::MODE_NONE,             'first second');
     $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SOLR, CRM_Utils_QueryFormatter::MODE_PHRASE,           '"first second"');
     $cases[] = array('first second', CRM_Utils_QueryFormatter::LANG_SOLR, CRM_Utils_QueryFormatter::MODE_WILDPHRASE,       '"*first second*"');
@@ -30,12 +36,14 @@ class CRM_Utils_QueryFormatterTest extends CiviUnitTestCase {
 
     // if user supplies wildcards, then ignore mode
     foreach (array(CRM_Utils_QueryFormatter::MODE_NONE, CRM_Utils_QueryFormatter::MODE_WILDPHRASE, CRM_Utils_QueryFormatter::MODE_WILDWORDS, CRM_Utils_QueryFormatter::MODE_WILDWORDS_SUFFIX) as $mode) {
-      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SQL_LIKE, $mode, 'first% second');
-      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SQL_FTS,  $mode, 'first* second');
-      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SOLR,     $mode, 'first* second');
-      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SQL_LIKE, $mode, 'first second%');
-      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SQL_FTS,  $mode, 'first second*');
-      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SOLR,     $mode, 'first second*');
+      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SQL_LIKE,     $mode, 'first% second');
+      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SQL_FTS,      $mode, 'first* second');
+      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL,  $mode, '+first* +second');
+      $cases[] = array('first% second', CRM_Utils_QueryFormatter::LANG_SOLR,         $mode, 'first* second');
+      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SQL_LIKE,     $mode, 'first second%');
+      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SQL_FTS,      $mode, 'first second*');
+      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SQL_FTSBOOL,  $mode, '+first +second*');
+      $cases[] = array('first second%', CRM_Utils_QueryFormatter::LANG_SOLR,         $mode, 'first second*');
     }
 
     return $cases;