Line 51... |
Line 51... |
51 |
* - *peut-être*: passer requestFilterParams() en méthode de classe
|
51 |
* - *peut-être*: passer requestFilterParams() en méthode de classe
|
52 |
*
|
52 |
*
|
53 |
*
|
53 |
*
|
54 |
* MySQL sux:
|
54 |
* MySQL sux:
|
55 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
|
55 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1 LIMIT 1);
|
56 |
* MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
|
56 |
* MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery
|
57 |
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT 3);
|
57 |
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT 3);
|
58 |
* PRIMARY
|
58 |
* PRIMARY
|
59 |
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT MIN(3));
|
59 |
* EXPLAIN SELECT * FROM del_image WHERE id_image IN (SELECT MIN(3));
|
60 |
* DEPENDENT SUBQUERY ... ... ... mwarf !
|
60 |
* DEPENDENT SUBQUERY ... ... ... mwarf !
|
61 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1);
|
61 |
* EXPLAIN SELECT id_image FROM v_del_image vdi WHERE vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1);
|
62 |
* 5.5: MATERIALIZED del_image_tag ALL ce_image NULL NULL NULL 38276 Using where
|
62 |
* 5.5: MATERIALIZED del_image_tag ALL ce_image NULL NULL NULL 38276 Using where
|
63 |
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
|
63 |
* 5.1: DEPENDENT SUBQUERY del_image_tag index_subquery ce_image ce_image 8 func 1 Using where
|
64 |
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
|
64 |
* FORCE INDEX/IGNORE INDEX semble incapable de résoudre le problème de l'optimiseur MySQL
|
65 |
*
|
65 |
*
|
66 |
*/
|
66 |
*/
|
Line 67... |
Line 67... |
67 |
|
67 |
|
Line 85... |
Line 85... |
85 |
|
85 |
|
86 |
// en plus de ceux dans ListeObservations
|
86 |
// en plus de ceux dans ListeObservations
|
Line 87... |
Line 87... |
87 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
87 |
static $parametres_autorises = array('protocole', 'masque.tag_cel', 'masque.tag_pictoflora', 'masque.milieu');
|
88 |
|
88 |
|
89 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
89 |
static $default_params = array('navigation.depart' => 0, 'navigation.limite' => 10,
|
90 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
90 |
'tri' => 'date_transmission', 'ordre' => 'desc',
|
Line 91... |
Line 91... |
91 |
// spécifiques à PictoFlora:
|
91 |
// spécifiques à PictoFlora:
|
Line 92... |
Line 92... |
92 |
'format' => 'XL');
|
92 |
'format' => 'XL');
|
93 |
|
93 |
|
Line 150... |
Line 150... |
150 |
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
|
150 |
// filtrage de l'INPUT général, on réutilise 90% de identiplante en terme de paramètres autorisés
|
151 |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
|
151 |
// ($parametres_autorises) sauf... masque.type qui fait des modif' de WHERE sur les mots-clefs.
|
152 |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
|
152 |
// Évitons ce genre de chose pour PictoFlora et les risques de conflits avec masque.tag
|
153 |
// même si ceux-ci sont improbables (pas d'<input> pour cela).
|
153 |
// même si ceux-ci sont improbables (pas d'<input> pour cela).
|
154 |
$params_ip = ListeObservations::requestFilterParams($parametres,
|
154 |
$params_ip = ListeObservations::requestFilterParams($parametres,
|
155 |
array_diff(ListeObservations::$parametres_autorises,
|
155 |
array_diff(ListeObservations::$parametres_autorises,
|
156 |
array('masque.type')),
|
156 |
array('masque.type')),
|
157 |
$this->conteneur);
|
157 |
$this->conteneur);
|
Line 158... |
Line 158... |
158 |
|
158 |
|
159 |
// notre propre filtrage sur l'INPUT
|
159 |
// notre propre filtrage sur l'INPUT
|
160 |
$params_pf = self::requestFilterParams($parametres,
|
160 |
$params_pf = self::requestFilterParams($parametres,
|
161 |
array_merge(ListeObservations::$parametres_autorises,
|
161 |
array_merge(ListeObservations::$parametres_autorises,
|
Line 162... |
Line 162... |
162 |
self::$parametres_autorises));
|
162 |
self::$parametres_autorises));
|
163 |
|
163 |
|
164 |
/* filtrage des tags + sémantique des valeurs multiples:
|
164 |
/* filtrage des tags + sémantique des valeurs multiples:
|
165 |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
|
165 |
Lorsqu'on utilise masque.tag* pour chercher des tags, ils sont
|
166 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
166 |
postulés comme séparés par des virgule, et l'un au moins des tags doit matcher. */
|
Line 167... |
Line 167... |
167 |
$params_pf['masque.tag_cel'] = self::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
167 |
$params_pf['masque.tag_cel'] = self::buildTagsAST(@$parametres['masque.tag_cel'], 'OR', ',');
|
168 |
$params_pf['masque.tag_pictoflora'] = self::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
168 |
$params_pf['masque.tag_pictoflora'] = self::buildTagsAST(@$parametres['masque.tag_pictoflora'], 'OR', ',');
|
169 |
|
169 |
|
170 |
$params = array_merge(ListeObservations::$default_params, // paramètre par défaut Identiplante
|
170 |
$params = array_merge(ListeObservations::$default_params, // paramètre par défaut Identiplante
|
Line 171... |
Line 171... |
171 |
self::$default_params, // paramètres par défaut PictoFlora
|
171 |
self::$default_params, // paramètres par défaut PictoFlora
|
172 |
$params_ip, // les paramètres passés, traités par Identiplante
|
172 |
$params_ip, // les paramètres passés, traités par Identiplante
|
173 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
173 |
$params_pf); // les paramètres passés, traités par PictoFlora
|
Line 191... |
Line 191... |
191 |
|
191 |
|
192 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
192 |
// Ce n'est pas la peine de continuer s'il n'y a pas eu de résultats dans la table del_obs_images
|
193 |
if(!$idobs_tab) {
|
193 |
if(!$idobs_tab) {
|
194 |
$resultat = new ResultatService();
|
194 |
$resultat = new ResultatService();
|
195 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader(0, $params, Config::get('url_service')),
|
195 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader(0, $params, Config::get('url_service')),
|
196 |
'resultats' => array());
|
196 |
'resultats' => array());
|
197 |
return $resultat;
|
197 |
return $resultat;
|
198 |
/*
|
198 |
/*
|
199 |
header('HTTP/1.0 404 Not Found');
|
199 |
header('HTTP/1.0 404 Not Found');
|
200 |
// don't die (phpunit)
|
200 |
// don't die (phpunit)
|
201 |
throw(new Exception()); */
|
201 |
throw(new Exception()); */
|
Line 202... |
Line 202... |
202 |
}
|
202 |
}
|
203 |
|
203 |
|
204 |
|
204 |
|
Line 205... |
Line 205... |
205 |
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
|
205 |
// idobs est une liste (toujours ordonnée) des id d'observations recherchées
|
Line 206... |
Line 206... |
206 |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
|
206 |
$idobs = array_values(array_map(create_function('$a', 'return $a["id_image"];'), $idobs_tab));
|
207 |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
|
207 |
$total = $db->recuperer('SELECT FOUND_ROWS() AS c'); $total = intval($total['c']);
|
208 |
|
208 |
|
209 |
$liaisons = self::chargerImages($db, $idobs);
|
209 |
$liaisons = self::chargerImages($db, $idobs);
|
210 |
|
210 |
|
211 |
/*
|
211 |
/*
|
212 |
// Q&D
|
212 |
// Q&D
|
213 |
$images = array();
|
213 |
$images = array();
|
214 |
$o = new Observation($this->conteneur);
|
214 |
$o = new Observation($this->conteneur);
|
215 |
foreach($idobs as $i) {
|
215 |
foreach($idobs as $i) {
|
216 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
216 |
$images[$i] = $o->consulter(array($i), array('justthrow' => 1));
|
217 |
}
|
217 |
}
|
Line 233... |
Line 233... |
233 |
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
|
233 |
if($votes) Observation::mapVotesToImages($votes, $images_keyed_by_id_image);
|
Line 234... |
Line 234... |
234 |
|
234 |
|
235 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
235 |
// les deux masques de tags sont transformés en AST dans le processus de construction de la requête.
|
236 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
236 |
// Reprenous les paramètres originaux non-nettoyés (ils sont valables car le nettoyage est déterministe)
|
237 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
237 |
$params_header = array_merge($params, array_filter(array('masque.tag_cel' => @$parametres['masque.tag_cel'],
|
238 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
238 |
'masque.tag_pictoflora' => @$parametres['masque.tag_pictoflora'])));
|
239 |
$resultat = new ResultatService();
|
239 |
$resultat = new ResultatService();
|
240 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
240 |
$resultat->corps = array('entete' => ListeObservations::makeJSONHeader($total, $params_header, Config::get('url_service')),
|
241 |
'resultats' => $images);
|
241 |
'resultats' => $images);
|
242 |
return $resultat;
|
242 |
return $resultat;
|
Line 243... |
Line 243... |
243 |
}
|
243 |
}
|
244 |
|
244 |
|
Line 301... |
Line 301... |
301 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
301 |
/* Pour le tri par AVG() des votes nous avons toujours un protocole donné,
|
302 |
celui-ci indique sur quels votes porte l'AVG.
|
302 |
celui-ci indique sur quels votes porte l'AVG.
|
303 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
303 |
(c'est un *vote* qui porte sur un protocole et non l'image elle-même) */
|
304 |
/* TODO: perf problème:
|
304 |
/* TODO: perf problème:
|
305 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
305 |
1) SQL_CALC_FOUND_ROWS: fixable en:
|
306 |
- dissociant le comptage de la récup d'id + javascript async
|
306 |
- dissociant le comptage de la récup d'id + javascript async
|
307 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
307 |
- ou ne rafraîchir le total *que* pour les requête impliquant un changement de pagination
|
308 |
(paramètre booléen "with-total" par exemple)
|
308 |
(paramètre booléen "with-total" par exemple)
|
309 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
309 |
2) jointure forcées: en utilisant `del_imagè`, nous forçons les 2 premiers
|
310 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
310 |
JOIN sur cel_obs_images et cel_obs pour filtrer sur "transmission".
|
311 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
311 |
Dénormaliser cette valeur et l'intégrer à `cel_images` ferait économiser cette couteuse
|
312 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
312 |
jointure, ... lorsqu'aucun masque portant sur `cel_obs` n'est utilisé
|
313 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
313 |
3) non-problème: l'ordre des joins est forcé par l'usage de la vue:
|
314 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
314 |
(cel_images/cel_obs_images/cel_obs/del_image_stat)
|
315 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
315 |
Cependant c'est à l'optimiseur de définir son ordre préféré. */
|
316 |
if($p['tri'] == 'votes') {
|
316 |
if($p['tri'] == 'votes') {
|
317 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
317 |
// $p['protocole'] *est* défini (cf requestFilterParams())
|
318 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
318 |
// petite optimisation: INNER JOIN si ordre DESC car les 0 à la fin
|
319 |
if($p['ordre'] == 'desc') {
|
319 |
if($p['ordre'] == 'desc') {
|
320 |
// pas de group by nécessaire pour cette jointure
|
320 |
// pas de group by nécessaire pour cette jointure
|
321 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
321 |
// PRIMARY KEY (`ce_image`, `ce_protocole`)
|
322 |
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
|
322 |
$req['join'][] = sprintf('INNER JOIN del_image_stat dis'.
|
323 |
' ON vdi.id_image = dis.ce_image'.
|
323 |
' ON vdi.id_image = dis.ce_image'.
|
324 |
' AND dis.ce_protocole = %d',
|
324 |
' AND dis.ce_protocole = %d',
|
325 |
$p['protocole']);
|
325 |
$p['protocole']);
|
326 |
} else {
|
326 |
} else {
|
327 |
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
|
327 |
$req['join'][] = sprintf('LEFT JOIN del_image_stat dis'.
|
328 |
' ON vdi.id_image = dis.ce_image'.
|
328 |
' ON vdi.id_image = dis.ce_image'.
|
329 |
' AND dis.ce_protocole = %d',
|
329 |
' AND dis.ce_protocole = %d',
|
330 |
$p['protocole']);
|
330 |
$p['protocole']);
|
331 |
// nécessaire (dup ce_image dans del_image_stat)
|
331 |
// nécessaire (dup ce_image dans del_image_stat)
|
332 |
$req['groupby'][] = 'vdi.id_observation';
|
332 |
$req['groupby'][] = 'vdi.id_observation';
|
333 |
}
|
333 |
}
|
334 |
}
|
334 |
}
|
Line 335... |
Line 335... |
335 |
|
335 |
|
336 |
if($p['tri'] == 'tags') {
|
336 |
if($p['tri'] == 'tags') {
|
337 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
337 |
$req['join'][] = sprintf('%s JOIN del_image_stat dis ON vdi.id_image = dis.ce_image',
|
338 |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
|
338 |
($p['ordre'] == 'desc') ? 'INNER' : 'LEFT');
|
339 |
// nécessaire (dup ce_image dans del_image_stat)
|
339 |
// nécessaire (dup ce_image dans del_image_stat)
|
340 |
$req['groupby'][] = 'vdi.id_observation';
|
340 |
$req['groupby'][] = 'vdi.id_observation';
|
Line 341... |
Line 341... |
341 |
}
|
341 |
}
|
Line 346... |
Line 346... |
346 |
if(isset($p['masque.tag_cel']['AND'])) {
|
346 |
if(isset($p['masque.tag_cel']['AND'])) {
|
347 |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
|
347 |
// TODO: utiliser les tables de mots clefs normaliées dans tb_cel ?
|
348 |
// et auquel cas laisser au client le choix du couteux "%" ?
|
348 |
// et auquel cas laisser au client le choix du couteux "%" ?
|
349 |
$tags = $p['masque.tag_cel']['AND'];
|
349 |
$tags = $p['masque.tag_cel']['AND'];
|
350 |
array_walk($tags, create_function('&$val, $k, $db',
|
350 |
array_walk($tags, create_function('&$val, $k, $db',
|
351 |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
|
351 |
'$val = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) LIKE %s",
|
352 |
$db->proteger("%".$val."%"));'),
|
352 |
$db->proteger("%".$val."%"));'),
|
353 |
$db);
|
353 |
$db);
|
354 |
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
|
354 |
$req['where'][] = '(' . implode(' AND ', $tags) . ')';
|
355 |
}
|
355 |
}
|
356 |
else {
|
356 |
else {
|
357 |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
|
357 |
$req['where'][] = sprintf("CONCAT(vdi.mots_cles_texte,vdi.i_mots_cles_texte) REGEXP %s",
|
358 |
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
|
358 |
$db->proteger(implode('|', $p['masque.tag_cel']['OR'])));
|
359 |
}
|
359 |
}
|
360 |
}
|
360 |
}
|
Line 361... |
Line 361... |
361 |
|
361 |
|
362 |
|
362 |
|
363 |
// XXX: utiliser tag plutôt que tag_normalise ?
|
363 |
// XXX: utiliser tag plutôt que tag_normalise ?
|
364 |
if($p['masque.tag_pictoflora']) {
|
364 |
if($p['masque.tag_pictoflora']) {
|
365 |
// pas de LEFT JOIN ? ou bien peut-être en cas de tri, mais nous parlons bien ici d'un masque
|
365 |
// pas de LEFT JOIN ? ou bien peut-être en cas de tri, mais nous parlons bien ici d'un masque
|
Line -... |
Line 366... |
- |
|
366 |
/* $req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
|
- |
|
367 |
$req['where'][] = 'dit.actif = 1'; */
|
- |
|
368 |
|
- |
|
369 |
|
- |
|
370 |
|
- |
|
371 |
// ==== commenté pour l'instant pour cause de soucis d'optimiseur MySQL (cf commentaire en intro) ====
|
- |
|
372 |
/*
|
- |
|
373 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
- |
|
374 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
- |
|
375 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
- |
|
376 |
sort($p['masque.tag_pictoflora']['AND']);
|
- |
|
377 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
- |
|
378 |
" GROUP BY ce_image".
|
- |
|
379 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
|
- |
|
380 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
|
- |
|
381 |
}
|
- |
|
382 |
else {
|
- |
|
383 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
- |
|
384 |
" GROUP BY ce_image".
|
- |
|
385 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
|
- |
|
386 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
|
- |
|
387 |
}
|
366 |
/* $req['join'][] = 'LEFT JOIN del_image_tag dit ON dit.ce_image = vdi.id_image';
|
388 |
*/
|
367 |
$req['where'][] = 'dit.actif = 1'; */
|
389 |
|
368 |
|
390 |
// ==== XXX: puisque on est bassiné par cette "DEPENDENT SUBQUERY", nous la faisons donc indépendemment ====
|
369 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
391 |
if(isset($p['masque.tag_pictoflora']['AND'])) {
|
- |
|
392 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
- |
|
393 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
- |
|
394 |
sort($p['masque.tag_pictoflora']['AND']);
|
370 |
// optimsation: en cas de "AND" on sort() l'input et le GROUP_CONCAT()
|
395 |
|
371 |
// donc nous utilisons des ".*" plutôt que de multiples conditions et "|"
|
396 |
// plutôt que db->connexion->query->fetchColumn(), une API pourrie nous oblige à ...
|
372 |
sort($p['masque.tag_pictoflora']['AND']);
|
397 |
$ids = $db->recupererTous(sprintf(
|
373 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
398 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
- |
|
399 |
" GROUP BY ce_image".
|
- |
|
400 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s",
|
- |
|
401 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND']))));
|
- |
|
402 |
// puis:
|
374 |
" GROUP BY ce_image".
|
403 |
$ids = array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
375 |
" HAVING GROUP_CONCAT(tag_normalise ORDER BY tag_normalise) REGEXP %s)",
|
404 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
- |
|
405 |
|
376 |
$db->proteger(implode('.*', $p['masque.tag_pictoflora']['AND'])));
|
406 |
}
|
377 |
}
|
407 |
else {
|
378 |
else {
|
408 |
$ids = $db->recupererTous(sprintf(
|
379 |
$req['where'][] = sprintf("vdi.id_image IN (SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
409 |
"SELECT ce_image FROM del_image_tag WHERE actif = 1".
|
- |
|
410 |
" GROUP BY ce_image".
|
- |
|
411 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s",
|
380 |
" GROUP BY ce_image".
|
412 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR']))));
|
- |
|
413 |
$ids = array_map(create_function('$e', 'return $e["ce_image"];'), $ids);
|
381 |
" HAVING GROUP_CONCAT(tag_normalise) REGEXP %s)",
|
414 |
if($ids) $req['where'][] = sprintf("vdi.id_image IN (%s)", implode(',', $ids));
|
382 |
$db->proteger(implode('|', $p['masque.tag_pictoflora']['OR'])));
|
415 |
}
|
Line 383... |
Line 416... |
383 |
}
|
416 |
|
Line 409... |
Line 442... |
409 |
static function chargerImages($db, $idImg) {
|
442 |
static function chargerImages($db, $idImg) {
|
410 |
$obs_fields = Observation::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
443 |
$obs_fields = Observation::sqlFieldsToAlias(self::$mappings['observations'], NULL);
|
411 |
$image_fields = Observation::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
444 |
$image_fields = Observation::sqlFieldsToAlias(self::$mappings['images'], NULL);
|
Line 412... |
Line 445... |
412 |
|
445 |
|
413 |
return $db->recupererTous(sprintf('SELECT '.
|
446 |
return $db->recupererTous(sprintf('SELECT '.
|
414 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
447 |
' CONCAT(id_image, "-", id_observation) AS jsonindex,'.
|
415 |
' %1$s, %2$s FROM v_del_image '.
|
448 |
' %1$s, %2$s FROM v_del_image '.
|
416 |
' WHERE %3$s'.
|
449 |
' WHERE %3$s'.
|
417 |
' -- %4$s',
|
450 |
' -- %4$s',
|
418 |
$obs_fields, $image_fields,
|
451 |
$obs_fields, $image_fields,
|
419 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
452 |
sprintf('id_image IN (%s)', implode(',', $idImg)),
|
Line 420... |
Line 453... |
420 |
__FILE__ . ':' . __LINE__));
|
453 |
__FILE__ . ':' . __LINE__));
|
Line 421... |
Line 454... |
421 |
|
454 |
|
422 |
}
|
455 |
}
|
423 |
|
456 |
|
424 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
457 |
/* "masque" ne fait jamais que faire une requête sur la plupart des champs, (presque) tous traités
|
425 |
de manière identique à la seule différence que:
|
458 |
de manière identique à la seule différence que:
|
426 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
459 |
1) ils sont combinés par des "OU" logiques plutôt que des "ET".
|
427 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
460 |
2) les tags sont traités différemment pour conserver la compatibilité avec l'utilisation historique:
|
428 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
461 |
Tous les mots-clefs doivent matcher et sont séparés par des espaces
|
429 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
462 |
(dit autrement, str_replace(" ", ".*", $mots-clefs-requête) =~ $mots-clefs-mysql)
|
430 |
Pour plus d'information: ListeObservations::sqlAddMasqueConstraint() */
|
463 |
Pour plus d'information: ListeObservations::sqlAddMasqueConstraint() */
|
431 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
464 |
static function sqlAddMasqueConstraint($p, $db, &$req, Conteneur $c = NULL) {
|
432 |
if(!empty($p['masque'])) {
|
465 |
if(!empty($p['masque'])) {
|
433 |
$or_params = array('masque.auteur' => $p['masque'],
|
466 |
$or_params = array('masque.auteur' => $p['masque'],
|
434 |
'masque.departement' => $p['masque'],
|
467 |
'masque.departement' => $p['masque'],
|
435 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
468 |
'masque.commune' => $p['masque'], // TODO/XXX ?
|
436 |
'masque.id_zone_geo' => $p['masque'],
|
469 |
'masque.id_zone_geo' => $p['masque'],
|
437 |
|
470 |
|
438 |
/* tous-deux remplacent masque.tag
|
471 |
/* tous-deux remplacent masque.tag
|
439 |
mais sont traité séparément des requestFilterParams() */
|
472 |
mais sont traité séparément des requestFilterParams() */
|
440 |
// 'masque.tag_cel' => $p['masque'],
|
473 |
// 'masque.tag_cel' => $p['masque'],
|
441 |
// 'masque.tag_pictoflora' => $p['masque'],
|
474 |
// 'masque.tag_pictoflora' => $p['masque'],
|
442 |
|
475 |
|
443 |
'masque.ns' => $p['masque'],
|
476 |
'masque.ns' => $p['masque'],
|
444 |
'masque.famille' => $p['masque'],
|
477 |
'masque.famille' => $p['masque'],
|
445 |
'masque.date' => $p['masque'],
|
478 |
'masque.date' => $p['masque'],
|
446 |
'masque.genre' => $p['masque'],
|
479 |
'masque.genre' => $p['masque'],
|
447 |
'masque.milieu' => $p['masque'],
|
480 |
'masque.milieu' => $p['masque'],
|
448 |
|
481 |
|
Line 449... |
Line 482... |
449 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
482 |
// tri est aussi nécessaire car affecte les contraintes de JOIN
|
450 |
'tri' => $p['tri'],
|
483 |
'tri' => $p['tri'],
|
451 |
'ordre' => $p['ordre']);
|
484 |
'ordre' => $p['ordre']);
|
Line 550... |
Line 583... |
550 |
elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
|
583 |
elseif(preg_match('/\b(OU|OR)\b/', $str)) $op = 'OR';
|
551 |
else $op = $default_op;
|
584 |
else $op = $default_op;
|
Line 552... |
Line 585... |
552 |
|
585 |
|
553 |
if($additional_sep) {
|
586 |
if($additional_sep) {
|
554 |
array_walk($words,
|
587 |
array_walk($words,
|
555 |
create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
|
588 |
create_function('&$v, $k, $sep', '$v = preg_split("/".$sep."/", $v, -1, PREG_SPLIT_NO_EMPTY);'),
|
556 |
$additional_sep);
|
589 |
$additional_sep);
|
557 |
}
|
590 |
}
|
558 |
$words = self::array_flatten($words);
|
591 |
$words = self::array_flatten($words);
|
559 |
$words = array_map('trim', $words);
|
592 |
$words = array_map('trim', $words);
|
560 |
return array($op => array_filter($words));
|
593 |
return array($op => array_filter($words));
|
Line 592... |
Line 625... |
592 |
next($arr);
|
625 |
next($arr);
|
593 |
}
|
626 |
}
|
594 |
}
|
627 |
}
|
595 |
return $arr;
|
628 |
return $arr;
|
596 |
}
|
629 |
}
|
597 |
}
|
- |
|
598 |
|
630 |
}
|
- |
|
631 |
|