Added support for querying by user.
[zzz-floof.git] / floof / lib / search.py
index 37d762d..8874824 100644 (file)
@@ -1,43 +1,51 @@
-from floof.model import Art, Tag, TagText
+from floof.model import Art, ArtUser, ArtUserType, Tag, TagText, User
 
-def do_search(query):
-    tags = query.split()
+def parse(search_string):
+    """Parses a search query, and returns a query object on Art.
 
-    tagtexts = TagText.query.filter(TagText.text.in_(tags))
-    tagtext_ids = [_.id for _ in tagtexts]
+    Queries can contain:
+    - Regular tags: foo
+    - User relations: by:kalu, of:eevee, for:ootachi
 
-    # Fetch art that has all the tags
-    artwork = Art.query.join(Tag) \
-                   .filter(Tag.tagtext_id.in_(tagtext_ids)) \
-                   .all()
-    return artwork
+    Later:
+    - Negative versions of anything above: -by:eevee, -dongs
+    """
 
+    # XXX doesn't do negative querying yet.
+    # XXX could use some sane limits.
 
+    # We'll be building this as we go.
+    q = Art.query
 
+    terms = search_string.split()
+    for tag in terms:
+        if ':' in tag:
+            # This is a special tag; at the moment, by/for/of to indicate
+            # related users
+            prefix, tag = tag.split(':', 1)
 
+            # XXX what to do if this fails?  abort?  return empty query?
+            target_user = User.get_by(name=tag)
 
-
-# unfinished stuff
-def parse(query):
-    words = query.split()
-
-    tags = []
-    for word in words:
-        components = word.split(':')
-        if len(components) == 1:
-            # tags are plain.
-            tags.append(word)
-        elif components[0] == "rating":
-            if components[1].isnumeric():
-                score = int(components[1])
+            if prefix == 'by':
+                rel = ArtUserType.BY
+            elif prefix == 'for':
+                rel = ArtUserType.FOR
+            elif prefix == 'of':
+                rel = ArtUserType.OF
             else:
-                score = Rating.reverse_options.get(components[1])
+                # Bogus tag.  Not sure what to do here, so for the moment,
+                # ignore it
+                continue
 
-            if -1 <= score <= 3:
-                pass
-                # TODO: Find stuff that has this rating
-                # Rating.query.filter(Rating.s)
+            # Inner join to the ArtUser table
+            q = q.join(ArtUser, aliased=True) \
+                 .filter(ArtUser.user == target_user) \
+                 .filter(ArtUser.type == rel)
 
-    tagtexts = TagText.query.filter(TagText.text.in_(tags))
-    tagtext_ids = map(lambda x:x.id, tagtexts)
+        else:
+            # Regular ol' tag
+            q = q.join(Tag, TagText, aliased=True) \
+                 .filter(TagText.text == tag)
 
+    return q