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

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

calcul des scores : on pondère le nombre d'occurence par une fonction inverse a la longueur du contenu (avoir 10 occurences dans un contenu fleuve n'est pas forcément plus pertinent que 2 occurences dans un contenu court)
Cette modification n'intervient que si pas de fulltext ou si on a activé l'option _FULLTEXT_FIELD_SCORE

File size: 10.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        $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                                                $join = "
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                                                $join = "
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                                        #var_dump($join);
177                                        $from .= $join;
178                                }
179                        }
180                }
181                $requete['FROM'][] = $from;
182                $score = join(' + ', $score).' AS score';
183                spip_log($score, 'recherche');
184
185                // si on define(_FULLTEXT_WHERE_$table,'date>"2000")
186                // cette contrainte est ajoutee ici:)
187                $requete['WHERE'] = $full_text_where;
188                $requete['WHERE'] = array();
189                if (defined('_FULLTEXT_WHERE_'.$table))
190                        $requete['WHERE'][] = constant('_FULLTEXT_WHERE_'.$table);
191                else
192                        if (!test_espace_prive()
193                        AND !defined('_RECHERCHE_FULLTEXT_COMPLETE')
194                        AND in_array($table, array('article', 'rubrique', 'breve', 'forum', 'syndic_article')))
195                                $requete['WHERE'][] = "t.statut='publie'";
196
197                // nombre max de resultats renvoyes par l'API
198                define('_FULLTEXT_MAX_RESULTS', 500);
199
200                // preparer la requete
201                $requete['SELECT'][] = $score;
202
203                // popularite ?
204                if (true # config : "prendre en compte la popularite
205                AND $table == 'article')
206                        $requete['SELECT'][] = "t.popularite";
207
208                # "t.date"
209                # "t.note"
210
211                #array_unshift($requete['FROM'], table_objet_sql($table)." AS t");
212                $requete['GROUPBY'] = array("t.$_id_table");
213                $requete['ORDERBY'] = "score DESC";
214                $requete['LIMIT'] = "0,"._FULLTEXT_MAX_RESULTS;
215                $requete['HAVING'] = '';
216
217                #var_dump($requete);
218                #spip_log($requete,'recherche');
219                #exit;
220        }
221
222        $r = array();
223
224        $s = sql_select(
225                $requete['SELECT'], $requete['FROM'], $requete['WHERE'],
226                implode(" ",$requete['GROUPBY']),
227                $requete['ORDERBY'], $requete['LIMIT'],
228                $requete['HAVING'], $serveur
229        );
230
231        if (!$s) spip_log(mysql_errno().' '.mysql_error()."\n".$recherche, 'recherche');
232
233        while ($t = sql_fetch($s,$serveur)
234        AND (!isset($t['score']) OR $t['score']>0)) {
235                $id = intval($t[$_id_table]);
236
237                // FULLTEXT
238                if ($fulltext) {
239                        $pts = $t['score'];
240
241                        if (isset($t['popularite'])
242                        AND $mpop = $GLOBALS['meta']['popularite_max'])
243                                $pts *= (1+$t['popularite']/$mpop);
244
245                        $r[$id]['score'] = $pts;
246
247                }
248                // fin FULLTEXT
249
250
251                if (!$fulltext OR (defined('_FULLTEXT_FIELD_SCORE') AND _FULLTEXT_FIELD_SCORE)) {
252                        if ($options['toutvoir']
253                        OR autoriser('voir', $table, $id)) {
254                                // indiquer les champs concernes
255                                $champs_vus = array();
256                                $score = 0;
257                                $matches = array();
258
259                                $vu = false;
260                                foreach ($champs as $champ => $poids) {
261                                        $champ = explode('.',$champ);
262                                        $champ = end($champ);
263                                        if ($n =
264                                                ($options['score'] || $options['matches'])
265                                                ? preg_match_all($preg, translitteration_rapide($t[$champ]), $regs, PREG_SET_ORDER)
266                                                : preg_match($preg, translitteration_rapide($t[$champ]))
267                                        ) {
268                                                $vu = true;
269
270                                                if ($options['champs'])
271                                                        $champs_vus[$champ] = $t[$champ];
272                                                if ($options['score'])
273                                                        // on pondere le nombre d'occurence par une fonction inverse de la longueur du contenu
274                                                        // 1 = 1 occurence pour 200 mots de 8 lettres = 1600 signes
275                                                        $score += $n * $poids * sqrt(sqrt(1600/strlen($t[$champ])));
276                                                if ($options['matches'])
277                                                        $matches[$champ] = $regs;
278
279                                                if (!$options['champs']
280                                                AND !$options['score']
281                                                AND !$options['matches'])
282                                                        break;
283                                        }
284                                }
285
286                                if ($vu) {
287                                        $r[$id] = array();
288                                        if ($champs_vus)
289                                                $r[$id]['champs'] = $champs_vus;
290                                        if ($score)
291                                                $r[$id]['score'] = ($fulltext?$r[$id]['score']:0)+$score;
292                                        if ($matches)
293                                                $r[$id]['matches'] = $matches;
294                                }
295                        }
296                }
297        }
298
299        // Gerer les donnees associees
300        if (!$fulltext
301        AND isset($jointures[$table])
302        AND $joints = recherche_en_base(
303                        $recherche,
304                        $jointures[$table],
305                        array_merge($options, array('jointures' => false))
306                )
307        ) {
308                foreach ($joints as $jtable => $jj) {
309                        $it = id_table_objet($table);
310                        $ij =  id_table_objet($jtable);
311                        $lesliens = recherche_tables_liens();
312                        if (in_array($jtable, $lesliens))
313                                $s = sql_select("id_objet as $it", "spip_${jtable}s_liens", array("objet='$table'",sql_in('id_'.${jtable}, array_keys($jj))), '','','','',$serveur);
314                        else
315                                $s = sql_select("$it,$ij", "spip_${jtable}s_${table}s", sql_in('id_'.${jtable}, array_keys($jj)), '','','','',$serveur);
316                        while ($t = sql_fetch($s)) {
317                                $id = $t[$it];
318                                $joint = $jj[$t[$ij]];
319                                if (!isset($r))
320                                        $r = array();
321                                if (!isset($r[$id]))
322                                        $r[$id] = array();
323                                if ($joint['score'])
324                                        $r[$id]['score'] += $joint['score'];
325                                if ($joint['champs'])
326                                foreach($joint['champs'] as $c => $val)
327                                        $r[$id]['champs'][$jtable.'.'.$c] = $val;
328                                if ($joint['matches'])
329                                foreach($joint['matches'] as $c => $val)
330                                        $r[$id]['matches'][$jtable.'.'.$c] = $val;
331                        }
332                }
333        }
334
335        return $r;
336}
337
338
339?>
Note: See TracBrowser for help on using the repository browser.