7
votes

Recherche d'articles dans une relation nombreuses à plusieurs

J'écris actuellement une application qui permet de stocker des images, puis de tagler ces images. J'utilise Python et le PEEWEE ORM (http://charlesleifer.com/docs/peewee/), qui ressemble beaucoup à l'ormes de Django.

Mon modèle de données ressemble à ceci (simplifié): < Pré> xxx

Maintenant, je comprends de manière conceptuelle comment interroger sur toutes les images qui ont un ensemble donné de balises: xxx

Cependant, je veux aussi être capable de faire des recherches plus complexes. Spécifiquement, j'aimerais pouvoir spécifier une liste de "toutes les balises" - c'est-à-dire qu'une image doit avoir toutes les balises spécifiées à être renvoyées, ainsi qu'une liste de "toute" et une liste de "Aucun". < / p>

EDIT: J'aimerais clarifier cela un peu. Plus précisément, la requête ci-dessus est une "toutes les balises" -style. Il renvoie des images qui ont toutes les balises données. Je veux être capable de spécifier quelque chose comme: "Donnez-moi toutes les images qui ont les étiquettes (vertes, montagne), l'une des balises (arrière-plan, paysage) mais pas les étiquettes (numériques, dessin)".

Maintenant, idéalement, j'aimerais que cela soit une requête SQL, car la pagination devient alors très facile avec la limite et le décalage. J'ai eu une implémentation de la mise en œuvre qui vous permet de tout charger dans des ensembles Python, puis utilisez les différents opérateurs d'intersection. Ce que je me demande, c'est s'il y a une méthode de faire tout cela à la fois?

Aussi, pour les personnes intéressées, j'ai envoyé l'auteur de Peewee sur la manière de représenter la requête ci-dessus à l'aide de Peewee, et il a répondu avec la solution suivante: xxx

ou, alternativement, une version plus courte: xxx

Merci d'avance pour votre temps .


8 commentaires

Si je comprends bien, vous avez la réponse dont vous avez besoin dans Peewee Code, mais vous voulez savoir comment faire la même chose en SQL droit?


Pouvez-vous s'il vous plaît expliquer la pièce "Cependant, je veux aussi pouvoir faire des recherches plus complexes. Spécifiquement, je voudrais être capable de spécifier une liste de" toutes les balises "- c'est-à-dire une image doit avoir toutes les balises spécifiées Pour être retourné, avec une liste de "toute" et une liste de "Aucun". "


@naresh Spécifiquement, la requête ci-dessus est une "liste des balises" -style. Il renvoie des images qui ont toutes les balises données. Je veux pouvoir spécifier quelque chose comme: "Donnez-moi toutes les images qui ont les étiquettes (vertes, montagne), l'une des balises (arrière-plan, paysage) mais pas les étiquettes (numériques, dessin)". S'il vous plaît laissez-moi savoir si ce n'est pas clair.


Oh, cela clarifie beaucoup. Je pensais que la question ci-dessus était un peu ambiguë. C'est possible cependant. Est-ce que ça va bien s'il avait vert, montagne et paysage?


@JustinDanielson - Ouais, ça irait bien. N'importe quel "vert, montagne, paysage" ou "vert, montagne, fond", avec des autres étiquettes qui ne sont pas "numériques" ou "dessin" fonctionnent.


Ok donc tu veux ((A et B) ou (c et d)) mais pas (e et f)?


@JustinDanielson - Utilisation de la logique booléenne: (A et B) et (c ou d) et (non (e ou f)))


@JustinDanielson Pouvez-vous vérifier ma réponse mise à jour?


3 Réponses :


0
votes

La requête suivante doit renvoyer toutes les images étiquetées avec les deux ('A' et 'B') et ('C' ou 'D' ou 'D') mais pas 'E' et 'F'

SELECT Image.key
FROM Image
INNER JOIN TagRelationship
    ON Image.ID = TagRelationship.ImageID
INNER JOIN Tag tag1
    ON TagRelationship.TagID = tag1.ID
INNER JOIN Tag tag2
    ON TagRelationship.TagID = tag2.ID
WHERE tag1.tag
    IN ( 'A' , 'B' )
AND tag2.tag NOT IN ('E', 'F')

GROUP BY Image.key
HAVING COUNT(*) = 2 

UNION

SELECT Image.key
FROM Image
INNER JOIN TagRelationship
    ON Image.ID = TagRelationship.ImageID
INNER JOIN Tag tag1
    ON TagRelationship.TagID = tag1.ID
INNER JOIN Tag tag2
    ON TagRelationship.TagID = tag2.ID
WHERE tag1.tag
   IN ( 'C' , 'D' )
AND tag2.tag NOT IN ('E', 'F')


4 commentaires

Si tag.tag est dans ('a', 'B') , il ne peut jamais être trouvé dans ('E', 'F') . Les deux pas dans les conditions ont l'air redondant dans votre requête particulière.


S'il veut plus de 2 tags, vous devrez faire une autre jointure pour TAG3. Cela va rapidement sortir de la main et très lent s'il veut de nombreuses tags.


