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

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

bugfix sur le scoring fulltext :

  • la liste des champs=>poids n'était pas prise en compte par la faute d'un mauvais nommage de variable (utilisation sur l'index titre de fulltext)

Amelioration du scoring :

  • le score calculé par champs/poids classique SPIP est plus précis car compte les occurences dans un champs, on peut donc le mixer avec le score fulltext pour améliorer les résultats (mais plus couteux en terme de calcul) : avec

define('_FULLTEXT_FIELD_SCORE',true) les 2 scores sont ajoutés

File size: 9.7 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        $liste = liste_des_champs();
58        $champs = $liste[$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                $full_text_where = array();
103                foreach ($keys as $name => $key) {
104                        $val = "MATCH($key) AGAINST ($p)";
105                        $val_where = $val;
106                        // Une chaine exacte rapporte plein de points
107                        if ($pe)
108                                $val .= "+ 2 * MATCH($key) AGAINST ($pe)";
109
110                        // Appliquer les ponderations donnees
111                        // quels sont les champs presents ?
112                        // par defaut le poids d'une cle est fonction decroissante
113                        // de son nombre d'elements
114                        // ainsi un FULLTEXT sur `titre` vaudra plus que `titre`,`chapo`
115                        $compteur = preg_match_all(',`.*`,U', $key, $ignore);
116                        $mult = intval(sqrt(1000/$compteur))/10;
117
118                        // (Compat ascendante) si un FULLTEXT porte sur un seul champ,
119                        // ET est nomme de la meme facon : `titre` (`titre`)
120                        // sa ponderation est eventuellement donnee par la table $liste
121                        if ($key == "t.`${name}`"
122                        AND $ponderation = $liste[$table][$name])
123                                $mult = $ponderation;
124
125                        // Appliquer le coefficient multiplicatif
126                        if ($mult != 1)
127                                $val = "($val) * $mult";
128
129                        // si symboles booleens les prendre en compte
130                        if ($boolean = preg_match(', [+-><~]|\* |".*?",', " $r ")) {
131                                $val = "MATCH($key) AGAINST ($p IN BOOLEAN MODE) * $mult";
132                                $val_where = "MATCH($key) AGAINST ($p IN BOOLEAN MODE)";
133                        }
134                        $full_text_where[] = $val_where;
135                        $score[] = $val;
136                }
137                $full_text_where = array("(".implode(") OR (",$full_text_where).")");
138
139                // On ajoute la premiere cle FULLTEXT de chaque jointure
140                $from = array_pop($requete['FROM']);
141
142                if (is_array($jointures[$table])){
143                        $i = 0;
144                        foreach(array_keys($jointures[$table]) as $jtable) {
145                                $i++;
146                                spip_log($pe,'recherche');
147                                if ($mkeys = fulltext_keys($jtable, 'obj'.$i, $serveur)) {
148                                        $score[] = "IF(SUM(o".$i.".score) IS NULL,0,SUM(o".$i.".score))";
149                                        $_id_join = id_table_objet($jtable);
150                                        $table_join = table_objet($jtable);
151                                        $lesliens = recherche_tables_liens();
152
153                                        $subscore = "MATCH(".implode($mkeys,',').") AGAINST ($p".($boolean ? ' IN BOOLEAN MODE':'').")";
154
155                                        if (in_array($jtable, $lesliens))
156                                                $from .= "
157                                                LEFT JOIN (
158                                                 SELECT lien$i.id_objet,$subscore AS score
159                                                 FROM spip_${jtable}s_liens as lien$i
160                                                 JOIN spip_${jtable}s as obj$i ON obj$i.$_id_join=lien$i.$_id_join
161                                                 AND lien$i.objet='${table}'
162                                                 WHERE $subscore > 0
163                                                 ORDER BY score DESC LIMIT 100
164                                                 ) AS o$i ON o$i.id_objet=t.$_id_table
165                                                ";
166                                        else
167                                                $from .= "
168                                                LEFT JOIN (
169                                                 SELECT lien$i.$_id_table,$subscore AS score
170                                                 FROM spip_${jtable}s_${table}s as lien$i
171                                                 JOIN spip_${table_join} AS obj$i ON lien$i.$_id_join=obj$i.$_id_join
172                                                 WHERE $subscore > 0
173                                                 ORDER BY score DESC LIMIT 100
174                                                 ) AS o$i ON o$i.$_id_table=t.$_id_table
175                                                ";
176                                }
177                        }
178                }
179                $requete['FROM'][] = $from;
180                $score = join(' + ', $score).' AS score';
181                spip_log($score, 'recherche');
182
183                // si on define(_FULLTEXT_WHERE_$table,'date>"2000")
184                // cette contrainte est ajoutee ici:)
185                $requete['WHERE'] = $full_text_where;
186                if (defined('_FULLTEXT_WHERE_'.$table))
187                        $requete['WHERE'][] = constant('_FULLTEXT_WHERE_'.$table);
188                else
189                        if (!test_espace_prive()
190                        AND !defined('_RECHERCHE_FULLTEXT_COMPLETE')
191                        AND in_array($table, array('article', 'rubrique', 'breve', 'forum', 'syndic_article')))
192                                $requete['WHERE'][] = "t.statut='publie'";
193
194                // nombre max de resultats renvoyes par l'API
195                define('_FULLTEXT_MAX_RESULTS', 500);
196
197                // preparer la requete
198                $requete['SELECT'][] = $score;
199
200                // popularite ?
201                if (true # config : "prendre en compte la popularite
202                AND $table == 'article')
203                        $requete['SELECT'][] = "t.popularite";
204
205                # "t.date"
206                # "t.note"
207
208                #array_unshift($requete['FROM'], table_objet_sql($table)." AS t");
209                $requete['GROUPBY'] = array("t.$_id_table");
210                $requete['ORDERBY'] = "score DESC";
211                $requete['LIMIT'] = "0,"._FULLTEXT_MAX_RESULTS;
212                $requete['HAVING'] = '';
213
214                #var_dump($requete);
215                #spip_log($requete,'recherche');
216                #exit;
217        }
218
219        $r = array();
220
221        $s = sql_select(
222                $requete['SELECT'], $requete['FROM'], $requete['WHERE'],
223                implode(" ",$requete['GROUPBY']),
224                $requete['ORDERBY'], $requete['LIMIT'],
225                $requete['HAVING'], $serveur
226        );
227
228        if (!$s) spip_log(mysql_errno().' '.mysql_error()."\n".$recherche, 'recherche');
229
230        while ($t = sql_fetch($s,$serveur)
231        AND (!isset($t['score']) OR $t['score']>0)) {
232                $id = intval($t[$_id_table]);
233
234                // FULLTEXT
235                if ($fulltext) {
236                        $pts = $t['score'];
237
238                        if (isset($t['popularite'])
239                        AND $mpop = $GLOBALS['meta']['popularite_max'])
240                                $pts *= (1+$t['popularite']/$mpop);
241
242                        $r[$id]['score'] = $pts;
243
244                }
245                // fin FULLTEXT
246
247
248                if (!$fulltext OR (defined('_FULLTEXT_FIELD_SCORE') AND _FULLTEXT_FIELD_SCORE)) {
249                        if ($options['toutvoir']
250                        OR autoriser('voir', $table, $id)) {
251                                // indiquer les champs concernes
252                                $champs_vus = array();
253                                $score = 0;
254                                $matches = array();
255
256                                $vu = false;
257                                foreach ($champs as $champ => $poids) {
258                                        $champ = explode('.',$champ);
259                                        $champ = end($champ);
260                                        if ($n =
261                                                ($options['score'] || $options['matches'])
262                                                ? preg_match_all($preg, translitteration_rapide($t[$champ]), $regs, PREG_SET_ORDER)
263                                                : preg_match($preg, translitteration_rapide($t[$champ]))
264                                        ) {
265                                                $vu = true;
266
267                                                if ($options['champs'])
268                                                        $champs_vus[$champ] = $t[$champ];
269                                                if ($options['score'])
270                                                        $score += $n * $poids;
271                                                if ($options['matches'])
272                                                        $matches[$champ] = $regs;
273
274                                                if (!$options['champs']
275                                                AND !$options['score']
276                                                AND !$options['matches'])
277                                                        break;
278                                        }
279                                }
280
281                                if ($vu) {
282                                        $r[$id] = array();
283                                        if ($champs_vus)
284                                                $r[$id]['champs'] = $champs_vus;
285                                        if ($score)
286                                                $r[$id]['score'] = ($fulltext?$r[$id]['score']:0)+$score;
287                                        if ($matches)
288                                                $r[$id]['matches'] = $matches;
289                                }
290                        }
291                }
292        }
293
294        // Gerer les donnees associees
295        if (!$fulltext
296        AND isset($jointures[$table])
297        AND $joints = recherche_en_base(
298                        $recherche,
299                        $jointures[$table],
300                        array_merge($options, array('jointures' => false))
301                )
302        ) {
303                foreach ($joints as $jtable => $jj) {
304                        $it = id_table_objet($table);
305                        $ij =  id_table_objet($jtable);
306                        $lesliens = recherche_tables_liens();
307                        if (in_array($jtable, $lesliens))
308                                $s = sql_select("id_objet as $it", "spip_${jtable}s_liens", array("objet='$table'",sql_in('id_'.${jtable}, array_keys($jj))), '','','','',$serveur);
309                        else
310                                $s = sql_select("$it,$ij", "spip_${jtable}s_${table}s", sql_in('id_'.${jtable}, array_keys($jj)), '','','','',$serveur);
311                        while ($t = sql_fetch($s)) {
312                                $id = $t[$it];
313                                $joint = $jj[$t[$ij]];
314                                if (!isset($r))
315                                        $r = array();
316                                if (!isset($r[$id]))
317                                        $r[$id] = array();
318                                if ($joint['score'])
319                                        $r[$id]['score'] += $joint['score'];
320                                if ($joint['champs'])
321                                foreach($joint['champs'] as $c => $val)
322                                        $r[$id]['champs'][$jtable.'.'.$c] = $val;
323                                if ($joint['matches'])
324                                foreach($joint['matches'] as $c => $val)
325                                        $r[$id]['matches'][$jtable.'.'.$c] = $val;
326                        }
327                }
328        }
329
330        return $r;
331}
332
333
334?>
Note: See TracBrowser for help on using the repository browser.