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

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

Un tableau des options dans la classe de requête + le critère de la boucle associé.

{option nom, valeur}

{option field_weights, "(title=10, content=5)"}

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