Je pense que cette requête ne retournera aucun résultat. Tag1 == Tag2 est possible car Tag1! = Tag2 n'est pas appliqué. Donc, vous pourriez avoir une situation où vous obtenez image. Valeur 1 avec des balises A, B, C, D et PAS E, F. Mais vous obtiendrez TAG1 = A, TAG2 = A, ainsi que A, B A, C A, D B, A et toutes les autres permutations. Donc, le comte finira par être 8


Si une image a des étiquettes A, E, F et ID 1. Vous obtiendrez des rangées de 1AA, 1AA, 1Ae, 1ea, 1f, 1EF, 1EF, 1FE, 1FE et le comte (*) sera 2 parce que 1AA et 1AA être accepté.



2
votes

La moitié supérieure obtient les mots qui correspondent aux balises obligatoires. La moitié inférieure fait les étiquettes où au moins 1 doivent être présents. La requête inférieure n'a pas de groupe par parce que je veux savoir si une image apparaît deux fois. Si c'est le cas, il a à la fois de l'arrière-plan et du paysage. La commande par compte (*) fera des photos avec des balises d'arrière-plan et de paysage à apparaître en haut. Si vert, montagne, paysage de fond sera le plus pertinent. Puis des images vertes, montagne, fond ou paysage.

SELECT Image.key, count(*) AS 'relevance' 
FROM
     (SELECT Image.key
      FROM
        --good image candidates
        (SELECT Image.key
         FROM Image
         WHERE Image.key NOT IN 
            --Bad Images
            (SELECT DISTINCT(Image.key)   --Will reduce size of set, remove duplicates
             FROM Image
             INNER JOIN TagRelationship
                ON Image.ID = TagRelationship.ImageID
             INNER JOIN Tag
                ON TagRelationship.TagID = Tag.ID
              WHERE Tag.tag
                   IN ('digital', 'drawing' )))
    INNER JOIN TagRelationship
        ON Image.ID = TagRelationship.ImageID
    INNER JOIN Tag
        ON TagRelationship.TagID = Tag.ID
    WHERE Tag.tag
           IN ('green', 'mountain')
    GROUP BY Image.key
    HAVING COUNT(*) = count('green', 'mountain')
    --we need green AND mountain

    UNION ALL

    --Get all images with one of the following 2 tags
    SELECT * 
    FROM
        (SELECT Image.key
         FROM Image
         INNER JOIN TagRelationship
             ON Image.ID = TagRelationship.ImageID
         INNER JOIN Tag
             ON TagRelationship.TagID = Tag.ID
          WHERE Tag.tag
             IN ( 'background' , 'landscape' ))
)
GROUP BY Image.key
ORDER BY relevance DESC


0 commentaires

5
votes
SELECT Image.key
  FROM Image
  JOIN TagRelationship
    ON Image.ID = TagRelationship.ImageID
  JOIN Tag
    ON TagRelationship.TagID = Tag.ID
 GROUP BY Image.key
HAVING COUNT(CASE WHEN Tag.tag IN (mandatory tags ) THEN 1 END) = N  /*the number of mandatory tags*/
   AND COUNT(CASE WHEN Tag.tag IN (optional tags  ) THEN 1 END) > 0
   AND COUNT(CASE WHEN Tag.tag IN (prohibited tags) THEN 1 END) = 0

5 commentaires

Est-ce que la déclaration d'avoir fonctionne correctement? Depuis que vous effectuez un groupe, vous n'aurez que 1 valeur pour la balise. Puisque c'est en somme, cela ira-t-il sur toutes les étiquettes et préfère le chèque? C'est beaucoup plus élégant que le mien si cela fonctionne.


Je pensais après avoir regroupé par un attribut, vous n'avez pas pu préformer l'analyse sur des lignes individuelles dans des groupes tels que Tag.tag In (Tags obligatoires)) = N. Je pourrais avoir à configurer des tables et tester cette requête pour moi-même. Je sais que des opérations globales telles que la somme fonctionneront, mais je ne sais pas si tag.tag in (tags obligatoires)) = n fonctionnera. Pas parce que je doute de toi, mais parce que je n'ai pas vu ni fait ça. C'est nouveau pour moi.


@JustinDanielson: Oui, vous pouvez avoir des expressions de référencement non-groupe par des colonnes à l'intérieur des fonctions globales, si c'est ce que vous demandez. Les résultats des expressions sont agrégés en conséquence. Vous savez que vous pouvez somme (valeur) , mais de la même manière que vous pouvez somme (valeur * qty) ou somme (valeur> 10) . Dans la saveur de MySQL de SQL, quelle est ma requête, dans peut être visualisé comme un autre opérateur, comme * ou + , seulement il renvoie Un booléen (qui est implicitement converti en un entier), comme << / code> ou = aussi.


Merci d'avoir expliqué cela avec des détails. Heureux que j'ai appris ça. Je vais me sauver beaucoup de temps en cas où je dois écrire une autre requête comme celle-ci. Découvrez le mien, je l'ai fait la dure. : [


Salut là-bas - merci pour ça! Ça marche vraiment bien. Juste un petit changement - le ">" dans la deuxième condition devrait être "> =", pour couvrir le cas que toutes les balises correspondent. Autre que cela, merci encore. J'ai marqué cela comme une réponse :)