source: spip-zone/_plugins_/indexer/trunk/iterateur/sphinx.php @ 82793

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

retablir http://zone.spip.org/trac/spip-zone/changeset/82769 que j'ai écrasé en http://zone.spip.org/trac/spip-zone/changeset/82786 (sorry rastapopoulos)

File size: 25.6 KB
Line 
1<?php
2
3if (!defined('_ECRIRE_INC_VERSION')) return;
4
5/**
6 * Gestion de l'itérateur SPHINX
7 *
8 * @package SPIP\Indexer\Iterateur\Sphinx
9**/
10
11
12/**
13 * Créer une boucle sur un itérateur SPHINX
14 *
15 * Annonce au compilateur les "champs" disponibles,
16 *
17 * @param Boucle $b
18 *     Description de la boucle
19 * @return Boucle
20 *     Description de la boucle complétée des champs
21 */
22function iterateur_SPHINX_dist($b) {
23        $b->iterateur = 'SPHINX'; # designe la classe d'iterateur
24        $b->show = array(
25                'field' => array(
26                        '*' => 'ALL' // Champ joker *
27                )
28        );
29        return $b;
30}
31
32
33/**
34 * Iterateur SPHINX pour itérer sur des données
35 *
36 * La boucle SPHINX n'a toujours qu'un seul élément.
37 */
38class IterateurSPHINX implements Iterator {
39
40        /**
41         * Type de l'iterateur
42         * @var string
43         */
44        protected $type = 'SPHINX';
45
46        /**
47         * Commandes transmises à l'iterateur
48         * @var array
49         */
50        protected $command = array();
51
52        /**
53         * Infos de debug transmises à l'iterateur
54         * @var array
55         */
56        protected $info = array();
57
58        /**
59         * Instance de SphinxQL
60         * @var \Sphinx\SphinxQL\SphinxQL
61         */
62        protected $sphinxQL = null;
63
64        /**
65         * Instance de SphinxQL\QueryApi
66         * @var \Sphinx\SphinxQL\QueryAPi
67         */
68        protected $queryApi = null;
69
70        /**
71         * Résultats par la requête à Sphinx
72         * @var array
73         */
74        protected $result = array();
75
76        /**
77         * Cle courante
78         * @var null
79         */
80        protected $cle = null;
81
82        /**
83         * facettes
84         * @var array
85         */
86        protected $facet = array();
87
88        /**
89         * index(es) scannés
90         * @var array
91         */
92        protected $index = array();
93
94        /**
95         * Valeur courante
96         * @var null
97         */
98        protected $valeur = null;
99
100        /**
101         * Constructeur
102         *
103         * @param  $command
104         * @param array $info
105         */
106        public function __construct($command, $info=array()) {
107
108                $this->command = $command + array(
109                        'index'             => array(),
110                        'selection'         => array(),
111                        'recherche'         => array(),
112                        'orderby'           => array(),
113                        'group'             => array(),
114                        'snippet'           => array(),
115                        'facet'             => array(),
116                        'filter'            => array(),
117                        'filters_mono'       => array(),
118                        'filters_multijson'  => array(),
119                );
120
121
122                $this->info = $info;
123
124                include_spip('inc/indexer');
125
126                $this->sphinxQL  = new \Sphinx\SphinxQL\SphinxQL(SPHINX_SERVER_HOST, SPHINX_SERVER_PORT);
127                $this->queryApi  = new \Sphinx\SphinxQL\QueryApi();
128
129                $this->setIndex($this->command['index']);
130                $this->setSelection($this->command['selection']);
131                $this->setMatch($this->command['recherche']);
132                $this->setOrderBy($this->command['orderby']);
133                $this->setGroupBy($this->command['group']); // groupby interfère avec spip :/
134                $this->setFacet($this->command['facet']);
135
136                $this->setFilter($this->command['filter']);
137                $this->setFiltersMono($this->command['filters_mono']);
138                $this->setFiltersMultiJson($this->command['filters_multijson']);
139
140                $this->setSnippet($this->command);
141
142
143                $this->setPagination($this->command['pagination']);
144
145                $this->runQuery();
146
147        }
148
149
150        /**
151         * Sauvegarde des données pour utilisation ultérieure
152         * dans les squelettes via les balises `#SPHINX_xx`
153         * où xx est la clé sauvegardée.
154         *
155         * @param string $cle
156         * @param mixed $val
157         * @return void
158        **/
159        private function save($cle, $val) {
160                if (!isset($GLOBALS['SphinxSave'])) {
161                        $GLOBALS['SphinxSave'] = array();
162                }
163                // identifiant de la boucle
164                $id = $this->command['id'];
165                if (!isset($GLOBALS['SphinxSave'][$id])) {
166                        $GLOBALS['SphinxSave'][$id] = array();
167                }
168                $GLOBALS['SphinxSave'][$id][$cle] = $val;
169        }
170
171        /**
172         * Sauvegarde toutes les données pour utilisation ultérieure
173         *
174         * @param array $data
175         * @return void
176         */
177        private function saveAll($data) {
178                foreach ($data as $cle => $val) {
179                        $this->save($cle, $val);
180                }
181        }
182
183        /*
184         * Récupérer la forme exacte du mot à partir de
185         * la version indexée ; utilise snippet(query, racine)
186         */
187        public function keyword2word($keyword, $q) {
188                $u = $this->sphinxQL->allfetsel(
189                "SELECT SNIPPET("._q($q).","._q($keyword).") AS m "
190                . "FROM ". join(',', $this->index) ." LIMIT 1"
191                );
192                if (!$mot = supprimer_tags(extraire_balise($u['query']['docs'][0]['m'], 'b')))
193                        $mot = $keyword;
194                return $mot;
195        }
196
197        /**
198         * Exécute la requête
199         *
200         * Exécute la requête, sauvegarde des données, retravaille
201         * les résultats pour que la pagination fonctionne.
202         *
203         * @param
204         * @return
205        **/
206        public function runQuery() {
207                $q = $this->queryApi->getMatch();
208               
209                // on sait deja que cette requete necessite une correction ?
210                if (isset($GLOBALS['sphinxReplace'][$q])) {
211                        $this->queryApi->match($GLOBALS['sphinxReplace'][$q]);
212                        $this->save('message', $GLOBALS['sphinxReplaceMessage'][$q]);
213                }
214
215                $query  = $this->queryApi->get();
216                $this->save('query', $query);
217
218                $result = $this->sphinxQL->allfetsel($query);
219
220                // erreur de syntaxe ? correction de la requete
221                if (isset($result['query']['meta']['error'])) {
222                        $q = $this->queryApi->getMatch();
223                        $GLOBALS['sphinxReplace'][$q] = trim(preg_replace('/\W+/u', ' ', $q));
224                        $this->queryApi->match($GLOBALS['sphinxReplace'][$q]);
225                        $query  = $this->queryApi->get();
226                        $result = $this->sphinxQL->allfetsel($query);
227                        $message = _L('transformation de la requête en « ').htmlspecialchars($GLOBALS['sphinxReplace'][$q])." »";
228                        $GLOBALS['sphinxReplaceMessage'][$q] = $message;
229                        $this->save('message', $message);
230                }
231
232                if (!$result) {
233                        return false;
234                }
235
236                // resultat vide et plusieurs mots dont certains ont 0 hit ?
237                if (is_array($result['query']['docs'])
238                AND count($result['query']['docs']) == 0
239                AND !preg_match('/["\/&|)(]/u', $q)
240                ) {
241                        $q2 = $msg = array();
242                        if (isset($result['query']['meta']['keywords'])){
243                                foreach($result['query']['meta']['keywords'] as $w) {
244                                        $mot = $this->keyword2word($w['keyword'], $q);
245                                        if($w['docs'] == 0) {
246                                                $msg[] = "<del>".htmlspecialchars($mot)."</del>";
247                                        } else {
248                                                $msg[] = htmlspecialchars($mot);
249                                                $q2[] = $mot;
250                                        }
251                                }
252                        }
253
254                        if (count($q2) >0
255                        AND count($q2) < count($result['query']['meta']['keywords'])) {
256                                $q2 = trim(join(' ',$q2));
257                                $GLOBALS['sphinxReplace'][$q] = trim(preg_replace('/\W+/u', ' ', $q2));
258                                $this->queryApi->match($GLOBALS['sphinxReplace'][$q]);
259                                $query  = $this->queryApi->get();
260                                $result = $this->sphinxQL->allfetsel($query);
261                                $GLOBALS['sphinxReplace'][$q] = $q2;
262                                $GLOBALS['sphinxReplaceMessage'][$q] = $message = _L('Résultats pour : ').join(' ',$msg);
263                                $this->save('message', $message);
264                        }
265                }
266
267
268                // decaler les docs en fonction de la pagination demandee
269                if (is_array($result['query']['docs'])
270                AND $pagination = $this->getPaginationLimit()) { 
271
272                        list($debut) = array_map('intval', $pagination); 
273
274                        $result['query']['docs'] = array_pad($result['query']['docs'], - count($result['query']['docs']) - $debut, null);
275                        $result['query']['docs'] = array_pad($result['query']['docs'], $result['query']['meta']['total'], null);
276                }
277
278                // remettre les alias sur les facettes :
279                // {facet truc, FORMULE()} cree la facette 'truc'
280                $facets = array();
281                foreach ($this->facet as $f) {
282                        $facets[$f['alias']] = array_shift($result['query']['facets']);
283                }
284                $result['query']['facets'] = $facets;
285
286
287                $this->result = $result['query'];
288                unset($result['query']['docs']);
289                $this->saveAll($result['query']);
290
291                return true;
292        }
293
294
295        public function quote($m) {
296                return $this->queryApi->quote($m);
297        }
298
299
300        /**
301         * Définir la liste des index interrogés (FROM de la requête)
302         *
303         * Par défaut on utilise l'index déclaré dans la conf
304         *
305         * @param array $index Liste des index
306         * @return bool True si au moins un index est ajouté, false sinon
307        **/
308        public function setIndex($index) {
309                if (!is_array($index)) $index = array($index);
310                $index = array_filter($index);
311                if (!$index) {
312                        $index[] = SPHINX_DEFAULT_INDEX;
313                }
314                foreach ($index as $i) {
315                        $this->queryApi->from($i);
316                }
317                $this->index = $index;
318                return true;
319        }
320
321
322
323        /**
324         * Définir la liste des champs récupérés (SELECT de la requête)
325         *
326         * Par défaut, en absence de précisions, on prend tous les champs
327         *
328         * @param array $select Liste des index
329         * @return bool True si au moins un index est ajouté, false sinon
330        **/
331        public function setSelection($select) {
332                if (!is_array($select)) $select = array($select);
333                $select = array_filter($select);
334                // si aucune selection demandée, on prend tout !
335                if (!$select) {
336                        $select[] = '*';
337                }
338                foreach ($select as $s) {
339                        $this->queryApi->select($s);
340                }
341                return true;
342        }
343
344
345
346        /**
347         * Définir la recherche fulltext
348         *
349         * @param array $index Liste des index
350         * @return bool True si au moins un index est ajouté, false sinon
351        **/
352        public function setMatch($match) {
353                if (!is_array($match)) $match = array($match);
354                $match = array_filter($match);
355                if (!$match) {
356                        return false;
357                }
358                $match = implode(' ',$match);
359                $this->queryApi
360                        ->select('WEIGHT() AS score')
361                        ->match( $match );
362                return true;
363        }
364
365
366        public function setOrderby($orderby) {
367                if (!is_array($orderby)) $orderby = array($orderby);
368                $orderby = array_filter($orderby);
369                if (!$orderby) {
370                        return false;
371                }
372                foreach ($orderby as $order) {
373                        // juste ASC ou DESC sans le champ… passer le chemin…
374                        if (in_array(trim($order), array('ASC', 'DESC'))) {
375                                continue;
376                        }
377                        if (!preg_match('/(ASC|DESC)$/i', $order)) {
378                                $order .= ' ASC';
379                        }
380                        $this->queryApi->orderby($order);
381                }
382                return true;
383        }
384
385        public function setGroupby($groupby) {
386                if (!is_array($groupby)) $groupby = array($groupby);
387                $groupby = array_filter($groupby);
388                if (!$groupby) {
389                        return false;
390                }
391                foreach ($groupby as $group) {
392                        $this->queryApi->groupby($group);
393                }
394                return true;
395        }
396
397
398        /**
399        * Affecte une limite à la requête Sphinx (et sauve ses bornes)
400        *
401        * @param int Début
402        * @param int Nombre de résultats
403        **/ 
404        public function setPaginationLimit($debut, $nombre) { 
405                $this->pagination_limit = array($debut, $nombre); 
406                $this->queryApi->limit("$debut,$nombre"); 
407        } 
408
409        /**
410        * Retourne les limites de pagination précédemment sauvées
411        *
412        * @param int Début
413        * @param int Nombre de résultats
414        **/ 
415        public function getPaginationLimit() { 
416                return $this->pagination_limit; 
417                # return explode(',', $this->queryApi->getLimit());
418        }
419
420        /**
421         * Définir la pagination
422         *
423         * @param array $pagination
424         * @return bool True si une pagination est demandee
425        **/
426        public function setPagination($pagination) {
427                # {pagination 20}
428                if (is_array($pagination) and $pagination) {
429                        $debut = intval($pagination[0]);
430                        $nombre = 20;
431                        if (isset($pagination[1])) {
432                                $nombre = intval($pagination[1]);
433                        }
434                        $this->setPaginationLimit($debut, $nombre); 
435                        return true;
436                }
437        }
438
439        /**
440         * Définir le snippet
441         */
442        public function setSnippet($command) {
443                $snippet = array_filter($command['snippet']);
444                // si aucune selection demandée, on prend tout !
445                if (!$snippet) {
446                        return $this->setSnippetAuto($command);
447                } else {
448                        $ok = true;
449                        foreach ($snippet as $s) {
450                                if (!is_array($s)) continue;
451                                if (!$s['phrase']) {
452                                        $s['phrase'] = $this->getSnippetAutoPhrase($command);
453                                }
454                                $ok &= $this->setOneSnippet($s);
455                        }
456                }
457                return $ok;
458        }
459
460        /**
461         * Définir 1 snippet depuis sa description
462         *
463         * @param array $desc
464         * @return bool
465        **/
466        public function setOneSnippet($desc) {
467
468                $desc += array(
469                        'champ'  => 'content',
470                        'phrase' => '',
471                        'limit'  => 200,
472                        'as'     => 'snippet'
473                );
474                if (!$desc['phrase']) {
475                        return false;
476                }
477
478                $this->queryApi->addSnippetWords( $desc['phrase'] );
479                $desc['phrase'] = $this->queryApi->getSnippetWords();
480
481                if (!$desc['phrase'] OR !$desc['champ']) {
482                        return false;
483                }
484                $this->queryApi->select("SNIPPET($desc[champ], " . $this->quote($desc['phrase']) . ", 'limit=$desc[limit],html_strip_mode=strip') AS $desc[as]");
485                return true;
486        }
487
488        /**
489         * Définir automatiquement un snippet dans le champ 'snippet'
490         * à partir de la recherche et des filtres
491         */
492        public function setSnippetAuto($command) {
493                $phrase = $this->getSnippetAutoPhrase($command);
494                if (!$phrase) return false;
495                return $this->setOneSnippet(array('phrase' => $phrase));
496        }
497
498        /**
499         * Extrait de la commande de boucle les phrases pertinentes cherchées
500         *
501         * - Cherche la phrase de recherche
502         *
503         * @param array $command Commande de la boucle Sphinx
504         * @return string phrases séparées par espace.
505        **/
506        public function getSnippetAutoPhrase($command) {
507                $phrase = '';
508
509                // mots de la recherche
510                $recherche = $command['recherche'];
511                if (!is_array($recherche)) $recherche = array($recherche);
512                $recherche = array_filter($recherche);
513                $phrase .= implode(' ', $recherche);
514
515                return $phrase;
516        }
517
518
519        /**
520         * Définit les commandes FACET
521         *
522         * @param array $facets Tableau des facettes demandées
523         * @return bool
524        **/
525        public function setFacet($facets) {
526                $facets = array_filter($facets);
527                if (!$facets) {
528                        return false;
529                }
530                $ok = true;
531                foreach ($facets as $facet) {
532                        if (!isset($facet['alias']) OR !isset($facet['query'])) {
533                                $ok = false;
534                                continue;
535                        }
536                        $alias = trim($facet['alias']);
537                        $query = trim($facet['query']);
538                        if (!$alias OR !$query) {
539                                $ok =  false;
540                                continue;
541                        }
542                        $this->facet[] = array('alias' => $alias, 'query' => $query);
543                        $this->queryApi->facet($query);
544                }
545                return $ok;
546        }
547
548
549
550        /**
551         * Définit des filtres
552         *
553         * @param array $facets Tableau des filtres demandées
554         * @return bool
555        **/
556        public function setFilter($filters) {
557                // compter le nombre de filtres ajoutés à la requête.
558                static $nb = 0;
559
560                $facets = array_filter($filters);
561                if (!$filters) {
562                        return false;
563                }
564                foreach ($filters as $filter) {
565                        // ignorer toutes les données vides
566                        if (!is_array($filter) OR !isset($filter['valeur']) OR !$valeur = $filter['valeur']) {
567                                continue;
568                        }
569                        if (is_array($valeur)) {
570                                $valeurs = $valeur;
571                                $valeur = 'Array !';
572                        } else {
573                                $valeur = trim($valeur);
574                                $valeurs = array($valeur);
575                        }
576                        $valeurs = array_unique(array_filter($valeurs));
577                        if (!$valeurs) {
578                                continue;
579                        }
580
581                        $filter += array(
582                                'select_oui'  => '',
583                                'select_null' => '',
584                        );
585
586                        // préparer les données
587                        $aucun = ($valeur == '-'); // si aucun demandé
588                        $valeur = $this->quote($valeur);
589                        $valeurs = array_map(array($this, 'quote'), $valeurs);
590                        $valeurs = implode(', ', $valeurs);
591
592                        if (($aucun == '-') and $filter['select_null']) {
593                                $f = $filter['select_null'];
594                        } elseif ($filter['select_oui']) {
595                                $f = $filter['select_oui'];
596                        }
597
598                        // remplacer d'abord le pluriel !
599                        $f = str_replace(array('@valeurs', '@valeur'), array($valeurs, $valeur), $f);
600                        $this->queryApi->select("($f) AS f$nb");
601                        $this->queryApi->where("f$nb = 1");
602                        $nb++;
603                }
604        }
605       
606        function setFiltersMono($filters){
607                $filters = array_filter($filters);
608                if (!$filters) {
609                        return false;
610                }
611               
612                $ok = true;
613                foreach ($filters as $filter){
614                        $ok &= $this->queryApi->setApiFilterMono($filter);
615                }
616               
617                return $ok;
618        }
619       
620        function setFiltersMultiJson($filters){
621                $filters = array_filter($filters);
622                if (!$filters) {
623                        return false;
624                }
625               
626                $ok = true;
627                foreach ($filters as $filter){
628                        $ok &= $this->queryApi->setApiFilterMultiJson($filter);
629                }
630               
631                return $ok;
632        }
633       
634        /**
635         * Revenir au depart
636         * @return void
637         */
638        public function rewind() {
639                if (!is_array($this->result['docs'])) return false;
640                reset($this->result['docs']);
641                list($this->cle, $this->valeur) = each($this->result['docs']);
642        }
643
644        /**
645         * L'iterateur est-il encore valide ?
646         * @return bool
647         */
648        public function valid(){
649                return !is_null($this->cle);
650        }
651
652        /**
653         * Retourner la valeur
654         * @return null
655         */
656        public function current() {
657                return $this->valeur;
658        }
659
660        /**
661         * Retourner la cle
662         * @return null
663         */
664        public function key() {
665                return $this->cle;
666        }
667
668        /**
669         * Passer a la valeur suivante
670         * @return void
671         */
672        public function next(){
673                if ($this->valid()) {
674                        list($this->cle, $this->valeur) = each($this->result['docs']);
675                }
676        }
677
678        /**
679         * Compter le nombre total de resultats
680         * @return int
681         */
682        public function count() {
683                if (is_null($this->total))
684                        $this->total = count($this->result['docs']);
685          return $this->total;
686        }
687
688}
689
690
691/**
692 * Transmettre la source (l'index sphinx) désirée
693 * @param string $idb
694 * @param object $boucles
695 * @param object $crit
696 */
697function critere_SPHINX_index_dist($idb, &$boucles, $crit) {
698        $boucle = &$boucles[$idb];
699        // critere unique
700        $boucle->hash .= "\n\t" . '$command[\'index\'] = array();';
701
702        foreach ($crit->param as $param){
703                $boucle->hash .= "\n\t" . '$command[\'index\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).';';
704        }
705}
706
707/**
708 * Transmettre la recherche (le match fulltext) désirée
709 * @param string $idb
710 * @param object $boucles
711 * @param object $crit
712 */
713function critere_SPHINX_recherche_dist($idb, &$boucles, $crit) {
714        $boucle = &$boucles[$idb];
715        // critere unique
716        $boucle->hash .= "\n\t" . '$command[\'recherche\'] = array();';
717
718        foreach ($crit->param as $param){
719                $boucle->hash .= "\n\t" . '$command[\'recherche\'][] = '.calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent).';';
720        }
721}
722
723
724/**
725 * Indiquer les sélections de la requête
726 *
727 * @param string $idb
728 * @param object $boucles
729 * @param object $crit
730 */
731function critere_SPHINX_select_dist($idb, &$boucles, $crit) {
732        $boucle = &$boucles[$idb];
733        // critere multiple
734        $boucle->hash .= "\n\tif (!isset(\$select_init)) { \$command['selection'] = array(); \$select_init = true; }\n";
735
736        foreach ($crit->param as $param){
737                $boucle->hash .= "\t\$command['selection'][] = "
738                                . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ";\n";
739        }
740}
741
742
743/**
744 * Indiquer les group by de la requête
745 *
746 * @param string $idb
747 * @param object $boucles
748 * @param object $crit
749 */
750function critere_SPHINX_groupby_dist($idb, &$boucles, $crit) {
751        $boucle = &$boucles[$idb];
752        // critere multiple
753        $boucle->hash .= "\n\tif (!isset(\$group_init)) { \$command['group'] = array(); \$group_init = true; }\n";
754
755        foreach ($crit->param as $param){
756                $boucle->hash .= "\t\$command['group'][] = "
757                                . calculer_liste($param, array(), $boucles, $boucles[$idb]->id_parent) . ";\n";
758        }
759}
760
761
762/**
763 * Indiquer les snippets de la requête
764 *
765 * @param string $idb
766 * @param object $boucles
767 * @param object $crit
768 */
769function critere_SPHINX_snippet_dist($idb, &$boucles, $crit) {
770        $boucle = &$boucles[$idb];
771        // critere multiple
772        $boucle->hash .= "\n\tif (!isset(\$snippet_init)) { \$command['snippet'] = array(); \$snippet_init = true; }\n";
773
774        $boucle->hash .= "\t\$command['snippet'][] = [\n"
775                . (isset($crit->param[0]) ? "\t\t'champ'  => ". calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
776                . (isset($crit->param[1]) ? "\t\t'phrase' => ". calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
777                . (isset($crit->param[2]) ? "\t\t'limit'  => ". calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
778                . (isset($crit->param[3]) ? "\t\t'as'     => ". calculer_liste($crit->param[3], array(), $boucles, $boucles[$idb]->id_parent) . "\n"  : '')
779                . "\t];\n";
780}
781
782
783
784/**
785 * Indiquer les facets de la requête
786 *
787 * @param string $idb
788 * @param object $boucles
789 * @param object $crit
790 */
791function critere_SPHINX_facet_dist($idb, &$boucles, $crit) {
792        $boucle = &$boucles[$idb];
793        // critere multiple
794        $boucle->hash .= "\n\tif (!isset(\$facet_init)) { \$command['facet'] = array(); \$facet_init = true; }\n";
795
796        $boucle->hash .= "\t\$command['facet'][] = array(\n"
797                . (isset($crit->param[0]) ? "\t\t'alias'  => ". calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
798                . (isset($crit->param[1]) ? "\t\t'query' => ". calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
799                . "\t);\n";
800}
801
802/**
803 * Indiquer les filtres de la requête
804 *
805 * @param string $idb
806 * @param object $boucles
807 * @param object $crit
808 */
809function critere_SPHINX_filter_dist($idb, &$boucles, $crit) {
810        $boucle = &$boucles[$idb];
811        // critere multiple
812        $boucle->hash .= "\n\tif (!isset(\$sfilter_init)) { \$command['filter'] = array(); \$sfilter_init = true; }\n";
813
814        $boucle->hash .= "\t\$command['filter'][] = array(\n"
815                . (isset($crit->param[0]) ? "\t\t'valeur'      => ". calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
816                . (isset($crit->param[1]) ? "\t\t'select_oui'  => ". calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
817                . (isset($crit->param[2]) ? "\t\t'select_null' => ". calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent) . ",\n" : '')
818                . "\t);\n";
819}
820
821/**
822 * Indiquer les filtres mono-valués de la requête
823 *
824 * @param string $idb
825 * @param object $boucles
826 * @param object $crit
827 */
828function critere_SPHINX_filtermono_dist($idb, &$boucles, $crit) {
829        $boucle = &$boucles[$idb];
830       
831        if (isset($crit->param[0])) {
832                $test = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
833        }
834        if (isset($crit->param[1])) {
835                $field = calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent);
836        }
837        if (isset($crit->param[2])) {
838                $values = calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent);
839        }
840        if (isset($crit->param[3])) {
841                $comparison = calculer_liste($crit->param[3], array(), $boucles, $boucles[$idb]->id_parent);
842        }
843       
844        // Test
845        $boucle->hash .= "\n\tif ($test) {\n";
846       
847        // Critere multiple
848        $boucle->hash .= "\t\tif (!isset(\$filters_mono_init)) { \$command['filters_mono'] = array(); \$filters_mono_init = true; }\n";
849
850        $boucle->hash .= "\t\t\$command['filters_mono'][] = array(\n"
851                . (isset($crit->param[1]) ? "\t\t\t'field'       => $field,\n" : '')
852                . (isset($crit->param[2]) ? "\t\t\t'values'      => $values,\n" : '')
853                . (isset($crit->param[3]) ? "\t\t\t'comparison'  => $comparison,\n" : '')
854                . "\t\t);\n";
855       
856        // Fin de test
857        $boucle->hash .= "\t}\n";
858}
859
860/**
861 * Indiquer les filtres multi-valués JSON de la requête
862 *
863 * @param string $idb
864 * @param object $boucles
865 * @param object $crit
866 */
867function critere_SPHINX_filtermultijson_dist($idb, &$boucles, $crit) {
868        $boucle = &$boucles[$idb];
869       
870        if (isset($crit->param[0])) {
871                $test = calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent);
872        }
873        if (isset($crit->param[1])) {
874                $field = calculer_liste($crit->param[1], array(), $boucles, $boucles[$idb]->id_parent);
875        }
876        if (isset($crit->param[2])) {
877                $values = calculer_liste($crit->param[2], array(), $boucles, $boucles[$idb]->id_parent);
878        }
879       
880        // Test
881        $boucle->hash .= "\n\tif ($test) {\n";
882       
883        // Critere multiple
884        $boucle->hash .= "\t\tif (!isset(\$filters_multijson_init)) { \$command['filters_multijson'] = array(); \$filters_multijson_init = true; }\n";
885
886        $boucle->hash .= "\t\t\$command['filters_multijson'][] = array(\n"
887                . (isset($crit->param[1]) ? "\t\t\t'field'       => $field,\n" : '')
888                . (isset($crit->param[2]) ? "\t\t\t'values'      => $values,\n" : '')
889                . "\t\t);\n";
890       
891        // Fin de test
892        $boucle->hash .= "\t}\n";
893}
894
895/**
896 * Pagination
897 *
898 * @param string $idb
899 * @param object $boucles
900 * @param object $crit
901 */
902function critere_SPHINX_pagination_dist($idb, &$boucles, $crit) {
903        $boucle = &$boucles[$idb];
904        // critere unique
905        $boucle->hash .=        "\t\$command['pagination'] = array("
906                . "intval(@\$Pile[0]['debut".$idb."']),"
907                . (isset($crit->param[0]) ? calculer_liste($crit->param[0], array(), $boucles, $boucles[$idb]->id_parent) : '0')
908                . ");\n";
909
910        // appliquer enfin le critere {pagination} normal
911        return critere_pagination_dist($idb, $boucles, $crit);
912}
913
914
915/**
916 * Tris `{par x}`
917 *
918 * @param string $idb
919 * @param object $boucles
920 * @param object $crit
921 */
922function critere_SPHINX_par_dist($idb, &$boucles, $crit) {
923        return critere_SPHINX_parinverse($idb, $boucles, $crit);
924}
925
926/**
927 * Tris `{inverse}`
928 *
929 * @param string $idb
930 * @param object $boucles
931 * @param object $crit
932 */
933function critere_SPHINX_inverse_dist($idb, &$boucles, $crit) {
934        $boucle = &$boucles[$idb];
935        if ($crit->not) {
936                critere_SPHINX_parinverse($idb, $boucles, $crit);
937        } else {
938                // sinon idem parent.
939                critere_inverse_dist($idb, $boucles, $crit);
940        }
941}
942
943/**
944 * Gestion des critères `{par}` et `{inverse}`
945 *
946 * @note
947 *     Sphinx doit toujours avoir le sens de tri (ASC ou DESC).
948 *
949 *     Version simplifié du critère natif de SPIP, avec une permission
950 *     pour les champs de type json `properties.truc`
951 *
952 * @param string $idb
953 * @param object $boucles
954 * @param object $crit
955**/
956function critere_SPHINX_parinverse($idb, $boucles, $crit, $sens = '') {
957        $boucle = &$boucles[$idb];
958        if ($crit->not) {
959                $sens = $sens ? "" : " . ' DESC'";
960        }
961
962        foreach ($crit->param as $tri){
963                $order = "";
964
965                // tris specifies dynamiquement
966                if ($tri[0]->type!='texte'){
967                        // calculer le order dynamique qui verifie les champs
968                        $order = calculer_critere_arg_dynamique($idb, $boucles, $tri, $sens);
969                } else {
970                        $par = array_shift($tri);
971                        $par = $par->texte;
972                        $order = "'$par'";
973                }
974
975
976                $t = $order.$sens;
977                $boucle->order[] = $t;
978        }
979}
980
981
982/**
983 * Récupère pour une balise `#SPHINX_QQC` la valeur de 'qqc'
984 * dans les meta données associées à la requête
985 *
986 * - `#SPHINX_QUERY`
987 * - `#SPHINX_META`
988 * - `#SPHINX_FACETS`
989 *
990 * @param Champ $p
991 * @return Champ
992**/
993function balise_SPHINX__dist($p){
994        $champ = $p->nom_champ;
995        if ($champ == 'SPHINX_') {
996                $msg = _T('zbug_balise_sans_argument', array('balise' => ' SPHINX_'));
997                erreur_squelette($msg, $p);
998                $p->interdire_scripts = true;
999                return $p;
1000        };
1001        $champ = substr($champ, 7);
1002        return calculer_balise_SPHINX_CHAMP($p, $champ);
1003}
1004
1005
1006
1007
1008/**
1009 * Récupère pour une balise `#SPHINX_QQC` la valeur de 'qqc'
1010 * dans les meta données associées à la requête.
1011 *
1012 * @param Champ $p
1013 * @param string $champ
1014 * @return Champ
1015**/
1016function calculer_balise_SPHINX_CHAMP($p, $champ) {
1017        $b = $p->nom_boucle ? $p->nom_boucle : $p->descr['id_mere'];
1018        if ($b === '' || !isset($p->boucles[$b])) {
1019                $msg = array('zbug_champ_hors_boucle', array('champ' => '#SPHINX_' . $champ));
1020                erreur_squelette($msg, $p);
1021                $p->interdire_scripts = true;
1022                return $p;
1023        }
1024
1025        $champ = strtolower($champ);
1026        $p->code = '(isset($GLOBALS["SphinxSave"]["'.$b.'"]["'.$champ.'"]) ? $GLOBALS["SphinxSave"]["'.$b.'"]["'.$champ.'"] : "")';
1027
1028        $p->interdire_scripts = false;
1029        return $p;
1030}
Note: See TracBrowser for help on using the repository browser.