source: spip-zone/_plugins_/fulltext/inc/recherche_to_array.php @ 57905

Last change on this file since 57905 was 57905, checked in by cedric@…, 9 years ago

Pour les recherches avec jointures qui passent par une table de liaison faire un (LEFT JOIN INNER JOIN) plutot que (LEFT JOIN LEFT JOIN) cela accelere grandement la requete

File size: 9.0 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2011                                                *
7 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8 *                                                                         *
9 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11\***************************************************************************/
12
13
14if (!defined('_ECRIRE_INC_VERSION')) return;
15
16// Determiner les tables gerees via spip_xxx_liens
17function recherche_tables_liens() {
18        if ($GLOBALS['spip_version_base'] >= 16428)
19                return array('document', 'auteur', 'mot');
20        else
21        if ($GLOBALS['spip_version_base'] >= 12008)
22                return array('document');
23        else
24                return array();
25}
26
27// methodes sql
28function inc_recherche_to_array_dist($recherche, $options=null) {
29
30        $requete = array(
31        "SELECT"=>array(),
32        "FROM"=>array(),
33        "WHERE"=>array(),
34        "GROUPBY"=>array(),
35        "ORDERBY"=>array(),
36        "LIMIT"=>"",
37        "HAVING"=>array()
38        );
39
40        $options = array_merge(
41                array('table' => 'article',
42                ),
43                (array)$options
44        );
45        $table = $options['table'];
46        $serveur = $options['serveur'];
47
48        include_spip('inc/rechercher');
49
50        // s'il n'y a qu'un mot mais <= 3 lettres, il faut le chercher avec une *
51        // ex: RFC => RFC* ; car mysql fulltext n'indexe pas ces mots
52        if (preg_match('/^\w{1,3}$/', $recherche))
53                $recherche .= '*';
54
55        list($methode, $q, $preg) = expression_recherche($recherche, $options);
56
57        $l = liste_des_champs();
58        $champs = $l[$table];
59
60        $jointures = $options['jointures']
61                ? liste_des_jointures()
62                : array();
63
64        $_id_table = id_table_objet($table);
65        $requete['SELECT'][] = "t.".$_id_table;
66        $a = array();
67        // Recherche fulltext
68        foreach ($champs as $champ => $poids) {
69                if (is_array($champ)){
70                  spip_log("requetes imbriquees interdites");
71                } else {
72                        if (strpos($champ,".")===FALSE)
73                                $champ = "t.$champ";
74                        $requete['SELECT'][] = $champ;
75                        $a[] = $champ.' '.$methode.' '.$q;
76                }
77        }
78        if ($a) $requete['WHERE'][] = join(" OR ", $a);
79        $requete['FROM'][] = table_objet_sql($table).' AS t';
80
81        // FULLTEXT
82        $fulltext = false; # cette table est-elle fulltext?
83        if ($keys = fulltext_keys($table, 't', $serveur)) {
84                $fulltext = true;
85
86                $r = trim(preg_replace(',\s+,', ' ', $recherche));
87
88                // si espace, ajouter la meme chaine avec des guillemets pour ameliorer la pertinence
89                $pe = (strpos($r, ' ') AND strpos($r,'"')===false)
90                        ? sql_quote(trim("\"$r\""), $serveur) : '';
91
92                // On utilise la translitteration pour contourner le pb des bases
93                // declarees en iso-latin mais remplies d'utf8
94                if (($r2 = translitteration($r)) != $r)
95                        $r .= ' '.$r2;
96
97                $p = sql_quote(trim("$r"), $serveur);
98
99                // On va additionner toutes les cles FULLTEXT
100                // de la table
101                $score = array();
102                foreach ($keys as $name => $key) {
103                        $val = "MATCH($key) AGAINST ($p)";
104                        // Une chaine exacte rapporte plein de points
105                        if ($pe)
106                                $val .= "+ 2 * MATCH($key) AGAINST ($pe)";
107
108                        // Appliquer les ponderations donnees
109                        // quels sont les champs presents ?
110                        // par defaut le poids d'une cle est fonction decroissante
111                        // de son nombre d'elements
112                        // ainsi un FULLTEXT sur `titre` vaudra plus que `titre`,`chapo`
113                        $compteur = preg_match_all(',`.*`,U', $key, $ignore);
114                        $mult = intval(sqrt(1000/$compteur))/10;
115
116                        // (Compat ascendante) si un FULLTEXT porte sur un seul champ,
117                        // ET est nomme de la meme facon : `titre` (`titre`)
118                        // sa ponderation est eventuellement donnee par la table $liste
119                        if ($key == "t.`${name}`"
120                        AND $ponderation = $liste[$table][$name])
121                                $mult = $ponderation;
122
123                        // Appliquer le coefficient multiplicatif
124                        if ($mult != 1)
125                                $val = "($val) * $mult";
126
127                        // si symboles booleens les prendre en compte
128                        if ($boolean = preg_match(', [+-><~]|\* |".*?",', " $r "))
129                                $val = "MATCH($key) AGAINST ($p IN BOOLEAN MODE) * $mult";
130                        $score[] = $val;
131                }
132
133                // On ajoute la premiere cle FULLTEXT de chaque jointure
134                $from = array_pop($requete['FROM']);
135
136                if (is_array($jointures[$table]))
137                foreach(array_keys($jointures[$table]) as $jtable) {
138                        $i++;
139                        spip_log($pe,'recherche');
140                        if ($mkeys = fulltext_keys($jtable, 'obj'.$i, $serveur)) {
141                                $score[] = "SUM(MATCH(".implode($mkeys,',').") AGAINST ($p".($boolean ?' IN BOOLEAN MODE':'')."))";
142                                $_id_join = id_table_objet($jtable);
143                                $table_join = table_objet($jtable);
144                                $lesliens = recherche_tables_liens();
145                                if (in_array($jtable, $lesliens))
146                                        $from .= "
147                                        LEFT JOIN spip_${jtable}s_liens as lien$i ON lien$i.id_objet=t.$_id_table AND lien$i.objet='${table}'
148                                        INNER JOIN spip_${jtable}s as obj$i ON obj$i.$_id_join=lien$i.$_id_join
149                                        ";
150                                else
151                                        $from .= "
152                                        LEFT JOIN spip_${jtable}s_${table}s as lien$i ON lien$i.$_id_table=t.$_id_table
153                                        INNER JOIN spip_${table_join} AS obj$i ON lien$i.$_id_join=obj$i.$_id_join
154                                        ";
155                        }
156                }
157                $requete['FROM'][] = $from;
158                $score = join(' + ', $score).' AS score';
159                spip_log($score, 'recherche');
160
161                // si on define(_FULLTEXT_WHERE_$table,'date>"2000")
162                // cette contrainte est ajoutee ici:)
163                $requete['WHERE'] = array();
164                if (defined('_FULLTEXT_WHERE_'.$table))
165                        $requete['WHERE'][] = constant('_FULLTEXT_WHERE_'.$table);
166                else
167                        if (!test_espace_prive()
168                        AND !defined('_RECHERCHE_FULLTEXT_COMPLETE')
169                        AND in_array($table, array('article', 'rubrique', 'breve', 'forum', 'syndic_article')))
170                                $requete['WHERE'][] = "t.statut='publie'";
171
172                // nombre max de resultats renvoyes par l'API
173                define('_FULLTEXT_MAX_RESULTS', 500);
174
175                // preparer la requete
176                $requete['SELECT'] = array(
177                        "t.$_id_table"
178                        ,$score
179                );
180
181                // popularite ?
182                if (true # config : "prendre en compte la popularite
183                AND $table == 'article')
184                        $requete['SELECT'][] = "t.popularite";
185
186                # "t.date"
187                # "t.note"
188
189                #array_unshift($requete['FROM'], table_objet_sql($table)." AS t");
190                $requete['GROUPBY'] = array("t.$_id_table");
191                $requete['ORDERBY'] = "score DESC";
192                $requete['LIMIT'] = "0,"._FULLTEXT_MAX_RESULTS;
193                $requete['HAVING'] = '';
194
195                #var_dump($requete);
196                #spip_log($requete,'recherche');
197#                       exit;
198        }
199
200        $r = array();
201
202        $s = sql_select(
203                $requete['SELECT'], $requete['FROM'], $requete['WHERE'],
204                implode(" ",$requete['GROUPBY']),
205                $requete['ORDERBY'], $requete['LIMIT'],
206                $requete['HAVING'], $serveur
207        );
208
209        if (!$s) spip_log(mysql_errno().' '.mysql_error()."\n".$recherche, 'recherche');
210
211        while ($t = sql_fetch($s,$serveur)
212        AND (!isset($t['score']) OR $t['score']>0)) {
213                $id = intval($t[$_id_table]);
214
215                // FULLTEXT
216                if ($fulltext) {
217                        $pts = $t['score'];
218
219                        if (isset($t['popularite'])
220                        AND $mpop = $GLOBALS['meta']['popularite_max'])
221                                $pts *= (1+$t['popularite']/$mpop);
222
223                        $r[$id]['score'] = $pts;
224
225                } ELSE
226                // fin FULLTEXT
227
228                if ($options['toutvoir']
229                OR autoriser('voir', $table, $id)) {
230                        // indiquer les champs concernes
231                        $champs_vus = array();
232                        $score = 0;
233                        $matches = array();
234
235                        $vu = false;
236                        foreach ($champs as $champ => $poids) {
237                                $champ = explode('.',$champ);
238                                $champ = end($champ);
239                                if ($n = 
240                                        ($options['score'] || $options['matches'])
241                                        ? preg_match_all($preg, translitteration_rapide($t[$champ]), $regs, PREG_SET_ORDER)
242                                        : preg_match($preg, translitteration_rapide($t[$champ]))
243                                ) {
244                                        $vu = true;
245
246                                        if ($options['champs'])
247                                                $champs_vus[$champ] = $t[$champ];
248                                        if ($options['score'])
249                                                $score += $n * $poids;
250                                        if ($options['matches'])
251                                                $matches[$champ] = $regs;
252
253                                        if (!$options['champs']
254                                        AND !$options['score']
255                                        AND !$options['matches'])
256                                                break;
257                                }
258                        }
259
260                        if ($vu) {
261                                $r[$id] = array();
262                                if ($champs_vus)
263                                        $r[$id]['champs'] = $champs_vus;
264                                if ($score)
265                                        $r[$id]['score'] = $score;
266                                if ($matches)
267                                        $r[$id]['matches'] = $matches;
268                        }
269                }
270        }
271
272
273        // Gerer les donnees associees
274        if (!$fulltext
275        AND isset($jointures[$table])
276        AND $joints = recherche_en_base(
277                        $recherche,
278                        $jointures[$table],
279                        array_merge($options, array('jointures' => false))
280                )
281        ) {
282                foreach ($joints as $jtable => $jj) {
283                        $it = id_table_objet($table);
284                        $ij =  id_table_objet($jtable);
285                        $lesliens = recherche_tables_liens();
286                        if (in_array($jtable, $lesliens))
287                                $s = sql_select("id_objet as $it", "spip_${jtable}s_liens", array("objet='$table'",sql_in('id_'.${jtable}, array_keys($jj))), '','','','',$serveur);
288                        else
289                                $s = sql_select("$it,$ij", "spip_${jtable}s_${table}s", sql_in('id_'.${jtable}, array_keys($jj)), '','','','',$serveur);
290                        while ($t = sql_fetch($s)) {
291                                $id = $t[$it];
292                                $joint = $jj[$t[$ij]];
293                                if (!isset($r))
294                                        $r = array();
295                                if (!isset($r[$id]))
296                                        $r[$id] = array();
297                                if ($joint['score'])
298                                        $r[$id]['score'] += $joint['score'];
299                                if ($joint['champs'])
300                                foreach($joint['champs'] as $c => $val)
301                                        $r[$id]['champs'][$jtable.'.'.$c] = $val;
302                                if ($joint['matches'])
303                                foreach($joint['matches'] as $c => $val)
304                                        $r[$id]['matches'][$jtable.'.'.$c] = $val;
305                        }
306                }
307        }
308
309        return $r;
310}
311
312
313?>
Note: See TracBrowser for help on using the repository browser.