summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/doc/source/solr.rst113
-rw-r--r--askbot/management/commands/askbot_rebuild_index.py26
-rw-r--r--askbot/models/question.py36
-rw-r--r--askbot/search/haystack/__init__.py108
-rw-r--r--askbot/search/haystack/routers.py20
-rw-r--r--askbot/search/haystack/searchquery.py53
-rw-r--r--askbot/search_indexes.py4
-rw-r--r--askbot/setup_templates/settings.py2
-rw-r--r--askbot/setup_templates/settings.py.mustache2
-rw-r--r--askbot/startup_procedures.py12
-rw-r--r--askbot/templates/search/indexes/askbot/post_text.txt1
-rw-r--r--askbot/templates/search/indexes/askbot/thread_text.txt6
-rw-r--r--askbot/templates/search/indexes/auth/user_text.txt5
-rw-r--r--askbot/tests/haystack_search_tests.py3
14 files changed, 316 insertions, 75 deletions
diff --git a/askbot/doc/source/solr.rst b/askbot/doc/source/solr.rst
new file mode 100644
index 00000000..0b76aa94
--- /dev/null
+++ b/askbot/doc/source/solr.rst
@@ -0,0 +1,113 @@
+.. _solr:
+
+===========================================================
+Installing Apache Solr with Apache Tomcat 7 in Ubuntu 12.04
+===========================================================
+
+
+This document describes the process of instalation of Apache Solr search engine in Ubuntu Server 12.04
+for askbot use. To follow this steps you must have already askbot installed and running.
+
+Getting the requirements
+------------------------
+
+We need the following packages installed::
+
+ sudo apt-get install tomcat7 tomcat7-admin
+
+We need to download Apache Solr from the `official site <http://lucene.apache.org/solr/downloads.html>`_::
+
+ wget http://www.bizdirusa.com/mirrors/apache/lucene/solr/3.6.2/apache-solr-3.6.2.tgz
+
+Then we decompress it::
+
+ tar -xzf solr-3.6.2.tgz
+
+Setting up Tomcat
+-----------------
+
+After installing tomcat there are some configuration required to make it work. First we are going to add
+Tomcat users. Edit /etc/tomcat7/tomcat-users.xml and add the following::
+
+ <?xml version='1.0' encoding='utf-8'?>
+ <tomcat-users>
+ <role rolename="manager"/>
+ <role rolename="admin"/>
+ <role rolename="admin-gui"/>
+ <role rolename="manager-gui"/>
+ <user username="tomcat" password="tomcat" roles="manager,admin,manager-gui,admin-gui"/>
+ </tomcat-users>
+
+This will allow you to connect to the web management interface. After doing it restart the service:
+
+ service tomcat7 restart
+
+To make see if it works go to: http://youripaddress:8080/manager it will ask for your tomcat user password
+described in the tomcat-users.xml
+
+Installing Solr under Tomcat
+----------------------------
+
+Extract the solr tar archive from the previous download::
+
+ tar -xzf solr-4.3.0.tgz
+
+Copy the example/ directory from the source to /opt/solr/. Open the file /opt/solr/example/conf/solrconfig.xml
+and Modify the dataDir parameter as::
+
+ <dataDir>${solr.data.dir:/opt/solr/example/solr/data}</dataDir>
+
+Copy the .war file in dist directory to /opt/solr::
+
+ cp dist/apache-solr-3.6.2.war /opt/solr
+
+Create solr.xml inside of /etc/tomcat/Catalina/localhost/ with the following contents::
+
+ <?xml version="1.0" encoding="utf-8"?>
+ <Context docBase="/opt/solr/apache-solr-3.6.2.war" debug="0" crossContext="true">
+ <Environment name="solr/home" type="java.lang.String" value="/opt/solr/example/solr" override="true"/>
+ </Context>
+
+Restart tomcat server::
+
+ service tomcat7 restart
+
+By now you should be able to see the "solr" application in the tomcat manager and also access it in /solr/admin.
+
+
+Configuring Askbot with Solr
+----------------------------
+
+Open settings.py file and configure the following::
+
+ ENABLE_HAYSTACK_SEARCH = True
+ HAYSTACK_SEARCH_ENGINE = 'solr'
+ HAYSTACK_SOLR_URL = 'http://127.0.0.1:8080/solr'
+
+After that create the solr schema and store the output to your solr instalation::
+
+ python manage.py build_solr_schema > /opt/solr/example/solr/conf/schema.xml
+
+Restart tomcat server::
+
+ service tomcat7 restart
+
+Build the Index for the first time::
+
+ python manage.py rebuild_index
+
+The output should be something like::
+
+ All documents removed.
+ Indexing 43 people.
+ Indexing 101 posts.
+ Indexing 101 threads.
+
+You must be good to go after this, just restart the askbot application and test the search with haystack and solr
+
+
+Keeping the index fresh
+-----------------------
+
+For this we recommend to use one of haystack `third party apps <http://django-haystack.readthedocs.org/en/latest/other_apps.html>`_ that use celery,
+plese check this `link <http://django-haystack.readthedocs.org/en/latest/other_apps.html>`_ for more info.
diff --git a/askbot/management/commands/askbot_rebuild_index.py b/askbot/management/commands/askbot_rebuild_index.py
new file mode 100644
index 00000000..204f3b56
--- /dev/null
+++ b/askbot/management/commands/askbot_rebuild_index.py
@@ -0,0 +1,26 @@
+from optparse import make_option
+
+from django.core.management import call_command
+from django.utils.translation import activate as activate_language
+from django.core.management.base import BaseCommand
+from django.conf import settings
+
+try:
+ from haystack.management.commands.clear_index import Command as ClearCommand
+ from haystack.management.commands.update_index import Command as UpdateCommand
+ haystack_option_list = [option for option in UpdateCommand.base_options if option.get_opt_string() != '--verbosity'] + \
+ [option for option in ClearCommand.base_options if not option.get_opt_string() in ['--using', '--verbosity']]
+except ImportError:
+ haystack_option_list = []
+
+class Command(BaseCommand):
+ help = "Completely rebuilds the search index by removing the old data and then updating."
+ base_options = [make_option("-l", "--language", action="store", type="string", dest="language",
+ help='Language to user, in language code format'),]
+ option_list = list(BaseCommand.option_list) + haystack_option_list + base_options
+
+ def handle(self, **options):
+ lang_code = options.get('language', settings.LANGUAGE_CODE.lower())
+ activate_language(lang_code)
+ options['using'] = ['default_%s' % lang_code[:2],]
+ call_command('rebuild_index', **options)
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 3dd9fc6b..5c6814bb 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -48,24 +48,30 @@ class ThreadQuerySet(models.query.QuerySet):
todo: possibly add tags
todo: implement full text search on relevant fields
"""
- db_engine_name = askbot.get_database_engine_name()
- filter_parameters = {'deleted': False}
- if 'postgresql_psycopg2' in db_engine_name:
- from askbot.search import postgresql
- return postgresql.run_title_search(
- self, search_query
- ).filter(
- **filter_parameters
- ).order_by('-relevance')
- elif 'mysql' in db_engine_name and mysql.supports_full_text_search():
- filter_parameters['title__search'] = search_query
+
+ if getattr(django_settings, 'ENABLE_HAYSTACK_SEARCH', False):
+ from askbot.search.haystack import AskbotSearchQuerySet
+ hs_qs = AskbotSearchQuerySet().filter(content=search_query).models(self.model)
+ return hs_qs.get_django_queryset()
else:
- filter_parameters['title__icontains'] = search_query
+ db_engine_name = askbot.get_database_engine_name()
+ filter_parameters = {'deleted': False}
+ if 'postgresql_psycopg2' in db_engine_name:
+ from askbot.search import postgresql
+ return postgresql.run_title_search(
+ self, search_query
+ ).filter(
+ **filter_parameters
+ ).order_by('-relevance')
+ elif 'mysql' in db_engine_name and mysql.supports_full_text_search():
+ filter_parameters['title__search'] = search_query
+ else:
+ filter_parameters['title__icontains'] = search_query
- if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False):
- filter_parameters['language_code'] = get_language()
+ if getattr(django_settings, 'ASKBOT_MULTILINGUAL', False):
+ filter_parameters['language_code'] = get_language()
- return self.filter(**filter_parameters)
+ return self.filter(**filter_parameters)
class ThreadManager(BaseQuerySetManager):
diff --git a/askbot/search/haystack/__init__.py b/askbot/search/haystack/__init__.py
index 71f04d00..45a037c9 100644
--- a/askbot/search/haystack/__init__.py
+++ b/askbot/search/haystack/__init__.py
@@ -1,59 +1,57 @@
-try:
- from haystack import indexes, site
- from haystack.query import SearchQuerySet
- from askbot.models import Post, Thread, User
-
-
- class ThreadIndex(indexes.SearchIndex):
- text = indexes.CharField(document=True, use_template=True)
- title = indexes.CharField(model_attr='title')
- post_text = indexes.CharField(model_attr='posts__text__search')
-
- def index_queryset(self):
- return Thread.objects.filter(posts__deleted=False)
-
- def prepare(self, obj):
- self.prepared_data = super(ThreadIndex, self).prepare(object)
-
- self.prepared_data['tags'] = [tag.name for tag in objects.tags.all()]
-
- class PostIndex(indexes.SearchIndex):
- text = indexes.CharField(document=True, use_template=True)
- post_text = indexes.CharField(model_attr='text')
- author = indexes.CharField(model_attr='user')
- thread_id = indexes.CharField(model_attr='thread')
-
- def index_queryset(self):
- return Post.objects.filter(deleted=False)
+from django.conf import settings
+from django.utils.translation import get_language
- class UserIndex(indexes.SearchIndex):
- text = indexes.CharField(document=True, use_template=True)
-
- def index_queryset(self):
- return User.objects.all()
-
- site.register(Post, PostIndex)
- site.register(Thread, ThreadIndex)
- site.register(User, UserIndex)
-
- class AskbotSearchQuerySet(SearchQuerySet):
-
- def get_django_queryset(self, model_klass=Thread):
- '''dirty hack because models() method from the
- SearchQuerySet does not work </3'''
- id_list = []
- for r in self:
- if r.model_name in ['thread','post'] \
- and model_klass._meta.object_name.lower() == 'thread':
- if getattr(r, 'thread_id'):
- id_list.append(r.thread_id)
- else:
- id_list.append(r.pk)
- elif r.model_name == model_klass._meta.object_name.lower():
- #FIXME: add a highlight here?
- id_list.append(r.pk)
-
- return model_klass.objects.filter(id__in=set(id_list))
+from haystack import indexes
+try:
+ from searchquery import AskbotSearchQuerySet
except:
pass
+
+class ThreadIndex(indexes.SearchIndex, indexes.Indexable):
+ text = indexes.CharField(document=True, use_template=True)
+ title = indexes.CharField()
+ tags = indexes.MultiValueField()
+
+ def get_model(self):
+ from askbot.models import Thread
+ return Thread
+
+ def index_queryset(self, using=None):
+ if getattr(settings, 'ASKBOT_MULTILINGUAL', True):
+ lang_code = get_language()[:2]
+ return self.get_model().objects.filter(language_code=lang_code,
+ posts__deleted=False)
+ else:
+ return self.get_model().objects.filter(posts__deleted=False)
+
+ def prepare_tags(self, obj):
+ return [tag.name for tag in obj.tags.all()]
+
+class PostIndex(indexes.SearchIndex, indexes.Indexable):
+ text = indexes.CharField(document=True, use_template=True)
+ post_text = indexes.CharField(model_attr='text')
+ author = indexes.CharField()
+ thread_id = indexes.IntegerField(model_attr='thread__pk')
+
+ def get_model(self):
+ from askbot.models import Post
+ return Post
+
+ def index_queryset(self, using=None):
+ if getattr(settings, 'ASKBOT_MULTILINGUAL', True):
+ lang_code = get_language()[:2]
+ return self.get_model().objects.filter(language_code=lang_code,
+ deleted=False)
+ else:
+ return self.get_model().objects.filter(deleted=False)
+
+class UserIndex(indexes.SearchIndex, indexes.Indexable):
+ text = indexes.CharField(document=True, use_template=True)
+
+ def get_model(self):
+ from askbot.models import User
+ return User
+
+ def index_queryset(self, using=None):
+ return self.get_model().objects.all()
diff --git a/askbot/search/haystack/routers.py b/askbot/search/haystack/routers.py
new file mode 100644
index 00000000..7b7e0a4b
--- /dev/null
+++ b/askbot/search/haystack/routers.py
@@ -0,0 +1,20 @@
+from django.utils.translation import get_language
+
+from haystack.routers import BaseRouter
+from haystack.constants import DEFAULT_ALIAS
+
+class LanguageRouter(BaseRouter):
+
+ def for_read(self, **hints):
+ from django.conf import settings
+ if getattr(settings, 'ASKBOT_MULTILINGUAL'):
+ return 'default_' + get_language()[:2]
+ else:
+ return DEFAULT_ALIAS
+
+ def for_write(self, **hints):
+ from django.conf import settings
+ if getattr(settings, 'ASKBOT_MULTILINGUAL'):
+ return 'default_' + get_language()[:2]
+ else:
+ return DEFAULT_ALIAS
diff --git a/askbot/search/haystack/searchquery.py b/askbot/search/haystack/searchquery.py
new file mode 100644
index 00000000..116f4990
--- /dev/null
+++ b/askbot/search/haystack/searchquery.py
@@ -0,0 +1,53 @@
+from askbot.models import Thread, User
+from haystack.query import SearchQuerySet
+
+class AskbotSearchQuerySet(SearchQuerySet):
+
+ def _determine_backend(self):
+ '''This is a hack somehow connection_router got wrong values
+ from setting and did not loaded the LanguageRouter'''
+
+ from haystack import connections, connection_router
+ # A backend has been manually selected. Use it instead.
+ if self._using is not None:
+ self.query = connections[self._using].get_query()
+ return
+
+ # No backend, so rely on the routers to figure out what's right.
+ hints = {}
+
+ if self.query:
+ hints['models'] = self.query.models
+
+ backend_alias = connection_router.for_read(**hints)
+
+ if isinstance(backend_alias, (list, tuple)) and len(backend_alias):
+ # We can only effectively read from one engine.
+ backend_alias = backend_alias[0]
+
+ # The ``SearchQuery`` might swap itself out for a different variant
+ # here.
+ if self.query:
+ self.query = self.query.using(backend_alias)
+ else:
+ self.query = connections[backend_alias].get_query()
+
+ def get_django_queryset(self, model_klass=Thread):
+ '''dirty hack because models() method from the
+ SearchQuerySet does not work </3'''
+ id_list = []
+ for r in self:
+ if r.model_name in ['thread','post'] \
+ and model_klass._meta.object_name.lower() == 'thread':
+ if getattr(r, 'thread_id'):
+ id_list.append(r.thread_id)
+ else:
+ id_list.append(r.pk)
+ elif r.model_name == model_klass._meta.object_name.lower():
+ #FIXME: add a highlight here?
+ id_list.append(r.pk)
+
+ if model_klass == User:
+ return model_klass.objects.filter(id__in=set(id_list))
+ else:
+ return model_klass.objects.filter(id__in=set(id_list))
diff --git a/askbot/search_indexes.py b/askbot/search_indexes.py
new file mode 100644
index 00000000..f2674996
--- /dev/null
+++ b/askbot/search_indexes.py
@@ -0,0 +1,4 @@
+from django.conf import settings
+
+if getattr(settings, 'ENABLE_HAYSTACK_SEARCH'):
+ from askbot.search.haystack import UserIndex, ThreadIndex, PostIndex
diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py
index 1427e506..e5b21d92 100644
--- a/askbot/setup_templates/settings.py
+++ b/askbot/setup_templates/settings.py
@@ -258,6 +258,8 @@ RECAPTCHA_USE_SSL = True
#HAYSTACK_SETTINGS
ENABLE_HAYSTACK_SEARCH = False
HAYSTACK_SITECONF = 'askbot.search.haystack'
+#if you set this to True it can fail
+HAYSTACK_ENABLE_REGISTRATIONS = False
#more information
#http://django-haystack.readthedocs.org/en/v1.2.7/settings.html
HAYSTACK_SEARCH_ENGINE = 'simple'
diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache
index f30297d7..f59dd735 100644
--- a/askbot/setup_templates/settings.py.mustache
+++ b/askbot/setup_templates/settings.py.mustache
@@ -256,6 +256,8 @@ RECAPTCHA_USE_SSL = True
#HAYSTACK_SETTINGS
ENABLE_HAYSTACK_SEARCH = False
HAYSTACK_SITECONF = 'askbot.search.haystack'
+#if you set this to True it can fail
+HAYSTACK_ENABLE_REGISTRATIONS = False
#more information
#http://django-haystack.readthedocs.org/en/v1.2.7/settings.html
HAYSTACK_SEARCH_ENGINE = 'simple'
diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py
index b7ad47bf..8f4ea2ac 100644
--- a/askbot/startup_procedures.py
+++ b/askbot/startup_procedures.py
@@ -622,6 +622,12 @@ def test_haystack():
if not hasattr(django_settings, 'HAYSTACK_SITECONF'):
message = 'Please add HAYSTACK_SITECONF = "askbot.search.haystack"'
errors.append(message)
+ if not hasattr(django_settings, 'HAYSTACK_ENABLE_REGISTRATIONS'):
+ message = 'Please add "HAYSTACK_ENABLE_REGISTRATIONS = False"'
+ errors.append(message)
+ elif getattr(django_settings, 'HAYSTACK_ENABLE_REGISTRATIONS'):
+ message = 'Please set "HAYSTACK_ENABLE_REGISTRATIONS = False"'
+ errors.append(message)
footer = 'Please refer to haystack documentation at http://django-haystack.readthedocs.org/en/v1.2.7/settings.html#haystack-search-engine'
print_errors(errors, footer=footer)
@@ -919,7 +925,7 @@ def run_startup_tests():
test_compressor()
test_custom_user_profile_tab()
test_group_messaging()
- test_haystack()
+ #test_haystack()
test_jinja2()
test_longerusername()
test_new_skins()
@@ -965,10 +971,6 @@ def run_startup_tests():
'value': True,
'message': 'Please add: RECAPTCHA_USE_SSL = True'
},
- 'HAYSTACK_SITECONF': {
- 'value': 'askbot.search.haystack',
- 'message': 'Please add: HAYSTACK_SITECONF = "askbot.search.haystack"'
- }
})
settings_tester.run()
if 'manage.py test' in ' '.join(sys.argv):
diff --git a/askbot/templates/search/indexes/askbot/post_text.txt b/askbot/templates/search/indexes/askbot/post_text.txt
new file mode 100644
index 00000000..b373ccf7
--- /dev/null
+++ b/askbot/templates/search/indexes/askbot/post_text.txt
@@ -0,0 +1 @@
+{{ object.text }}
diff --git a/askbot/templates/search/indexes/askbot/thread_text.txt b/askbot/templates/search/indexes/askbot/thread_text.txt
new file mode 100644
index 00000000..0db5dba0
--- /dev/null
+++ b/askbot/templates/search/indexes/askbot/thread_text.txt
@@ -0,0 +1,6 @@
+{{ object.title }}
+{{ object.tags }}
+
+{% for tag in object.tags.all() %}
+{{ tag.name }}
+{% endfor %}
diff --git a/askbot/templates/search/indexes/auth/user_text.txt b/askbot/templates/search/indexes/auth/user_text.txt
new file mode 100644
index 00000000..b0baa3a6
--- /dev/null
+++ b/askbot/templates/search/indexes/auth/user_text.txt
@@ -0,0 +1,5 @@
+{{ object.username }}
+{{ object.first_name }}
+{{ object.last_name }}
+{{ object.email }}
+{{ object.full_name }}
diff --git a/askbot/tests/haystack_search_tests.py b/askbot/tests/haystack_search_tests.py
index 7a8bfcfd..c256c533 100644
--- a/askbot/tests/haystack_search_tests.py
+++ b/askbot/tests/haystack_search_tests.py
@@ -12,7 +12,9 @@ class HaystackSearchTests(AskbotTestCase):
"""
def setUp(self):
self._old_value = getattr(settings, 'ENABLE_HAYSTACK_SEARCH', False)
+ self._old_search_engine = getattr(settings, 'HAYSTACK_SEARCH_ENGINE', 'simple')
setattr(settings, "ENABLE_HAYSTACK_SEARCH", True)
+ setattr(settings, "HAYSTACK_SEARCH_ENGINE", 'simple')
self.user = self.create_user(username='gepeto')
self.other_user = self.create_user(username = 'pinocho')
@@ -53,6 +55,7 @@ class HaystackSearchTests(AskbotTestCase):
def tearDown(self):
setattr(settings, "ENABLE_HAYSTACK_SEARCH", self._old_value)
+ setattr(settings, "HAYSTACK_SEARCH_ENGINE", self._old_search_engine)
@skipIf('haystack' not in settings.INSTALLED_APPS,
'Haystack not setup')