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

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

on fait encore plus de trucs avec sphinx

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