7

私は現在、画像を保存し、これらの画像にタグを付けることができるアプリケーションを作成しています。私は Python と Peewee ORM (http://charlesleifer.com/docs/peewee/) を使用しています。これは Django の ORM に非常に似ています。

私のデータモデルは次のようになります(簡略化):

class Image(BaseModel):
    key = CharField()

class Tag(BaseModel):
    tag = CharField()

class TagRelationship(BaseModel):
    relImage = ForeignKeyField(Image)
    relTag   = ForeignKeyField(Tag)

これで、特定のタグ セットを持つすべての画像をクエリする方法を概念的に理解できました。

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 ( 'A' , 'B' )     -- list of multiple tags
GROUP BY Image.key
HAVING COUNT(*) = 2         -- where 2 == the number of tags specified, above

ただし、より複雑な検索もできるようにしたいと考えています。具体的には、「すべてのタグ」のリストを指定できるようにしたいと考えています。つまり、画像には、指定されたすべてのタグと、「任意」のリストおよび「なし」のリストが含まれている必要があります。

編集:これを少し明確にしたいと思います。具体的には、上記のクエリは「すべてのタグ」スタイルのクエリです。指定されたすべてのタグを持つ画像を返します。次のようなものを指定できるようにしたい:「タグ(緑、山)、タグ(背景、風景)のいずれかを持ち、タグ(デジタル、描画)を持たないすべての画像を教えてください」。

さて、理想的には、これを 1 つの SQL クエリにしたいと思います。なぜなら、LIMIT と OFFSET を使用すると、ページネーションが非常に簡単になるからです。私は実際に、すべてをPythonセットにロードしてから、さまざまな交差演算子を使用する実装を行っています。私が疑問に思っているのは、これを一度に行う方法があるかどうかです。

また、興味のある方のために、Peewee を使用して上記のクエリを表す方法について Peewee の作成者にメールを送信したところ、彼は次の解決策で応答しました。

Image.select(['key']).group_by('key').join(TagRelationship).join(Tag).where(tag__in=['tag1', 'tag2']).having('count(*) = 2')

または、代わりに、短いバージョン:

Image.filter(tagrelationship_set__relTag__tag__in=['tag1', 'tag2']).group_by(Image).having('count(*) = 2')

お時間をいただきありがとうございます。

4

3 に答える 3

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

アップデート

上記のクエリのより広く受け入れられているバージョン (CASE 式を使用して、IN 述語のブール結果を整数に変換します):

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

またはSUMの代わりにCOUNTを使用:

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
于 2012-01-16T07:58:38.817 に答える
2

上半分は、必須タグに一致する単語を取得します。下半分は、少なくとも 1 つ存在する必要があるタグを実行します。画像が 2 回表示されるかどうかを知りたいので、一番下のクエリには GROUP BY がありません。ある場合は、背景と風景の両方があります。ORDER BY count(*) は、背景タグと風景タグの両方を含む写真を上部に表示します。そのため、緑、山、背景の風景が最も関連性があります。次に、緑、山、背景または風景の写真。

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
于 2012-01-16T07:08:16.497 に答える
0

次のクエリは、('A' と 'B') と ('C' OR 'D') の両方でタグ付けされ、'E' と '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')
于 2012-01-16T07:05:45.963 に答える