Repository: django-wiki/django-wiki

Issue:
Hierarchical search
Is there a way to search only BELOW the current article?


Discussion:
Good question!
No, but *someone* could work it out :)
We can leave this open as a feature request if there's an interest?

Resolving patch:
diff --git a/src/wiki/urls.py b/src/wiki/urls.py
--- a/src/wiki/urls.py
+++ b/src/wiki/urls.py
@@ -200,6 +200,9 @@ def get_article_path_urls(self):
             url(r'^(?P<path>.+/|)_dir/$',
                 self.article_dir_view_class.as_view(),
                 name='dir'),
+            url(r'^(?P<path>.+/|)_search/$',
+                get_class_from_str(self.search_view_class).as_view(),
+                name='search'),
             url(r'^(?P<path>.+/|)_settings/$',
                 self.article_settings_view_class.as_view(),
                 name='settings'),
diff --git a/src/wiki/views/article.py b/src/wiki/views/article.py
--- a/src/wiki/views/article.py
+++ b/src/wiki/views/article.py
@@ -670,6 +670,7 @@ class SearchView(ListView):
     context_object_name = "articles"
 
     def dispatch(self, request, *args, **kwargs):
+        self.urlpath = None
         # Do not allow anonymous users to search if they cannot read content
         if request.user.is_anonymous() and not settings.ANONYMOUS:
             return redirect(settings.LOGIN_URL)
@@ -683,7 +684,17 @@ def dispatch(self, request, *args, **kwargs):
     def get_queryset(self):
         if not self.query:
             return models.Article.objects.none().order_by('-current_revision__created')
-        articles = models.Article.objects.filter(
+        articles = models.Article.objects
+        path = self.kwargs.get('path', None)
+        if path:
+            try:
+                self.urlpath = models.URLPath.get_by_path(path)
+                article_ids = self.urlpath.get_descendants(
+                    include_self=True).values_list('article_id')
+                articles = articles.filter(id__in=article_ids)
+            except (NoRootURL, models.URLPath.DoesNotExist):
+                raise Http404
+        articles = articles.filter(
             Q(current_revision__title__icontains=self.query) |
             Q(current_revision__content__icontains=self.query))
         if not permissions.can_moderate(
@@ -696,6 +707,7 @@ def get_context_data(self, **kwargs):
         kwargs = super(SearchView, self).get_context_data(**kwargs)
         kwargs['search_form'] = self.search_form
         kwargs['search_query'] = self.query
+        kwargs['urlpath'] = self.urlpath
         return kwargs
 
 


Test patch:
diff --git a/tests/core/test_views.py b/tests/core/test_views.py
--- a/tests/core/test_views.py
+++ b/tests/core/test_views.py
@@ -432,6 +432,42 @@ def test_empty_query_string(self):
         response = self.client.get(resolve_url('wiki:search'), {'q': ''})
         self.assertFalse(response.context['articles'])
 
+    def test_hierarchy_search(self):
+
+        c = self.client
+
+        c.post(
+            resolve_url('wiki:create', path=''),
+            {'title': 'Test0', 'slug': 'test0', 'content': 'Content test0'}
+        )
+        c.post(
+            resolve_url('wiki:create', path=''),
+            {'title': 'Test1', 'slug': 'test1', 'content': 'Content test1'}
+        )
+        c.post(
+            resolve_url('wiki:create', path='test0/'),
+            {'title': 'Subtest0', 'slug': 'subtest0', 'content': 'Content test2'}
+        )
+
+        response = c.get(resolve_url('wiki:search', path='test0/'), {'q': 'Content test'})
+        articles = response.context['articles']
+
+        def contains_title(articles, title):
+            return any(article.current_revision.title == title for article in articles)
+
+        self.assertTrue(contains_title(articles, 'Test0'))
+        self.assertFalse(contains_title(articles, 'Test1'))
+        self.assertTrue(contains_title(articles, 'Subtest0'))
+
+    def test_hierarchy_search_404(self):
+
+        c = self.client
+
+        response = c.get(resolve_url(
+            'wiki:search', path='test0/'), {'q': 'Content test'})
+
+        self.assertEqual(response.status_code, 404)
+
 
 class DeletedListViewTest(RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase):