import
[web.mtrack] / inc / search / solr.php
1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
3
4 class MTrackSearchEngineSolr implements IMTrackSearchEngine {
5   var $url;
6
7   public function __construct() {
8     $this->url = MTrackConfig::get('solr', 'url');
9   }
10
11   public function setBatchMode() {
12   }
13
14   function post($xml) {
15     $params = array(
16       'http' => array(
17         'method' => 'POST',
18         'content' => $xml,
19         'header' => 'Content-Type: text/xml',
20       ),
21     );
22     $ctx = stream_context_create($params);
23     for ($i = 0; $i < 10; $i++) {
24       $fp = fopen("$this->url/update", 'rb', false, $ctx);
25       if ($fp) {
26         fclose($fp);
27         return;
28       }
29       sleep(1);
30     }
31     throw new Exception("unable to update index; is Solr running?\n$xml\n");
32   }
33
34   public function commit($optimize = false) {
35     $this->post('<optimize/>');
36   }
37
38   public function add($object, $fields, $replace = false) {
39     $xml = "<add overwrite='true'><doc><field name='id'>$object</field>";
40     foreach ($fields as $key => $value) {
41       if (!strlen($value)) continue;
42       if (!strncmp($key, 'stored:', 7)) {
43         $key = substr($key, 7);
44       }
45
46       switch ($key) {
47         case 'date':
48         case 'created':
49           $t = strtotime($value);
50           $value = date('Y-m-d\\TH:i:s', $t) . 'Z';
51           break;
52       }
53       // avoid: HTTP/1.1 400 Illegal_character_CTRLCHAR_code_12
54       $value = str_replace("\x0c", " ", $value);
55
56       $xml .= "<field name='$key'>" .
57         htmlspecialchars($value, ENT_QUOTES, 'utf-8') .
58         "</field>";
59     }
60     $xml .= "</doc></add>";
61
62     $this->post($xml);
63   }
64
65   /** returns an array of MTrackSearchResult objects corresponding
66    * to matches to the supplied query string */
67   public function search($query) {
68     $q = http_build_query(array(
69       'q' => $query,
70       'version' => '2.2',
71       'hl' => 'on',
72       'hl.fl' => '',
73       'hl.usePhraseHighlighter' => 'on',
74       'hl.simple.pre' => "<span class='hl'>",
75       'hl.simple.post' => "</span>",
76       'fl' => 'id,score',
77       'wt' => 'json',
78       'rows' => 250,
79     ));
80     $json = file_get_contents("$this->url/select?$q");
81     $doc = json_decode($json);
82     //echo htmlentities($json);
83     //var_dump($doc);
84     $result = array();
85
86     /* look for excerpt text */
87     $hl = array();
88     foreach ($doc->highlighting as $name => $arr) {
89       $hl[$name] = array();
90       foreach ($arr as $fname => $v) {
91         foreach ($v as $a) {
92           $hl[$name][] = $a;
93         }
94       }
95     }
96
97     foreach ($doc->response->docs as $doc) {
98       $r = new MTrackSearchResult;
99       $r->objectid = $doc->id;
100       $r->score = $doc->score;
101       $r->excerpt = null;
102       if (isset($hl[$r->objectid])) {
103         $r->excerpt = "<div class='excerpt'>" .
104           join("\n", $hl[$r->objectid]) .
105           "</div>";
106       }
107       $result[] = $r;
108     }
109
110     return $result;
111   }
112 }