summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-06-19 14:22:47 -0600
committerAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-06-19 14:22:47 -0600
commitd26f827463f2d76423dc12288f2c3d6740f2b607 (patch)
treed073a8d9993295d98ae61a83e03bfd928d3ff5ae
parente99720f1f6699ed3fc6afe52e93b2d9f9b33fd5c (diff)
downloadaskbot-d26f827463f2d76423dc12288f2c3d6740f2b607.tar.gz
askbot-d26f827463f2d76423dc12288f2c3d6740f2b607.tar.bz2
askbot-d26f827463f2d76423dc12288f2c3d6740f2b607.zip
temporal commit, search is still broken
-rw-r--r--askbot/models/question.py167
-rw-r--r--askbot/models/tag.py3
-rw-r--r--askbot/search/haystack/__init__.py7
3 files changed, 176 insertions, 1 deletions
diff --git a/askbot/models/question.py b/askbot/models/question.py
index d0e5c5b2..371063eb 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -154,6 +154,13 @@ class ThreadManager(models.Manager):
def run_advanced_search(self, request_user, search_state): # TODO: !! review, fix, and write tests for this
+ if settings.ENABLE_HAYSTACK_SEARCH:
+ return self._run_advanced_haystack_search(request_user, search_state)
+ else:
+ return self._run_advanced_full_text_search(request_user, search_state)
+
+
+ def _run_advanced_full_text_search(self, request_user, search_state):
"""
all parameters are guaranteed to be clean
however may not relate to database - in that case
@@ -309,6 +316,166 @@ class ThreadManager(models.Manager):
return qs.distinct(), meta_data
+ def _run_advanced_haystack_search(self, request_user, search_state):
+ from askbot.conf import settings as askbot_settings # Avoid circular import
+ try:
+ from askbot.search.haystack import AskbotSearchQuerySet
+ qs = AskbotSearchQuerySet()
+ except ImportError, e:
+ print e.message
+ qs = self
+
+ # TODO: add a possibility to see deleted questions
+ qs = qs.filter(
+ posts__post_type='question'
+ # posts__deleted=False,
+ ) # (***) brings `askbot_post` into the SQL query, see the ordering section below
+
+ #if askbot_settings.ENABLE_CONTENT_MODERATION:
+ # qs = qs.filter(approved = True)
+
+ meta_data = {}
+
+ if search_state.stripped_query:
+ qs = self.get_for_query(search_query=search_state.stripped_query, qs=qs)
+ if search_state.query_title:
+ qs = qs.filter(title__icontains = search_state.query_title)
+ if search_state.query_users:
+ query_users = User.objects.filter(username__in=search_state.query_users)
+ if query_users:
+ qs = qs.filter(posts__post_type='question', posts__author__in=query_users) # TODO: unify with search_state.author ?
+
+ tags = search_state.unified_tags()
+ if len(tags) > 0:
+
+ if askbot_settings.TAG_SEARCH_INPUT_ENABLED:
+ #todo: this may be gone or disabled per option
+ #"tag_search_box_enabled"
+ existing_tags = set(
+ Tag.objects.filter(
+ name__in = tags
+ ).values_list(
+ 'name',
+ flat = True
+ )
+ )
+
+ non_existing_tags = set(tags) - existing_tags
+ meta_data['non_existing_tags'] = list(non_existing_tags)
+ tags = existing_tags
+ else:
+ meta_data['non_existing_tags'] = list()
+
+ #construct filter for the tag search
+ for tag in tags:
+ qs = qs.filter(tags__name=tag) # Tags or AND-ed here, not OR-ed (i.e. we fetch only threads with all tags)
+ else:
+ meta_data['non_existing_tags'] = list()
+
+ if search_state.scope == 'unanswered':
+ print 'search state unanswered'
+ qs = qs.filter(closed = False) # Do not show closed questions in unanswered section
+ if askbot_settings.UNANSWERED_QUESTION_MEANING == 'NO_ANSWERS':
+ qs = qs.filter(answer_count=0) # TODO: expand for different meanings of this
+ elif askbot_settings.UNANSWERED_QUESTION_MEANING == 'NO_ACCEPTED_ANSWERS':
+ qs = qs.filter(accepted_answer__isnull=True)
+ elif askbot_settings.UNANSWERED_QUESTION_MEANING == 'NO_UPVOTED_ANSWERS':
+ raise NotImplementedError()
+ else:
+ raise Exception('UNANSWERED_QUESTION_MEANING setting is wrong')
+
+ elif search_state.scope == 'favorite':
+ print 'favorite_filter'
+ favorite_filter = models.Q(favorited_by=request_user)
+ if 'followit' in settings.INSTALLED_APPS:
+ followed_users = request_user.get_followed_users()
+ favorite_filter |= models.Q(posts__post_type__in=('question', 'answer'), posts__author__in=followed_users)
+ qs = qs.filter(favorite_filter)
+
+ #user contributed questions & answers
+ if search_state.author:
+ try:
+ # TODO: maybe support selection by multiple authors
+ u = User.objects.get(id=int(search_state.author))
+ except User.DoesNotExist:
+ meta_data['author_name'] = None
+ else:
+ qs = qs.filter(posts__post_type__in=('question', 'answer'), posts__author=u, posts__deleted=False)
+ meta_data['author_name'] = u.username
+
+ #get users tag filters
+ if request_user and request_user.is_authenticated():
+ #mark questions tagged with interesting tags
+ #a kind of fancy annotation, would be nice to avoid it
+ interesting_tags = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'good'
+ )
+ ignored_tags = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'bad'
+ )
+ if askbot_settings.SUBSCRIBED_TAG_SELECTOR_ENABLED:
+ meta_data['subscribed_tag_names'] = Tag.objects.filter(
+ user_selections__user = request_user,
+ user_selections__reason = 'subscribed'
+ ).values_list('name', flat = True)
+
+ meta_data['interesting_tag_names'] = [tag.name for tag in interesting_tags]
+ meta_data['ignored_tag_names'] = [tag.name for tag in ignored_tags]
+
+ if request_user.display_tag_filter_strategy == const.INCLUDE_INTERESTING and (interesting_tags or request_user.has_interesting_wildcard_tags()):
+ #filter by interesting tags only
+ interesting_tag_filter = models.Q(tags__in=interesting_tags)
+ if request_user.has_interesting_wildcard_tags():
+ interesting_wildcards = request_user.interesting_tags.split()
+ extra_interesting_tags = Tag.objects.get_by_wildcards(interesting_wildcards)
+ interesting_tag_filter |= models.Q(tags__in=extra_interesting_tags)
+ qs = qs.filter(interesting_tag_filter)
+
+ # get the list of interesting and ignored tags (interesting_tag_names, ignored_tag_names) = (None, None)
+ if request_user.display_tag_filter_strategy == const.EXCLUDE_IGNORED and (ignored_tags or request_user.has_ignored_wildcard_tags()):
+ #exclude ignored tags if the user wants to
+ qs = qs.exclude(tags__in=ignored_tags)
+ if request_user.has_ignored_wildcard_tags():
+ ignored_wildcards = request_user.ignored_tags.split()
+ extra_ignored_tags = Tag.objects.get_by_wildcards(ignored_wildcards)
+ qs = qs.exclude(tags__in = extra_ignored_tags)
+
+ if askbot_settings.USE_WILDCARD_TAGS:
+ meta_data['interesting_tag_names'].extend(request_user.interesting_tags.split())
+ meta_data['ignored_tag_names'].extend(request_user.ignored_tags.split())
+
+ QUESTION_ORDER_BY_MAP = {
+ 'age-desc': '-added_at',
+ 'age-asc': 'added_at',
+ 'activity-desc': '-last_activity_at',
+ 'activity-asc': 'last_activity_at',
+ 'answers-desc': '-answer_count',
+ 'answers-asc': 'answer_count',
+ 'votes-desc': '-points',
+ 'votes-asc': 'points',
+
+ 'relevance-desc': '-relevance', # special Postgresql-specific ordering, 'relevance' quaso-column is added by get_for_query()
+ }
+ orderby = QUESTION_ORDER_BY_MAP[search_state.sort]
+ #FIXTHIS
+ #qs = qs.extra(order_by=[orderby])
+
+ # HACK: We add 'ordering_key' column as an alias and order by it, because when distict() is used,
+ # qs.extra(order_by=[orderby,]) is lost if only `orderby` column is from askbot_post!
+ # Removing distinct() from the queryset fixes the problem, but we have to use it here.
+ # UPDATE: Apparently we don't need distinct, the query don't duplicate Thread rows!
+ # qs = qs.extra(select={'ordering_key': orderby.lstrip('-')}, order_by=['-ordering_key' if orderby.startswith('-') else 'ordering_key'])
+ # qs = qs.distinct()
+
+ #FIXTHIS
+ #qs = qs.only('id', 'title', 'view_count', 'answer_count', 'last_activity_at', 'last_activity_by', 'closed', 'tagnames', 'accepted_answer')
+
+ #print qs.query
+
+ return qs.get_django_queryset(Thread), meta_data
+
def precache_view_data_hack(self, threads):
# TODO: Re-enable this when we have a good test cases to verify that it works properly.
#
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 858db2e6..cb9070a4 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -2,11 +2,12 @@ import re
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
+from django.conf import settings
from askbot.models.base import BaseQuerySetManager
from askbot import const
def tags_match_some_wildcard(tag_names, wildcard_tags):
- """Same as
+ """Same as
:meth:`~askbot.models.tag.TagQuerySet.tags_match_some_wildcard`
except it works on tag name strings
"""
diff --git a/askbot/search/haystack/__init__.py b/askbot/search/haystack/__init__.py
index 8f008a93..0bd4476f 100644
--- a/askbot/search/haystack/__init__.py
+++ b/askbot/search/haystack/__init__.py
@@ -1,5 +1,6 @@
try:
from haystack import indexes, site
+ from haystack.query import SearchQuerySet
except ImportError:
pass
@@ -27,3 +28,9 @@ class PostIndex(indexes.SearchIndex):
site.register(Post, PostIndex)
site.register(Thread, ThreadIndex)
+
+class AskbotSearchQuerySet(SearchQuerySet):
+
+ def get_django_queryset(self, model_klass):
+ id_list = [r.pk for r in self.models(model_klass)]
+ return model_klass.objects.filter(id__in=id_list)