Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Slightly over-fuzzy fulltext indexing

  • Loading branch information...
commit b5543da24570ab6ced0b930399a69522906207a9 1 parent 603f6e4
Brendon Hogger authored
71 yii/modules/Yiidis/components/RedisFulltextIndex.php
... ... @@ -0,0 +1,71 @@
  1 +<?php
  2 +
  3 +class RedisFulltextIndex {
  4 +
  5 + public $keyPrefix;
  6 + public $cacheTime = 60;
  7 +
  8 + public $stopwords = array(
  9 + "the", "a", "an", "of", "at"
  10 + );
  11 +
  12 + public function __construct($prefix) {
  13 + $this->keyPrefix = $prefix;
  14 + }
  15 +
  16 + public function redisKey($key, $subKey) {
  17 + return $this->keyPrefix . ':' . $key . ':' . $subKey;
  18 + }
  19 +
  20 + public function index($key, $phrase) {
  21 + $scores = $this->analyze($phrase);
  22 + foreach($scores as $fragment=>$score) {
  23 + Yii::app()->redis->conn->zadd(
  24 + $this->redisKey("f", $fragment),
  25 + $score, $key);
  26 + }
  27 + }
  28 +
  29 + public function search($phrase, $offset=0, $limit=20) {
  30 + $scores = $this->analyze($phrase);
  31 + $keys = array_keys($scores);
  32 + sort($keys);
  33 + $key = implode("-", $keys);
  34 + $searchKey = $this->redisKey("s", $key);
  35 + $conn = Yii::app()->redis->conn;
  36 +
  37 + if (!$conn->expire($searchKey, $this->cacheTime)) {
  38 + $args = array();
  39 + $weights = array();
  40 + foreach($scores as $key => $score) {
  41 + $args[] = $this->redisKey("f", $key);
  42 + $weights[] = strlen($key) * $score;
  43 + }
  44 +
  45 + $args = array($searchKey, $args, $weights);
  46 + $command = $conn->createCommand("zunionstore", $args);
  47 + $conn->executeCommand($command);
  48 + $conn->expire($searchKey, $this->cacheTime);
  49 + }
  50 + $results = $conn->zrevrange($searchKey, $offset, $offset + $limit);
  51 + return $results;
  52 + }
  53 +
  54 + public function analyze($phrase) {
  55 + $phrase = preg_replace('/[^\p{L}\p{N}\p{Z}]/u', '', strtolower($phrase));
  56 +
  57 + $words = preg_split('/[\p{Z}]/u', $phrase);
  58 +
  59 + $scores = array();
  60 + foreach($words as $word) {
  61 + if (in_array($word, $this->stopwords)) continue;
  62 + list($a, $b) = double_metaphone(stem($word));
  63 + if (!$a) $a = $word;
  64 + $scores[$a] = isset($scores[$a]) ? $scores[$a]+1 : 1;
  65 + if ($b != $a) isset($scores[$b]) ? $scores[$b]+1 : 1;
  66 + }
  67 +
  68 + return $scores;
  69 + }
  70 +
  71 +}
26 yii/modules/Yiidis/components/RedisModel.php
@@ -68,6 +68,30 @@ public static function fromJSONArray($keysAndBlobs, $skipPrefix=false) {
68 68 return $objs;
69 69 }
70 70
  71 + public static function fromJSONArrays($keys, $blobs, $skipPrefix=false) {
  72 + $class = get_called_class();
  73 + $objs = array();
  74 +
  75 + for ($i = 0; $i < count($keys); $i ++) {
  76 + $objs[] = $class::fromJSON($keys[$i], $blobs[$i], $skipPrefix);
  77 + }
  78 +
  79 + return $objs;
  80 + }
  81 +
  82 + public static function fromKeyArray($keys, $skipPrefix=false) {
  83 + $class = get_called_class();
  84 +
  85 + if ($skipPrefix) {
  86 + $getKeys = $keys;
  87 + } else {
  88 + $getKeys = array_map(function($k) use ($class) { return "{$class::$_keyPrefix}:$k"; }, $keys);
  89 + }
  90 +
  91 + $blobs = Yii::app()->redis->conn->mget($getKeys);
  92 + return $class::fromJSONArrays($keys, $blobs);
  93 + }
  94 +
71 95 public static function getJSON($key, $skipPrefix=false) {
72 96 $class = get_called_class();
73 97 if ($skipPrefix) {
@@ -103,14 +127,12 @@ public static function ensure($key) {
103 127 }
104 128 }
105 129
106   -
107 130 public static function model($className=__CLASS__) {
108 131 if (isset(self::$_models[$className]))
109 132 return self::$_models[$className];
110 133
111 134 $model = self::$_models[$className] = new $className(null, null);
112 135 return $model;
113   -
114 136 }
115 137
116 138 /* -------------------------------------------- */

0 comments on commit b5543da

Please sign in to comment.
Something went wrong with that request. Please try again.