def suggestions(request): """A simple search view that returns OpenSearch suggestions.""" mimetype = 'application/x-suggestions+json' term = request.GET.get('q') if not term: return HttpResponseBadRequest(mimetype=mimetype) site = Site.objects.get_current() locale = locale_or_default(request.locale) try: query = dict(('%s__text' % field, term) for field in Document.get_query_fields()) wiki_s = (Document.search() .filter(document_is_archived=False) .filter(document_locale=locale) .values_dict('document_title', 'url') .query(or_=query)[:5]) query = dict(('%s__text' % field, term) for field in Question.get_query_fields()) question_s = (Question.search() .filter(question_has_helpful=True) .values_dict('question_title', 'url') .query(or_=query)[:5]) results = list(chain(question_s, wiki_s)) except (ESTimeoutError, ESMaxRetryError, ESException): # If we have ES problems, we just send back an empty result # set. results = [] urlize = lambda r: u'https://%s%s' % (site, r['url']) titleize = lambda r: (r['document_title'] if 'document_title' in r else r['question_title']) data = [term, [titleize(r) for r in results], [], [urlize(r) for r in results]] return HttpResponse(json.dumps(data), mimetype=mimetype)
def suggestions(request): """A simple search view that returns OpenSearch suggestions.""" mimetype = 'application/x-suggestions+json' term = request.GET.get('q') if not term: return HttpResponseBadRequest(mimetype=mimetype) site = Site.objects.get_current() locale = locale_or_default(request.LANGUAGE_CODE) try: query = dict(('%s__text' % field, term) for field in Document.get_query_fields()) wiki_s = (Document.search() .filter(document_is_archived=False) .filter(document_locale=locale) .values_dict('document_title', 'url') .query(or_=query)[:5]) query = dict(('%s__text' % field, term) for field in Question.get_query_fields()) question_s = (Question.search() .filter(question_has_helpful=True) .values_dict('question_title', 'url') .query(or_=query)[:5]) results = list(chain(question_s, wiki_s)) except (ESTimeoutError, ESMaxRetryError, ESException): # If we have ES problems, we just send back an empty result # set. results = [] urlize = lambda r: u'https://%s%s' % (site, r['url']) titleize = lambda r: (r['document_title'] if 'document_title' in r else r['question_title']) data = [term, [titleize(r) for r in results], [], [urlize(r) for r in results]] return HttpResponse(json.dumps(data), mimetype=mimetype)
def _search_suggestions(request, text, locale, tags, product_slugs): """Return an iterable of the most relevant wiki pages and questions. :arg text: full text to search on :arg locale: locale to limit to :arg tags: list of tags to filter questions on :arg product_slugs: list of product slugs to filter articles on (["desktop", "mobile", ...]) Items are dicts of:: { 'type': 'search_summary': 'title': 'url': 'object': } :returns: up to 3 wiki pages, then up to 3 questions. """ # TODO: this can be reworked to pull data from ES rather than # hit the db. question_s = Question.search() wiki_s = Document.search() # Max number of search results per type. WIKI_RESULTS = QUESTIONS_RESULTS = 3 default_categories = settings.SEARCH_DEFAULT_CATEGORIES # Apply product filters if product_slugs: wiki_s = wiki_s.filter(document_product__in=product_slugs) if tags: question_s = question_s.filter(question_tag__in=tags) results = [] try: query = dict(('%s__text' % field, text) for field in Document.get_query_fields()) raw_results = ( wiki_s.filter(document_locale=locale, document_category__in=default_categories) .query(or_=query) .values_dict('id')[:WIKI_RESULTS]) for r in raw_results: try: doc = (Document.objects.select_related('current_revision') .get(pk=r['id'])) results.append({ 'search_summary': clean_excerpt( doc.current_revision.summary), 'url': doc.get_absolute_url(), 'title': doc.title, 'type': 'document', 'object': doc, }) except Document.DoesNotExist: pass # Note: Questions app is en-US only. query = dict(('%s__text' % field, text) for field in Question.get_query_fields()) raw_results = (question_s.query(or_=query) .values_dict('id')[:QUESTIONS_RESULTS]) for r in raw_results: try: q = Question.objects.get(pk=r['id']) results.append({ 'search_summary': clean_excerpt(q.content[0:500]), 'url': q.get_absolute_url(), 'title': q.title, 'type': 'question', 'object': q, 'is_solved': q.is_solved, 'num_answers': q.num_answers, 'num_votes': q.num_votes, 'num_votes_past_week': q.num_votes_past_week }) except Question.DoesNotExist: pass except (ESTimeoutError, ESMaxRetryError, ESException) as exc: if isinstance(exc, ESTimeoutError): statsd.incr('questions.suggestions.timeouterror') elif isinstance(exc, ESMaxRetryError): statsd.incr('questions.suggestions.maxretryerror') elif isinstance(exc, ESException): statsd.incr('questions.suggestions.elasticsearchexception') log.debug(exc) return results
def _search_suggestions(request, text, locale, product_slugs): """Return an iterable of the most relevant wiki pages and questions. :arg text: full text to search on :arg locale: locale to limit to :arg product_slugs: list of product slugs to filter articles on (["desktop", "mobile", ...]) Items are dicts of:: { 'type': 'search_summary': 'title': 'url': 'object': } :returns: up to 3 wiki pages, then up to 3 questions. """ # TODO: this can be reworked to pull data from ES rather than # hit the db. question_s = Question.search() wiki_s = Document.search() # Max number of search results per type. WIKI_RESULTS = QUESTIONS_RESULTS = 3 default_categories = settings.SEARCH_DEFAULT_CATEGORIES # Apply product filters if product_slugs: wiki_s = wiki_s.filter(product__in=product_slugs) question_s = question_s.filter(product__in=product_slugs) results = [] try: # Search for relevant KB documents. query = dict(('%s__text' % field, text) for field in Document.get_query_fields()) query.update(dict(('%s__text_phrase' % field, text) for field in Document.get_query_fields())) filter = F() filter |= F(document_locale=locale) filter |= F(document_locale=settings.WIKI_DEFAULT_LANGUAGE) filter &= F(document_category__in=default_categories) filter &= F(document_is_archived=False) raw_results = ( wiki_s.filter(filter) .query(or_=query) .values_dict('id')[:WIKI_RESULTS]) for r in raw_results: try: doc = (Document.objects.select_related('current_revision') .get(pk=r['id'])) results.append({ 'search_summary': clean_excerpt( doc.current_revision.summary), 'url': doc.get_absolute_url(), 'title': doc.title, 'type': 'document', 'object': doc, }) except Document.DoesNotExist: pass # Search for relevant questions. query = dict(('%s__text' % field, text) for field in Question.get_query_fields()) query.update(dict(('%s__text_phrase' % field, text) for field in Question.get_query_fields())) max_age = int(time.time()) - settings.SEARCH_DEFAULT_MAX_QUESTION_AGE # Filter questions by language. Questions should be either in English # or in the locale's language. This is because we have some questions # marked English that are really in other languages. The assumption # being that if a native speakers submits a query in given language, # the items that are written in that language will automatically match # better, so questions incorrectly marked as english can be found too. question_filter = F(question_locale=locale) question_filter |= F(question_locale=settings.WIKI_DEFAULT_LANGUAGE) question_filter &= F(updated__gte=max_age) raw_results = (question_s .query(or_=query) .filter(question_filter) .values_dict('id')[:QUESTIONS_RESULTS]) for r in raw_results: try: q = Question.objects.get(pk=r['id']) results.append({ 'search_summary': clean_excerpt(q.content[0:500]), 'url': q.get_absolute_url(), 'title': q.title, 'type': 'question', 'object': q, 'is_solved': q.is_solved, 'num_answers': q.num_answers, 'num_votes': q.num_votes, 'num_votes_past_week': q.num_votes_past_week }) except Question.DoesNotExist: pass except (ESTimeoutError, ESMaxRetryError, ESException) as exc: if isinstance(exc, ESTimeoutError): statsd.incr('questions.suggestions.timeouterror') elif isinstance(exc, ESMaxRetryError): statsd.incr('questions.suggestions.maxretryerror') elif isinstance(exc, ESException): statsd.incr('questions.suggestions.elasticsearchexception') log.debug(exc) return results