1<?php
2/*
3 * To change this template, choose Tools | Templates
4 * and open the template in the editor.
5 */
6
7class SphinxSearch
8{
9    private $_sphinx = null;
10    private $_result = array();
11    private $_index = null;
12    private $_query = '';
13
14    private $_snippetSize = 256;
15    private $_aroundKeyword = 5;
16    private $_resultsPerPage = 10;
17
18    private $_titlePriority = 1;
19    private $_bodyPriority = 1;
20    private $_namespacePriority = 1;
21    private $_pagenamePriority = 1;
22
23    public function  __construct($host, $port, $index)
24    {
25        $this->_sphinx = new SphinxClient();
26        $this->_sphinx->SetServer($host, $port);
27        $this->_sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
28
29        $this->_index = $index;
30    }
31
32    public function setSearchAllQuery($keywords, $categories)
33    {
34        $keywords = $this->_sphinx->EscapeString($keywords);
35        $keywords = $this->_enableQuotesAndDefis($keywords);
36        $starKeyword = $this->starQuery($keywords);
37        $this->_query = "(@(namespace,pagename) $starKeyword) | (@(body,title) {$keywords})";
38    }
39
40    public function setSearchAllQueryWithCategoryFilter($keywords, $categories)
41    {
42        $keywords = $this->_sphinx->EscapeString($keywords);
43        $keywords = $this->_enableQuotesAndDefis($keywords);
44        $starKeyword = $this->starQuery($keywords);
45        if(strpos($categories, "-") === 0){
46            $categories = '-"'.substr($categories, 1).'"';
47        }
48        $this->_query = "(@(namespace,pagename) {$categories}) & ((@(body,title) {$keywords}) | (@(namespace,pagename) {$starKeyword}))";
49    }
50
51    public function setSearchCategoryQuery($keywords, $categories)
52    {
53        $keywords = $this->_sphinx->EscapeString($keywords);
54        $keywords = $this->_enableQuotesAndDefis($keywords);
55
56        $starKeyword = $this->starQuery($keywords);
57        if (!empty($categories)){
58            $this->_query = "(@(namespace,pagename) $categories $starKeyword)";
59        } else {
60            $this->_query = "(@(namespace,pagename) $starKeyword)";
61        }
62
63    }
64
65    public function setSearchOnlyPagename()
66    {
67    	$this->_query = "(@(pagename) {$this->_query})";
68    }
69
70    public function search($start, $resultsPerPage = 10)
71    {
72        $this->_resultsPerPage = $resultsPerPage;
73
74        $this->_sphinx->SetFieldWeights(array(
75            'namespace' => $this->_namespacePriority,
76            'pagename' => $this->_pagenamePriority,
77            'title' => $this->_titlePriority,
78            'body' => $this->_bodyPriority)
79        );
80
81        $this->_sphinx->SetLimits($start, $resultsPerPage+100, 1000);
82
83        $this->_result = $this->_sphinx->Query($this->_query, $this->_index);
84
85        if (empty($this->_result['matches'])) {
86            return false;
87	    }
88        return true;
89    }
90
91    public function getPages($keywords)
92    {
93        if (empty($this->_result['matches'])) {
94            return false;
95	    }
96
97        $pagesIdsAll = $this->getPagesIds();
98        $this->_offset = 0;
99        $counter = 0;
100        $tmpRes = array();
101        $pagesIds = array();
102        foreach($pagesIdsAll as $id => $pageData){
103            $this->_offset++;
104            if(auth_quickaclcheck($pageData['page']) >= AUTH_READ){
105                if(!isset($tmpRes[$pageData['page']])){
106                    $tmpRes[$pageData['page']] = 1;
107                    $counter++;
108                }
109                $pagesIds[$id] = $pageData;
110                if ($counter == $this->_resultsPerPage){
111                    break;
112                }
113            } else {
114                // decrease total found counter for the first page if the page is filtered
115                $this->_result['total_found']--;
116            }
117        }
118        if (empty($pagesIds)){
119            return false;
120        }
121
122        $pagesList = array();
123        $body = array();
124        $titleText = array();
125        $category = array();
126        foreach ($pagesIds as $crc => $data){
127            if (empty($data['page'])){
128                continue;
129            }
130            if (!empty($data['hid'])){
131                $bodyHtml = p_render('xhtml',p_get_instructions(getSectionByTitleLevel($data['page'], $data['title'], true)),$info);
132            } else {
133                $bodyHtml = p_wiki_xhtml($data['page']);
134            }
135            $bodyHtml = preg_replace("#[\s]+?</li>#", "</li>;", $bodyHtml);
136            $bodyHtml = htmlspecialchars_decode($bodyHtml);
137            $body[$crc] = strip_tags($bodyHtml);
138            if(!empty($data['title_text'])){
139                $titleText[$crc] = strip_tags($data['title_text']);
140            } else {
141                $titleText[$crc] = $data['page'];
142            }
143            $category[$crc] = $data['page'];
144        }
145
146        //$starQuery = $this->starQuery($keywords);
147        $bodyExcerpt = $this->getExcerpt($body, $keywords);
148        $titleTextExcerpt = $this->getExcerpt($titleText, $keywords);
149        $i = 0;
150        $results = array();
151        foreach($body as $crc => $notused){
152            $results[$crc] = array(
153                'page' => $pagesIds[$crc]['page'],
154                'bodyExcerpt' => $bodyExcerpt[$i],
155                'titleTextExcerpt' => $titleTextExcerpt[$i],
156                'hid' => $pagesIds[$crc]['hid'],
157                'title' => $pagesIds[$crc]['title'],
158                'title_text' => $pagesIds[$crc]['title_text']
159            );
160            $i++;
161        }
162        return $results;
163    }
164
165    public function getPagesIds()
166    {
167        $pageMapper = new PageMapper();
168
169        return $pageMapper->getByCrc(array_keys($this->_result['matches']));
170    }
171
172    public function getOffset()
173    {
174        return $this->_offset;
175    }
176
177    public function getPageNames()
178    {
179        $pageIds = $this->getPagesIds();
180
181        $matchPages = array();
182        foreach ($pageIds as $page) {
183            if (auth_quickaclcheck($page['page']) < AUTH_READ) {
184                continue;
185            }
186            $matchPages[$page['page']] = $page['hid'];
187        }
188
189        return $matchPages;
190    }
191
192    public function getError()
193    {
194        return $this->_sphinx->GetLastError();
195    }
196
197    public function getTotalFound()
198    {
199        return !empty($this->_result['total_found'])?$this->_result['total_found'] : 0;
200    }
201
202    public function getExcerpt($data, $query)
203    {
204        return $this->_sphinx->BuildExcerpts($data, $this->_index, $query,
205                    array(
206                        'limit' => $this->_snippetSize,
207                        'around' => $this->_aroundKeyword,
208                        'weight_order' => 1,
209                        'sp' => 1
210                    )
211                );
212    }
213
214    public function starQuery($query)
215    {
216        $query = $this->removeStars($query);
217        $words = explode(" ", $query);
218        foreach($words as $id => $word){
219            $words[$id] = "*".$word."*";
220        }
221        return implode(" ", $words);
222    }
223
224    public function removeStars($query)
225    {
226        $words = explode(" ", $query);
227        foreach($words as $id => $word){
228            $words[$id] = trim($word, "*");
229        }
230        return implode(" ", $words);
231    }
232
233    public function getQuery()
234    {
235        return $this->_query;
236    }
237
238    public function setSnippetSize($symbols = 256)
239    {
240        $this->_snippetSize = $symbols;
241    }
242
243    public function setArroundWordsCount($words = 5)
244    {
245        $this->_aroundKeyword = $words;
246    }
247
248    public function setTitlePriority($priority)
249    {
250        $this->_titlePriority = $priority;
251    }
252
253    public function setBodyPriority($priority)
254    {
255        $this->_bodyPriority = $priority;
256    }
257
258    public function setNamespacePriority($priority)
259    {
260        $this->_namespacePriority = $priority;
261    }
262
263    public function setPagenamePriority($priority)
264    {
265        $this->_pagenamePriority = $priority;
266    }
267
268    private function _enableQuotesAndDefis($query)
269    {
270        $query = ' '. $query;
271        $quotesCount = count(explode('"', $query))-1;
272        if ($quotesCount && $quotesCount%2 == 0){
273            $query = str_replace('\"', '"', $query);
274        }
275        $query = preg_replace("#\s\\\-(\w)#ui", " -$1", $query);
276
277        $query = substr($query, 1);
278
279        return $query;
280    }
281}
282