mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-05 03:00:15 -04:00
Show reddit search "facets"
If search results contain hits from more than one subreddit, the top 20 will be selectable to click through and narrow results
This commit is contained in:
@@ -672,8 +672,8 @@ class FrontController(RedditController):
|
||||
query = "(and %s timestamp:%s..%s)" % (query, start, end)
|
||||
q = SearchQuery(query, raw_sort="-text_relevance",
|
||||
syntax="cloudsearch")
|
||||
num, t, pane = self._search(q, num=num, after=after, reverse=reverse,
|
||||
count=count)
|
||||
pane = self._search(q, num=num, after=after, reverse=reverse,
|
||||
count=count)[2]
|
||||
|
||||
return LinkInfoPage(link=article, content=pane,
|
||||
subtitle=_('related')).render()
|
||||
@@ -705,14 +705,14 @@ class FrontController(RedditController):
|
||||
"""Search reddits by title and description."""
|
||||
q = SubredditSearchQuery(query)
|
||||
|
||||
num, t, spane = self._search(q, num=num, reverse=reverse,
|
||||
after=after, count=count,
|
||||
skip_deleted_authors=False)
|
||||
results, etime, spane = self._search(q, num=num, reverse=reverse,
|
||||
after=after, count=count,
|
||||
skip_deleted_authors=False)
|
||||
|
||||
res = SubredditsPage(content=spane,
|
||||
prev_search=query,
|
||||
elapsed_time=t,
|
||||
num_results=num,
|
||||
elapsed_time=etime,
|
||||
num_results=results.hits,
|
||||
# update if we ever add sorts
|
||||
search_params={},
|
||||
title=_("search results"),
|
||||
@@ -747,8 +747,9 @@ class FrontController(RedditController):
|
||||
cleanup_message = None
|
||||
try:
|
||||
q = SearchQuery(query, site, sort, syntax=syntax)
|
||||
num, t, spane = self._search(q, num=num, after=after,
|
||||
reverse = reverse, count = count)
|
||||
results, etime, spane = self._search(q, num=num, after=after,
|
||||
reverse=reverse,
|
||||
count=count)
|
||||
except InvalidQuery:
|
||||
# strip the query down to a whitelist
|
||||
cleaned = re.sub("[^\w\s]+", " ", query)
|
||||
@@ -756,12 +757,14 @@ class FrontController(RedditController):
|
||||
|
||||
# if it was nothing but mess, we have to stop
|
||||
if not cleaned.strip():
|
||||
num, t, spane = 0, 0, []
|
||||
results, etime, spane = 0, 0, []
|
||||
cleanup_message = strings.completely_invalid_search_query
|
||||
else:
|
||||
q = SearchQuery(cleaned, site, sort)
|
||||
num, t, spane = self._search(q, num=num, after=after,
|
||||
reverse=reverse, count=count)
|
||||
results, etime, spane = self._search(q, num=num,
|
||||
after=after,
|
||||
reverse=reverse,
|
||||
count=count)
|
||||
cleanup_message = strings.invalid_search_query % {
|
||||
"clean_query": cleaned
|
||||
}
|
||||
@@ -770,14 +773,17 @@ class FrontController(RedditController):
|
||||
self.search_help_page
|
||||
}
|
||||
|
||||
res = SearchPage(_('search results'), query, t, num, content=spane,
|
||||
res = SearchPage(_('search results'), query, etime, results.hits,
|
||||
content=spane,
|
||||
nav_menus=[SearchSortMenu(default=sort)],
|
||||
search_params=dict(sort=sort),
|
||||
infotext=cleanup_message,
|
||||
simple=False, site=c.site,
|
||||
restrict_sr=restrict_sr,
|
||||
syntax=syntax,
|
||||
converted_data=q.converted_data
|
||||
converted_data=q.converted_data,
|
||||
facets=results.subreddit_facets,
|
||||
sort=sort,
|
||||
).render()
|
||||
|
||||
return res
|
||||
@@ -805,7 +811,7 @@ class FrontController(RedditController):
|
||||
return self.search_fail(e)
|
||||
timing = time_module.time() - builder.start_time
|
||||
|
||||
return builder.total_num, timing, res
|
||||
return builder.results, timing, res
|
||||
|
||||
@validate(VAdmin(),
|
||||
comment = VCommentByID('comment_id'))
|
||||
|
||||
@@ -477,18 +477,29 @@ def test_run_srs(*sr_names):
|
||||
|
||||
### Query Code ###
|
||||
class Results(object):
|
||||
__slots__ = ["docs", "hits", "facets"]
|
||||
|
||||
def __init__(self, docs, hits, facets):
|
||||
self.docs = docs
|
||||
self.hits = hits
|
||||
self.facets = facets
|
||||
self._facets = facets
|
||||
self._subreddits = []
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r, %r)' % (self.__class__.__name__,
|
||||
self.docs,
|
||||
self.hits,
|
||||
self.facets)
|
||||
self._facets)
|
||||
|
||||
@property
|
||||
def subreddit_facets(self):
|
||||
'''Filter out subreddits that the user isn't allowed to see'''
|
||||
if not self._subreddits and 'reddit' in self._facets:
|
||||
sr_facets = [(sr['value'], sr['count']) for sr in
|
||||
self._facets['reddit']]
|
||||
srs_by_name = Subreddit._by_name([sr[0] for sr in sr_facets])
|
||||
self._subreddits = [sr for sr in sr_facets
|
||||
if srs_by_name[sr[0]].can_view(c.user)]
|
||||
|
||||
return self._subreddits
|
||||
|
||||
|
||||
def _to_fn(cls, id_):
|
||||
@@ -505,7 +516,7 @@ INVALID_QUERY_CODES = ('CS-UnknownFieldInMatchExpression',
|
||||
'CS-InvalidMatchSetExpression',
|
||||
'CS-IncorrectFieldTypeInMatchExpression',
|
||||
'CS-InvalidMatchSetExpression',)
|
||||
def basic_query(query=None, bq=None, facets=("reddit",), facet_count=10,
|
||||
def basic_query(query=None, bq=None, facets=("reddit",), facet_count=20,
|
||||
size=1000, start=0, rank="hot", return_fields=None,
|
||||
record_stats=False, search_api=None):
|
||||
if search_api is None:
|
||||
@@ -619,7 +630,7 @@ class CloudSearchQuery(object):
|
||||
|
||||
results = self._run(_update=_update)
|
||||
|
||||
docs, hits, facets = results.docs, results.hits, results.facets
|
||||
docs, hits, facets = results.docs, results.hits, results._facets
|
||||
|
||||
after_docs = r2utils.get_after(docs, after, num, reverse=reverse)
|
||||
|
||||
|
||||
@@ -822,9 +822,9 @@ class SearchPage(BoringPage):
|
||||
extra_page_classes = ['search-page']
|
||||
|
||||
def __init__(self, pagename, prev_search, elapsed_time,
|
||||
num_results, search_params = {},
|
||||
simple=False, restrict_sr = False, site=None,
|
||||
syntax=None, converted_data=None,
|
||||
num_results, search_params={},
|
||||
simple=False, restrict_sr=False, site=None,
|
||||
syntax=None, converted_data=None, facets={}, sort=None,
|
||||
*a, **kw):
|
||||
self.searchbar = SearchBar(prev_search=prev_search,
|
||||
elapsed_time=elapsed_time,
|
||||
@@ -832,7 +832,8 @@ class SearchPage(BoringPage):
|
||||
search_params=search_params,
|
||||
show_feedback=True, site=site,
|
||||
simple=simple, restrict_sr=restrict_sr,
|
||||
syntax=syntax, converted_data=converted_data)
|
||||
syntax=syntax, converted_data=converted_data,
|
||||
facets=facets, sort=sort)
|
||||
BoringPage.__init__(self, pagename, robots='noindex', *a, **kw)
|
||||
|
||||
def content(self):
|
||||
@@ -1745,7 +1746,8 @@ class SearchBar(Templated):
|
||||
def __init__(self, header=None, num_results=0, prev_search='',
|
||||
elapsed_time=0, search_params={}, show_feedback=False,
|
||||
simple=False, restrict_sr=False, site=None, syntax=None,
|
||||
subreddit_search=False, converted_data=None, **kw):
|
||||
subreddit_search=False, converted_data=None, facets={},
|
||||
sort=None, **kw):
|
||||
if header is None:
|
||||
header = _("previous search")
|
||||
self.header = header
|
||||
@@ -1764,7 +1766,8 @@ class SearchBar(Templated):
|
||||
simple=simple, restrict_sr=restrict_sr,
|
||||
site=site, syntax=syntax,
|
||||
converted_data=converted_data,
|
||||
subreddit_search=subreddit_search)
|
||||
subreddit_search=subreddit_search, facets=facets,
|
||||
sort=sort)
|
||||
|
||||
class Frame(Wrapped):
|
||||
"""Frameset for the FrameToolbar used when a user hits /tb/. The
|
||||
|
||||
@@ -513,6 +513,18 @@ def add_attr(attrs, kind, label=None, link=None, cssclass=None, symbol=None):
|
||||
attrs.append( (priority, symbol, cssclass, label, link, img) )
|
||||
|
||||
|
||||
def search_url(query, subreddit, restrict_sr="off", sort=None):
|
||||
import urllib
|
||||
url_query = {"q": query}
|
||||
if restrict_sr:
|
||||
url_query["restrict_sr"] = restrict_sr
|
||||
if sort:
|
||||
url_query["sort"] = sort
|
||||
path = "/r/%s/search?" % subreddit if subreddit else "/search?"
|
||||
path += urllib.urlencode(url_query)
|
||||
return path
|
||||
|
||||
|
||||
def format_number(number, locale=None):
|
||||
if not locale:
|
||||
locale = c.locale
|
||||
|
||||
@@ -474,9 +474,9 @@ class SearchBuilder(IDBuilder):
|
||||
|
||||
self.start_time = time.time()
|
||||
|
||||
search = self.query.run()
|
||||
names = list(search.docs)
|
||||
self.total_num = search.hits
|
||||
self.results = self.query.run()
|
||||
names = list(self.results.docs)
|
||||
self.total_num = self.results.hits
|
||||
|
||||
after = self.after._fullname if self.after else None
|
||||
|
||||
|
||||
@@ -1813,6 +1813,29 @@ label + #moresearchinfo {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.searchfacets {
|
||||
overflow: auto;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.searchfacets.title {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.searchfacets.list {
|
||||
margin: 0px 0px 0px 10px;
|
||||
}
|
||||
|
||||
li.searchfacet {
|
||||
display: inline-block;
|
||||
width: 15em;
|
||||
}
|
||||
|
||||
.facet.count {
|
||||
font-weight: bold;
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
/* login, register */
|
||||
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<%!
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.pages import SearchForm
|
||||
from r2.lib.template_helpers import static
|
||||
from r2.lib.template_helpers import static, search_url
|
||||
%>
|
||||
|
||||
<% isize = 60 %>
|
||||
@@ -72,4 +72,18 @@
|
||||
simple=thing.simple, restrict_sr=thing.restrict_sr,
|
||||
syntax=thing.syntax)}
|
||||
</div>
|
||||
</div>
|
||||
%if thing.facets and len(thing.facets) > 1:
|
||||
<div class="searchfacets">
|
||||
<h4 class="searchfacets title">${_("too many results? narrow it down to a subreddit!")}</h4>
|
||||
<ol class="searchfacets list">
|
||||
%for facet in thing.facets:
|
||||
<% subreddit, count = facet %>
|
||||
<li class="searchfacet reddit">
|
||||
<a class="facet title word" href="${search_url(thing.prev_search, subreddit, restrict_sr='on', sort=thing.sort)}">/r/${subreddit}</a>
|
||||
<span class="facet count number">(${count})</span>
|
||||
</li>
|
||||
%endfor
|
||||
</ol>
|
||||
</div>
|
||||
%endif
|
||||
</div>
|
||||
Reference in New Issue
Block a user