Ticket #90: svn.ticket90.search-on-part-of-a-tag.patch

File svn.ticket90.search-on-part-of-a-tag.patch, 20.9 KB (added by sebastian, 2 years ago)

Initial proposal to search for part of tags

  • search.php

     
    6565  /** Set a singular parameter  
    6666    @param name Parameter name 
    6767    @param value Parameter value 
     68    @param validate Optional parameter to validate the parameter. Default is 
     69    true 
    6870    @return True on success */ 
    69   function setParam($name, $value) { 
    70     if ($this->validate($name, $value)) { 
     71  function setParam($name, $value, $validate = true) { 
     72    if ($validate === false || $this->validate($name, $value)) { 
    7173      $this->_data[$name] = $value;     
    7274      return true; 
    7375    } else { 
     
    7577    } 
    7678  } 
    7779 
    78   function addParam($name, $value) { 
     80  /** Add a parameter to an array. 
     81    @param name Parameter name.  
     82    @param value Parameter value (which will be pluralized) 
     83    @param validate Optional parameter to validate the parameter. Default is 
     84    true 
     85    @note The name will be pluralized. */ 
     86  function addParam($name, $value, $validate = true) { 
    7987    $name = Inflector::pluralize($name); 
    8088    if (is_array($value)) { 
    8189      foreach ($value as $v) { 
    82         $this->addParam($name, $v); 
     90        $this->addParam($name, $v, $validate); 
    8391      } 
    8492      return; 
    8593    } 
    8694     
    8795    if ((!isset($this->_data[$name]) || !in_array($value, $this->_data[$name])) && 
    88       $this->validate($name, $value)) { 
     96      ($validate === false || $this->validate($name, $value))) { 
    8997      $this->_data[$name][] = $value; 
    9098    } 
    9199  } 
     
    133141      case 'set': 
    134142        if (count($args) == 1) { 
    135143          return $this->setParam($name, $args[0]); 
     144        } elseif (count($args) == 2) { 
     145          return $this->setParam($name, $args[0], $args[1]); 
    136146        } 
    137147        break; 
    138148      case 'add': 
    139149        if (count($args) == 1) { 
    140150          return $this->addParam($name, $args[0]); 
     151        } elseif (count($args) == 2) { 
     152          return $this->addParam($name, $args[0], $args[1]); 
    141153        } 
    142154        break; 
    143155      case 'del': 
  • views/explorer/quicksearch.ctp

     
    11<h1>Quick Search Results</h1> 
    22<?php $session->flash(); ?> 
    33 
    4 <div class="minis"> 
    5 <script type="text/javascript"> 
    6   var mediaData = []; 
    7 </script> 
    84 
    95<?php 
    106$search->initialize(); 
    117$cell=0; 
    128 
    13 if (count($dataTags) + count($dataCategories) + count($dataLocations) == 0): ?> 
     9if (!count($this->data)): ?> 
    1410<div class="info"> 
    1511<?php printf(__("Sorry, nothing was found for %s", true), h($quicksearch)); ?> 
    1612</div> 
    17 <?php endif; ?> 
     13<?php else: ?> 
    1814 
    19 <?php // -- Output for Tags -- 
    20 if (count($dataTags) > 0) : ?> 
    21 <h2>Results for Tags:</h2> 
    22 <div align="left">  
    23 <?php  
    24   foreach($dataTags as $media) { 
    25     echo $imageData->mediaLink($media, 'mini').' '; 
    26   } 
    27 ?> 
    28 </div> 
     15<h2><?php printf(__('Results of %s', true), h($quicksearch)); ?></h2> 
     16<div class="minis" align="left"> 
     17<script type="text/javascript"> 
     18  var mediaData = []; 
     19</script> 
    2920 
    30 <?php 
    31   echo 'See more results with tag: '; 
    32   $names = Set::extract('/Tag/name', $dataTags); 
    33   $names = array_unique($names); 
    34   $links = array(); 
    35   foreach ($names as $name) { 
    36     $links[] = $html->link($name, '/explorer/tag/'.$name); 
    37   } 
    38   echo implode(', ', $links); 
    39 ?> 
    40 <?php endif; /* if (count($dataTags) > 0) */ ?>   
    41  
    42 <?php // -- Output for Categories -- 
    43 if (count($dataCategories) > 0) : ?> 
    44 <h2>Results for Categories:</h2> 
    45 <div align="left">  
    4621<?php  
    47   foreach($dataCategories as $media) { 
     22  foreach($this->data as $media) { 
    4823    echo $imageData->mediaLink($media, 'mini').' '; 
    4924  } 
    5025?> 
    5126</div> 
    5227 
    5328<?php 
    54   echo 'See more results with category: '; 
    55   $names = Set::extract('/Category/name', $dataCategories); 
    56   $names = array_unique($names); 
    57   $links = array(); 
    58   foreach ($names as $name) { 
    59     $links[] = $html->link($name, '/explorer/category/'.$name); 
     29  $tags = Set::extract('/Tag/name', $this->data); 
     30  if (count($tags)) { 
     31    echo '<p>' . __('See more results of tag', true) .  ': '; 
     32    $tags = array_unique($tags); 
     33    $links = array(); 
     34    foreach ($tags as $name) { 
     35      $links[] = $html->link($name, '/explorer/tag/'.$name); 
     36    } 
     37    echo implode(', ', $links) . '</p>'; 
    6038  } 
    61   echo implode(', ', $links); 
    62 ?> 
    63 <?php endif; /* if (count($dataCategories) > 0) */ ?>   
    6439 
    65 <?php // -- Output for Locations -- 
    66 if (count($dataLocations) > 0) : ?> 
    67 <h2>Results for Locations:</h2> 
    68 <div align="left">  
    69 <?php  
    70   foreach($dataLocations as $media) { 
    71     echo $imageData->mediaLink($media, 'mini').' '; 
     40  $categories = Set::extract('/Category/name', $this->data); 
     41  if (count($categories)) { 
     42    echo '<p>' . __('See more results of category', true) .  ': '; 
     43    $categories = array_unique($categories); 
     44    $links = array(); 
     45    foreach ($categories as $name) { 
     46      $links[] = $html->link($name, '/explorer/category/'.$name); 
     47    } 
     48    echo implode(', ', $links) . '</p>'; 
    7249  } 
    73 ?> 
    74 </div> 
    7550 
    76 <?php 
    77   echo 'See more results with location: '; 
    78   $names = Set::extract('/Location/name', $dataLocations); 
    79   $names = array_unique($names); 
    80   $links = array(); 
    81   foreach ($names as $name) { 
    82     $links[] = $html->link($name, '/explorer/location/'.$name); 
     51  $locations = Set::extract('/Location/name', $this->data); 
     52  if (count($locations)) { 
     53    echo '<p>' . __('See more results of location', true) .  ': '; 
     54    $locations = array_unique($locations); 
     55    $links = array(); 
     56    foreach ($locations as $name) { 
     57      $links[] = $html->link($name, '/explorer/location/'.$name); 
     58    } 
     59    echo implode(', ', $links) . '</p>'; 
    8360  } 
    84   echo implode(', ', $links); 
    8561?> 
    86 <?php endif; /* if (count($dataLocations) > 0) */ ?>   
    87  
    88 </div> 
     62<?php endif; ?> 
  • views/elements/topnav.ctp

     
    1818  } 
    1919 
    2020  echo "\n<div class=\"searchBox\" ><div class=\"searchBoxSub\" >"; 
    21   echo $form->create(null, array('url' => array('controller' => 'explorer', 'action' => 'quicksearch')));  
     21  echo $form->create(null, array('url' => '/explorer/quicksearch'));  
    2222  echo $form->input('Media.quicksearch', array ('label' => false, 'div' => false)); 
    2323  $icon = Router::url("/img/icons/zoom.png"); 
    2424  echo "<input type=\"image\" src=\"$icon\" width=\"16\" height=\"16\" id=\"go\" alt=\"Search\" title=\"Search\" />"; 
  • tests/cases/components/search.test.php

     
    11<?php 
    22App::import('Core', array('Controller')); 
    33App::import('Component', array('Search')); 
     4App::import('File', 'Logger', array('file' => APP.'logger.php')); 
    45 
    56Mock::generatePartial('SearchComponent', 'NoStopSearch', array('_stop')); 
    67 
     
    8182    $result = $this->Search->getShow(); 
    8283    $this->assertEqual($result, 12); 
    8384 
     85    // one rule, disabled validation 
     86    $this->Search->setShow('no validation', false); 
     87    $result = $this->Search->getShow(); 
     88    $this->assertEqual($result, 'no validation'); 
     89 
    8490    // multple rules 
    8591    $this->Search->addTag(array('he', 'the')); 
    8692    $result = $this->Search->getTags(); 
    8793    $this->assertEqual($result, array('the')); 
    8894 
     95    // multple rules, disabled validation 
     96    $result = $this->Search->delTags(); 
     97    $this->Search->addTag(array('he', '+_?'), false); 
     98    $result = $this->Search->getTags(); 
     99    $this->assertEqual($result, array('he', '+_?')); 
     100 
    89101    // disabled parameter 
    90102    $this->Search->setUser('joe'); 
    91103    $result = $this->Search->getUser(); 
     
    100112    $this->Search->setWorld("rule it"); 
    101113    $result = $this->Search->getWorld(); 
    102114    $this->assertEqual($result, null); 
     115     
     116    // disabled parameter with disabled validation 
     117    $this->Search->setWorld("rule it", false); 
     118    $result = $this->Search->getWorld(); 
     119    $this->assertEqual($result, "rule it"); 
    103120  } 
    104121 
    105122} 
  • config/sql/schema.php

     
    3535        var $categories = array( 
    3636                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
    3737                        'name' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 64, 'key' => 'index'), 
    38                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name' => array('column' => 'name', 'unique' => 0)) 
     38                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name_index' => array('column' => 'name', 'unique' => 0)) 
    3939                ); 
    4040        var $categories_media = array( 
    4141                        'media_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    4242                        'category_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    43                         'indexes' => array('PRIMARY' => array('column' => array('media_id', 'category_id'), 'unique' => 1), 'setid' => array('column' => 'category_id', 'unique' => 0)) 
     43                        'indexes' => array('PRIMARY' => array('column' => array('media_id', 'category_id'), 'unique' => 1), 'category_id_index' => array('column' => 'category_id', 'unique' => 0), 'media_id_index' => array('column' => 'media_id', 'unique' => 0)) 
    4444                ); 
    4545        var $comments = array( 
    4646                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
     
    7070                        'size' => array('type'=>'integer', 'null' => false), 
    7171                        'time' => array('type'=>'datetime', 'null' => false), 
    7272                        'readed' => array('type'=>'datetime', 'null' => true, 'default' => NULL), 
    73                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'id' => array('column' => 'id', 'unique' => 0), 'user_id' => array('column' => 'user_id', 'unique' => 0), 'media_id' => array('column' => 'media_id', 'unique' => 0)) 
     73                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'user_id_index' => array('column' => 'user_id', 'unique' => 0), 'media_id_index' => array('column' => 'media_id', 'unique' => 0)) 
    7474                ); 
    7575        var $groups = array( 
    7676                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
    7777                        'user_id' => array('type'=>'integer', 'null' => true, 'default' => NULL), 
    7878                        'name' => array('type'=>'string', 'null' => false, 'length' => 32), 
    79                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'id' => array('column' => 'id', 'unique' => 0)) 
     79                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) 
    8080                ); 
    8181        var $groups_users = array( 
    8282                        'user_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    8383                        'group_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    84                         'indexes' => array('PRIMARY' => array('column' => array('user_id', 'group_id'), 'unique' => 1), 'userid' => array('column' => 'user_id', 'unique' => 0), 'groupid' => array('column' => 'group_id', 'unique' => 0)) 
     84                        'indexes' => array('PRIMARY' => array('column' => array('user_id', 'group_id'), 'unique' => 1), 'user_id_index' => array('column' => 'user_id', 'unique' => 0), 'group_id_index' => array('column' => 'group_id', 'unique' => 0)) 
    8585                ); 
    8686        var $locks = array( 
    8787                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
     
    9494                        'exclusivelock' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    9595                        'created' => array('type'=>'datetime', 'null' => true, 'default' => NULL), 
    9696                        'modified' => array('type'=>'datetime', 'null' => true, 'default' => NULL), 
    97                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'token' => array('column' => 'token', 'unique' => 0), 'expires' => array('column' => 'expires', 'unique' => 0)) 
     97                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'token_index' => array('column' => 'token', 'unique' => 0), 'expires_index' => array('column' => 'expires', 'unique' => 0)) 
    9898                ); 
    9999        var $locations = array( 
    100100                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
    101101                        'name' => array('type'=>'string', 'null' => false, 'length' => 64, 'key' => 'index'), 
    102102                        'type' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 3), 
    103                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name' => array('column' => 'name', 'unique' => 0)) 
     103                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name_index' => array('column' => 'name', 'unique' => 0)) 
    104104                ); 
    105105        var $locations_media = array( 
    106106                        'location_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    107107                        'media_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    108                         'indexes' => array('PRIMARY' => array('column' => array('media_id', 'location_id'), 'unique' => 1), 'locationid' => array('column' => 'location_id', 'unique' => 0)) 
     108                        'indexes' => array('PRIMARY' => array('column' => array('media_id', 'location_id'), 'unique' => 1), 'location_id_index' => array('column' => 'location_id', 'unique' => 0), 'media_id_index' => array('column' => 'media_id', 'unique' => 0)) 
    109109                ); 
    110110        var $media = array( 
    111111                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
     
    136136                        'ranking' => array('type'=>'float', 'null' => true, 'default' => '0', 'key' => 'index'), 
    137137                        'voting' => array('type'=>'float', 'null' => true, 'default' => '0'), 
    138138                        'votes' => array('type'=>'integer', 'null' => true, 'default' => '0'), 
    139                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'date' => array('column' => 'date', 'unique' => 0), 'ranking' => array('column' => 'ranking', 'unique' => 0), 'id' => array('column' => 'id', 'unique' => 0), 'oacl' => array('column' => 'oacl', 'unique' => 0)) 
     139                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'date_index' => array('column' => 'date', 'unique' => 0), 'ranking_index' => array('column' => 'ranking', 'unique' => 0), 'oacl_index' => array('column' => 'oacl', 'unique' => 0)) 
    140140                ); 
    141141        var $media_tags = array( 
    142142                        'media_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    143143                        'tag_id' => array('type'=>'integer', 'null' => false, 'default' => '0'), 
    144                         'indexes' => array('PRIMARY' => array('column' => array('media_id', 'tag_id'), 'unique' => 1), 'imageid' => array('column' => 'media_id', 'unique' => 0), 'tagid' => array('column' => 'tag_id', 'unique' => 0)) 
     144                        'indexes' => array('PRIMARY' => array('column' => array('media_id', 'tag_id'), 'unique' => 1), 'media_id_index' => array('column' => 'media_id', 'unique' => 0), 'tag_id_index' => array('column' => 'tag_id', 'unique' => 0)) 
    145145                ); 
    146146        var $options = array( 
    147147                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
    148148                        'user_id' => array('type'=>'integer', 'null' => true, 'default' => NULL), 
    149149                        'name' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 64), 
    150150                        'value' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 192), 
    151                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'user_id' => array('column' => 'user_id', 'unique' => 0)) 
     151                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'user_id_index' => array('column' => 'user_id', 'unique' => 0)) 
    152152                ); 
    153153        var $properties = array( 
    154154                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
     
    161161        var $tags = array( 
    162162                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
    163163                        'name' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 64, 'key' => 'index'), 
    164                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name' => array('column' => 'name', 'unique' => 0)) 
     164                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'name_index' => array('column' => 'name', 'unique' => 0)) 
    165165                ); 
    166166        var $users = array( 
    167167                        'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'), 
     
    177177                        'firstname' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 32), 
    178178                        'lastname' => array('type'=>'string', 'null' => false, 'length' => 32), 
    179179                        'email' => array('type'=>'string', 'null' => true, 'default' => NULL, 'length' => 64), 
    180                         'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1), 'id' => array('column' => 'id', 'unique' => 0)) 
     180                        'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) 
    181181                ); 
    182182} 
    183183?> 
  • controllers/components/search.php

     
    337337  
    338338    return $data; 
    339339  } 
     340 
     341  function quicksearch($text, $show = 12) { 
     342    $words = preg_split('/\s+/', trim($text)); 
     343 
     344    $tmp = array(); 
     345    foreach($words as $word) { 
     346      $tmp[] = '*' . $word . '*'; 
     347    } 
     348    $words = $tmp; 
     349 
     350    $this->addTags($words, false); 
     351    $this->addCategories($words, false); 
     352    $this->addLocations($words, false); 
     353    $this->setOperand('OR'); 
     354 
     355    $this->setSort('default', false); 
     356    $this->setShow($show); 
     357 
     358    return $this->paginate(); 
     359  }     
    340360} 
    341361?> 
  • controllers/components/upgrade_schema.php

     
    191191        $modelName = $this->modelMapping[$table]; 
    192192      } 
    193193      if (!in_array($modelName, $models)) { 
    194         Logger::err("Model '$modelName' does not exists"); 
    195         $columns[$table] = "Model '$modelName' does not exists"; 
    196         trigger_error(sprintf(__("Model '%s' does not exists", true), $modelName), E_USER_WARNING); 
    197         continue; 
     194        Logger::warn("Model '$modelName' does not exists"); 
    198195      } 
    199196 
    200197      $columns[$table] = $this->db->alterSchema(array($table => $changes), $table); 
  • controllers/components/query_builder.php

     
    240240          $counts = array(); 
    241241          $conditions = array(); 
    242242          foreach ($query['_counts'] as $count) { 
    243             $counts[] = "( $count + 1 )"; 
     243            $counts[] = "( COALESCE($count, 0) + 1 )"; 
    244244            $conditions[] = "COALESCE($count, 0)"; 
    245245          } 
    246246          $query['conditions'][] = '( '.implode(' + ', $conditions).' ) > 0'; 
     
    299299    $habtm = Inflector::singularize($name); 
    300300 
    301301    $field = Inflector::camelize($habtm).'.name'; 
    302     $query['conditions'][] = $this->_buildCondition($field, $value); 
    303302 
     303    $tags = array(); 
     304    foreach($value as $v) { 
     305      if (preg_match('/[*\?]/', $v)) { 
     306        $v = preg_replace('/\*/', '%', $v); 
     307        $v = preg_replace('/\?/', '_', $v); 
     308        $query['conditions'][] = $this->_buildCondition($field, $v, array('operand' => 'LIKE')); 
     309      } else { 
     310        $tags[] = $v; 
     311      } 
     312    } 
     313    if (count($tags)) { 
     314      $query['conditions'][] = $this->_buildCondition($field, $tags); 
     315    } 
     316 
    304317    $fieldCount = Inflector::camelize($habtm).'Count'; 
    305318    $query['_counts'][] = $fieldCount; 
    306319 
  • controllers/explorer_controller.php

     
    7070    }  
    7171 
    7272    if ($quicksearch) { 
    73       // Perform queries for each Tags, Categories and Locations 
    74       // seperately: 
    75  
    76       // Split the query so that we have a list of tags/categories/locations. 
    77       // For now we split at whitespaces, improvements could be made to not 
    78       // split multiple tags/categories/locations enclosed in quotation marks 
    79       $words = preg_split('/\s+/', trim($quicksearch)); 
    80  
    81       // Reduce results to 6  
    82       $this->Search->setShow(6); 
    83  
    84       // Add tag to the query 
    85       $this->Search->addTags($words); 
    86       // Set variable dataTags for view 
    87       $this->Search->setTagOp('OR'); 
    88       $this->set('dataTags', $this->Search->paginate()); 
    89       // Reset query 
    90       $this->Search->delTags(); 
    91       $this->Search->delTagOp(); 
    92  
    93       $this->Search->addCategories($words); 
    94       $this->Search->setCategoryOp('OR'); 
    95       $this->set('dataCategories', $this->Search->paginate()); 
    96       $this->Search->delCategories(); 
    97       $this->Search->delCategoryOp(); 
    98  
    99       $this->Search->addLocations($words); 
    100       $this->Search->setLocationOp('OR'); 
    101       $this->set('dataLocations', $this->Search->paginate()); 
    102       $this->Search->delLocations(); 
    103       $this->Search->delLocationOp(); 
    104     } else { 
    105       $this->set('dataTags', array()); 
    106       $this->set('dataCategories', array()); 
    107       $this->set('dataLocations', array()); 
     73      $this->data = $this->Search->quicksearch($quicksearch, 6); 
    10874    } 
    10975    $this->set('quicksearch', $quicksearch); 
    11076  }