Anomalie #3894

Jointures (erronées ?) avec les boucles documents et leurs critères

Ajouté par marcimat 🌈 il y a 6 mois. Mis à jour il y a environ un mois.

Statut:NouveauDébut:26/01/2017
Priorité:NormalEchéance:
Assigné à:-% réalisé:

0%

Catégorie:-
Version cible:3.2
Resolution: Navigateur:

Description

Soit le cas suivant :

- une document A, attaché à une rubrique R1 et R2
- <docA> mis dans le texte de R1 (il est donc "vu" dans R1, mais pas dans R2)
- une boucle documents simplifiée (issue de squelettes-dist/inclure/documents.html) dans le squelette test.html :

<h1>Test</h1>
<BOUCLE_A(DOCUMENTS){id_article?}{id_rubrique?}{objet?}{id_objet?}{vu=non}>
<li>#FICHIER</li>
</BOUCLE_A>

Constats

Paramètre id_rubrique :

Si on appelle ?page=test&id_rubrique=1 le document A sera retourné, malgré le critère {vu=non}.
Effectivement la boucle effectue 2 jointures différentes sur spip_documents_liens, une pour lier le champ "vu" et l'autre pour lier objet/id_objet.
Du coup, la requête SQL trouve effectivement un document A non vu (dans la rubrique 2) et le retourne (vu qu'il est lié aussi à la rubrique 1).

On obtient la requête suivante :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L2 ON ( L2.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L2.id_objet = 1)
    AND (L2.objet = 'rubrique')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Paramètres objet / id_objet :

Si on appelle page=test&objet=rubrique&id_objet=1, soit logiquement la même chose, on obtient 3 jointures sur spip_documents_liens.
Là, les documents retournés ne soient pas forcément ceux de l'objet demandé ! La jointure cherche des documents liés à objet=rubrique, id_objet=1, mais pas forcément dans la même liaison !
Les documents retournés ici sont parfois un peu farfelus donc là.

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L4 ON ( L4.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L3 ON ( L3.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L3.objet = 'rubrique')
    AND (L4.id_objet = 1)
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Paramètre id_article

Si on appelle page=test&id_article=1, on obtient, ô magie, une seule jointure. La boucle est correcte cette fois-ci donc. Je n'ai pas encore cherché pourquoi ça marche avec id_article, et pas id_rubrique…

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L1.id_objet = 1)
    AND (L1.objet = 'article')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Demandes liées

Lié à Medias - Roadmap #3887: Permettre d'ordonner les documents associés à des objets ... Fermé 11/01/2017

Historique

#1 Mis à jour par tcharlss (*´_ゝ`) il y a 6 mois

J'ai testé avec divers objets que j'avais sous la main pour voir, on reprenant le même procédé :

  • un document attaché à 2 objets, vu=oui pour le N°1, et vu=non pour le N°2
  • id_patate=1 dans l'URL
  • {id_patate?}{vu=non} dans la boucle

Ça n'a fait la bonne jointure que pour 1 seul objet, donc c'est plutôt une exception on dirait.
Rigolo : le nom de la jointure superflue s'incrémente entre chaque test, j'ai fini à L8 ! (L5 je l'ai enlevé, c'était sur les auteurs, donc même ambiguïté que pour les mots clés)

Album [OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L1.id_objet = 1)
    AND (L1.objet = 'album')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Brève [PAS OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L2 ON ( L2.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L2.id_objet = 1)
    AND (L2.objet = 'breve')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Forum [PAS OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L3 ON ( L3.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L3.id_objet = 1)
    AND (L3.objet = 'forum')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Mot [PAS OK] (là c'est un peu spécial car il y a une ambiguïté avec mots_liens) :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_mots_liens AS L4 ON ( L4.id_objet = documents.id_document AND L4.objet='document') 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L4.id_mot = 1)
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Abonnement [PAS OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L6 ON ( L6.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L6.id_objet = 1)
    AND (L6.objet = 'abonnement')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Ticket [PAS OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L7 ON ( L7.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L7.id_objet = 1)
    AND (L7.objet = 'ticket')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Adresse [PAS OK] :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L8 ON ( L8.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.taille > 0 OR documents.distant='oui')
    AND (L8.id_objet = 1)
    AND (L8.objet = 'adresse')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

#2 Mis à jour par marcimat 🌈 il y a 6 mois

Tu avais mis plusieurs boucles dans ton squelette de test du coup ? C'est étonnant cet incrément de Ln tout de même.

#3 Mis à jour par tcharlss (*´_ゝ`) il y a 6 mois

Non, il s'agissait d'une seule boucle, et à chaque test j'ai changé {id_patate} dans l'URL.
Ça vaudrait le coup de tester plusieurs boucles dans le squelette pour voir si ça fait pareil oui.

#4 Mis à jour par marcimat 🌈 il y a 6 mois

Jointure avec Documents / id_mot

Par ailleurs pour les mots la jointure que montre tcharles est correcte justement. Cependant, là il cherche les mots clés présents sur les documents, ce qui contredit ce que je disais en 2012 là http://marcimat.magraine.net/SPIP-3-Documents-Mots :

Ce que SPIP va décider lorsqu’il y a ambiguïté, c’est à dire comme ici (DOCUMENTS){id_mot} alors qu’il existe les 2 tables spip_documents_liens et spip_mots_liens, c’est qu’il va préférer interpréter cela comme une liaison sur la table de lien de la boucle en cours, c’est à dire sur spip_documents_liens, ce qui signifie donc que ça va retourner « les documents attachés à un mot clé »

Donc, pour ce point, quelque part à un moment donné il y a eu un changement.

Autres test.

Je viens de remarquer que le problème survient dès lors qu'il y a plusieurs critères optionnels sur la boucle. De telle sorte les boucles suivantes ont une seule jointure sur spip_documents_liens :
- <BOUCLE_A(DOCUMENTS){id_article?}{vu=non}>
- <BOUCLE_B(DOCUMENTS){id_rubrique?}{vu=non}>
- <BOUCLE_C(DOCUMENTS){id_mot?}{vu=non}> (+ 1 jointure sur spip_mots_liens)

C'est dès lors qu'on utilise plusieurs critères optionnels que les problèmes surviennent :
- <BOUCLE_E(DOCUMENTS){id_breve?}{id_article?}{id_rubrique?}{id_mot?}{objet?}{id_objet?}{vu=non}>

Si on appelle page=test&id_breve=1 (1er critère) on aura 1 seule jointure :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L1.id_objet = 1)
    AND (L1.objet = 'breve')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Si on appelle page=test&id_article=1 (2è critère) on aura 2 jointures :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L2 ON ( L2.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L2.id_objet = 1)
    AND (L2.objet = 'article')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Si on appelle page=test&id_rubrique=1 (3è critère) on aura 2 jointures (le L s'incrémente à L3) :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L3 ON ( L3.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L3.id_objet = 1)
    AND (L3.objet = 'rubrique')
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Autrement dit, je pense que SPIP crée une jointure pour la possibilité d'avoir id_breve dans l'URL (L1), pareil pour id_article (L2), etc pour les suivantes.
Le champ "vu" utilise la première jointure possible (toujours L1 du coup ici). Ensuite SPIP nettoie les jointures inutiles en fonction des paramètres d'environnement reçus, mais il ne peut enlever L1 car le champ "vu" l'utilise (alors qu'il faudrait qu'il utilise une autre jointure, L3 par exemple si id_rubrique dans l'env, et enlever L1).

#5 Mis à jour par marcimat 🌈 il y a 6 mois

Les cas objet / id_objet

En appelant page=test&objet=rubrique&id_objet=1 :

- (DOCUMENTS){objet?}{id_objet?}
- (DOCUMENTS){id_objet?}{objet?}
- (DOCUMENTS){objet?}{id_objet?}{vu=non}
- (DOCUMENTS){id_objet?}{objet?}{vu=non}
- (DOCUMENTS){vu=non}{id_objet?}{objet?}
Jointures correctes : 1 seule jointure L1 sur spip_documents_liens

- (DOCUMENTS){id_article?}{objet?}{id_objet?}
Jointure incorrecte : il y a 2 jointures différentes pour objet & id_objet

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L3 ON ( L3.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L2 ON ( L2.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L2.objet = 'rubrique')
    AND (L3.id_objet = 1)
GROUP BY documents.id_document

- (DOCUMENTS){id_article?}{objet?}{id_objet?}{vu=non}
Même comportement erroné, avec vu sur L1 et 2 jointures pour objet & id_objet :

SELECT documents.fichier
FROM spip_documents AS `documents`  
INNER JOIN spip_documents_liens AS L3 ON ( L3.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L2 ON ( L2.id_document = documents.id_document ) 
INNER JOIN spip_documents_liens AS L1 ON ( L1.id_document = documents.id_document )
WHERE (documents.statut = 'publie')
    AND (documents.mode IN ('image','document'))
    AND (documents.taille > 0 OR documents.distant='oui')
    AND (L2.objet = 'rubrique')
    AND (L3.id_objet = 1)
    AND (L1.vu = 'non')
GROUP BY documents.id_document

Il me semble que les jointures objet / id_objet ne fonctionnent que s'ils sont les premiers critères optionnels créant possiblement une jointure de la boucle.
Cependant (DOCUMENTS){vu?}{objet?}{id_objet?} fonctionne aussi avec vu optionnel (1 seule jointure).

#6 Mis à jour par marcimat 🌈 il y a 6 mois

Pour contourner le problème pour l'inclusion concernée dans squelettes-dist/inclure/documents.html, j'ai appliqué r102606 Et des oups avec…

#7 Mis à jour par marcimat 🌈 il y a 5 mois

  • Lié à Roadmap #3887: Permettre d'ordonner les documents associés à des objets éditoriaux (plugin Ordoc en 3.1) ajouté

#8 Mis à jour par cedric - il y a 3 mois

  • Version cible mis à 3.2

#9 Mis à jour par Emmanuel GUILLEMONT il y a environ un mois

comportement anormal/déroutant du critère id_mot? dans un contexte où il n'y a pas d'id_mot dans l'environnement
(signalé sur IRC le 12 juin)
la boucle :
<BOUCLE_archive(ARTICLES){id_rubrique?}{id_auteur?}{id_mot?}>
<a href="[(#URL_PAGE{mapage}|parametre_url{id_article,#ID_ARTICLE}|parametre_url{id_mot,#ID_MOT})]">
#MODELE{vignettes,largeur=60,hauteur=40}
</a>
</BOUCLE_archive>

ne donne pas le même résultat que la boucle :
<BOUCLE_archive(ARTICLES){id_rubrique?}{id_auteur?}}>
<a href="[(#URL_PAGE{mapage}|parametre_url{id_article,#ID_ARTICLE}|parametre_url{id_mot,#ID_MOT})]">
#MODELE{vignettes,largeur=60,hauteur=40}
</a>
</BOUCLE_archive>

----
Selon Marcimat : "ce qui se passe je pense, c'est que comme il y a une jointure possible sur spip_mots, et qu’il voit que tu demandes #ID_MOT, il ne supprime pas la jointure si id_mot n’est pas dans l’environnement."

la suppression de }|parametre_url{id_mot,#ID_MOT} rétablit en effet le comportement attendu par la présence de {id_mot?}

Formats disponibles : Atom PDF

Ajouter une image à partir du presse-papier (Taille maximale: 1,25 Mo)