source: spip-zone/_plugins_/sphinx/trunk/inc/sphinx.php @ 69849

Last change on this file since 69849 was 69849, checked in by fil@…, 7 years ago

loger une eventuelle erreur dans sphinx.log

File size: 7.8 KB
Line 
1<?php
2
3function recherchesphinx_array_dist($recherche, $options) {
4        $options = array_merge(
5                array('table' => 'article',
6                ),
7                (array)$options
8        );
9        $table = $options['table'];
10        $serveur = $options['serveur'];
11
12        # router les recherches article vers la base sphinx
13        if ($table == 'article') {
14                # array ( id_article => score, id_article => score ... );
15                $u = serialize(array('recherche' => $recherche));
16                $a = array();
17                foreach(inc_sphinx_to_array_dist($u, $selection='ecrire') as $r) {
18                        if ($id_article = $r['attrs']['id_objet']
19                        AND $r['attrs']['source'] == 1)
20                                $a[$id_article] = $r['weight'];
21                }
22                return $a;
23        }
24
25        # recherche d'un autre type
26        include_spip('inc/recherche_to_array');
27        return inc_recherche_to_array_dist($recherche, $options);
28}
29
30
31/**
32 * sphinx -> tableau
33 * @param string $u "serialize(env)"
34 * @param string $selection : indiquer quelle sŽlection on demande
35 * @return array|bool
36 */
37function inc_sphinx_to_array_dist($u, $selection=null) {
38        static $mem;
39
40        if (!$env = @unserialize($u))
41                return false;
42
43        spip_timer('total');
44
45
46        // les trucs demandŽs
47        $recherche = trim($env['recherche']);  # recherche fulltext
48
49        // appel par une page mot :
50        //   (ou limiter aux mots-clŽs)
51        // ou par l'url &mots=italie
52        // sprintf('%u') necessaire car le crc32 en 32 bits n'est pas le mme qu'en 64
53        if (strlen($env['mots'])) {
54                $cmot = sprintf('%u',crc32(trim(mb_strtolower($env['mots'], 'UTF-8'))));
55        }
56
57        # TODO
58        # $mots peut tre un array &mots[]=DŽfense, ou un string &mots=DŽfense
59        # &debut=1962-05&fin=2005-09 : plage temporelle
60        # &id_auteur=13226 => auteur (highlander)
61       
62
63        # validitŽ de la requte ?
64        if (!strlen($recherche)
65        AND !isset($cmot)
66        )
67                return false;
68
69        # charger sphinx
70        include_spip('lib/sphinxapi');
71        $cl = new SphinxClient ();
72
73        if (defined('_SPHINX_SERVER'))
74                $cl->SetServer(_SPHINX_SERVER, defined('_SPHINX_PORT') ? _SPHINX_PORT : 9312);
75
76        # visible = publie ou prop dans certains cas (voir sphinx.conf)
77        $cl->SetFilter ( "visible", array(1) );
78
79        # limites
80        $max_pagination = 10;
81
82        $debut = intval($env['debut_'.$selection]);
83        $cl->SetLimits($debut, $max_pagination);
84
85
86        # mot-clŽ sans fulltext
87        if (isset($cmot))
88                $cl->SetFilter ( "cmot", array($cmot), $exclude=false );
89
90        # matching mode
91        if (strlen($recherche)) {
92                if (preg_match(',[&|],', $recherche)) {
93                        $cl->SetMatchMode ( SPH_MATCH_BOOLEAN );
94                } else {
95                        $cl->SetMatchMode ( SPH_MATCH_EXTENDED2 );
96                }
97        } else {
98                $cl->SetMatchMode ( SPH_MATCH_FULLSCAN );
99        }
100
101        # que veut-on recuperer comme elements
102        $cl->SetSelect ( "*" );
103
104        # filtrage
105        #$cl->SetFilter ( "autotags", $tags );
106        #$cl->SetFilterRange ( "date", $min, $max, $exclude=false );
107
108        # booster le titre et l'auteur
109        $cl->SetFieldWeights(array(
110                'auteurs' => 10,
111                'titre' => 7,
112                'surtitre' => 4,
113                'petit' => 3,
114        ));
115
116        switch ($env['tri']) {
117                case 'relevance':
118                        $cl->SetSortMode ( SPH_SORT_RELEVANCE );
119                        break;
120                # tri par "time segments" :
121                #    1 heure, 1 jour, 1 semaine, 1 mois, 3 mois, et le reste
122                #    dans chaque segement, tri par pertinence
123                case 'tseg':
124                        $cl->SetSortMode ( SPH_SORT_TIME_SEGMENTS, 'date' );
125                        break;
126                case 'date':
127                        $cl->SetSortMode ( SPH_SORT_ATTR_DESC, 'dateu' );
128                        break;
129#               case 'expr2':
130#                       $cl->SetSelect("*, sum(lcs*user_weight) as w");
131#                       $cl->SetSortMode ( SPH_SORT_EXPR, "sum(lcs*user_weight)" );
132#                       break;
133                case 'points':  /* c'est le choix proposŽ aux visiteurs ! */
134                case 'expr1':
135                default:
136                        $cl->SetSortMode ( SPH_SORT_EXPR,
137                                "@weight / (100+SQRT(SQRT(".(time()+3600*24*365)."-dateu)))"
138                        );
139                        break;
140        }
141
142        // si aucune source n'est definie, on meurt
143        if (!defined('_SPHINX_SOURCE')) {
144                die ('Please define _SPHINX_SOURCE');
145        } else {
146                $sources = _SPHINX_SOURCE;
147        }
148
149        # agir enfonction de la selection demandee :
150        if ($selection == 'ecrire') {
151                # espace prive
152                $cl->SetLimits(0,500);
153        }
154        else if (function_exists($f = 'sphinx_selection_'.$selection)) {
155                // passage par parametre de $cl et $sources
156                // pour modification eventuelle par une fonction maison
157                $f($cl,$sources,$query);
158        }
159        else if (function_exists($f = 'sphinx_selection_default')) {
160                // passage par parametre de $cl et $sources
161                // pour modification eventuelle par une fonction maison
162                $f($cl,$sources,$query);
163        }
164
165        # recuperer les donnŽes
166        $cl->SetArrayResult ( true );
167
168        # analyser la query
169        $query = $recherche;
170
171        # lancer la query dans sphinx
172        $res = $cl->Query ( $query, $sources );
173
174        # loger une eventuelle erreur
175        if (strlen($cl->_error))
176                spip_log($cl->_error, 'sphinx');
177
178        # si ca ne donne rien au premier tour, on essaiera en relax
179        # sauf si la query est deja booleenne/complexe (l'utilisateur sait ce qu'il fait)
180        if (!isset($res['matches'])
181        AND !$selection
182        AND !preg_match('/[&|"~@]/', $query)
183        ) {
184                $GLOBALS['_SPHINX_RELAX'] = count(explode(" ",$query));
185                if (is_array($res['words'])) {
186                        foreach($res['words'] as $mot => $m) {
187                                $mot = mb_strtolower($mot, 'UTF-8');
188                                if (!$m['docs'])
189                                        $GLOBALS['_SPHINX_SUBQUERY'][] = '<del>'.$mot.'</del>';
190                                else
191                                        $GLOBALS['_SPHINX_SUBQUERY'][] = $mot;
192                        }
193                }
194        }
195        if ($GLOBALS['_SPHINX_RELAX']>1) {
196                # utiliser la syntaxe "mot1 mot2 mot3"/2
197                $query2 = '"'.$query.'"/'.($GLOBALS['_SPHINX_RELAX']-1);
198                $cl->SetLimits(0, 20);
199                $res = $cl->Query ( $query2, $sources );
200                if (!isset($res['matches']))
201                        $GLOBALS['_SPHINX_RELAX'] = 1;
202        }
203        if ($GLOBALS['_SPHINX_RELAX'] == 1) {
204                $cl->SetMatchMode( SPH_MATCH_ANY );
205                $cl->SetLimits(0, 20);
206                $res = $cl->Query ( $query, $sources );
207        }
208
209        if (isset($res['matches'])) {
210                $r = &$res['matches'];
211
212                # environ 3ms pour filtrer
213                if ($selection != ''
214                AND function_exists($f = 'sphinx_filtrer_resultats')) {
215                        $n = count($r);
216                        $f($r);
217                        $n -= count($r);
218                } else {
219                        $n = 0;
220                }
221
222                # creer les extraits avec surlignement des mots demandŽs
223                if ($selection != '') {
224                        sphinx_excerpts($r, $cl, $res['words'], $query, $sources);
225                }
226
227                if ($GLOBALS['_SPHINX_RELAX']) {
228                        foreach($r as &$match) {
229                                $match['relax'] = true;
230                                $match['mots'] = join(', ', (array) $GLOBALS['_SPHINX_SUBQUERY']);
231                        }
232                }
233
234                # remplir avant debut, avec du vide
235                for ($i=0; $i< $debut; $i++) {
236                        array_unshift($r, 0);
237                }
238
239                # remplir apres fin, avec du vide
240                $grand_total = min(1000, intval($res['total_found']));
241                for ($i=count($r); $i < $grand_total; $i++) {
242                        array_push($r, 0);
243                }
244
245                #echo '<li>total='.spip_timer('total').'</li>';
246
247
248                return $r;
249        }
250
251        #var_dump($res, $cl->_error);
252
253        return false;
254}
255
256
257/*
258 * ajouter les extraits (c'est couteux car il faut indexer 'full')
259 * @param $sources : au moins une des sources, n'importe laquelle
260 */
261function sphinx_excerpts(&$r, &$cl, $words=null, $query=null, $sources=null) {
262
263        // ne pas surligner si la query demande un champ (ex: @auteurs toto)
264        if (preg_match('/@/', $query))
265                return;
266
267        // ne pas surligner les mots de deux lettres sauf s'ils sont seuls ("UE")
268        if ($wds = $words) {
269                foreach ($wds as $wd=>$att) {
270                        if (strlen($wd)<=2        # mot trop court
271                        OR $att['hits'] > 100000  # stopword
272                        )
273                                unset($wds[$wd]);
274                }
275                if ($wds) $words = $wds;
276                $words = join(' ',array_keys($words));
277        }
278        if (!$words) return;
279
280        $textes = array();
281        foreach ($r as $k=>&$w) {
282                if (isset($w['attrs']['full'])) {
283                        $index[] = $k;
284                        $textes[] = $w['attrs']['full'];
285                }
286        }
287        if (!$textes) return;
288
289        $limit = defined('_SPHINX_COUPER_INTRO')
290                ? _SPHINX_COUPER_INTRO : 400;
291
292        $options = array(
293                'before_match'      => '<span class="spip_surligne">',
294                'after_match'       => '</span>',
295                'chunk_separator'   => ' (...) ',
296                'limit'             => $limit,
297                'around'            => 20,
298                'html_strip_mode'   => 'strip',
299        );
300
301        # BuildExcerpts demande une source unique
302        $source = array_pop(explode(',', $sources));
303
304        # construire les extraits
305        if ( $x = $cl->BuildExcerpts($textes, $source, $words, $options) ) {
306                foreach ($index as $n=>$i) {
307                        if (strpos($x[$n], $options['before_match']) !== false) {
308                                $r[$i]['attrs']['intro'] = '<intro>'
309                                        .typo(preg_replace('/[\[\]\{\}]|->.*\]/', ' ', $x[$n]))
310                                        .'</intro>';
311                        }
312                }
313        }
314}
315
316
317?>
Note: See TracBrowser for help on using the repository browser.