def suggest(request): if request.data and request.GET: raise GenericAPIException( 400, 'Put all parameters either in the querystring or the HTTP request body.' ) serializer = SuggestSerializer(data=(request.data or request.GET)) if not serializer.is_valid(): raise GenericAPIException(400, serializer.errors) searcher = (es_utils.AnalyzerS().es( urls=settings.ES_URLS, timeout=settings.ES_TIMEOUT, use_ssl=settings.ES_USE_SSL, http_auth=settings.ES_HTTP_AUTH, connection_class=RequestsHttpConnection).indexes( es_utils.read_index('default'))) data = serializer.validated_data return Response({ 'questions': _question_suggestions(searcher, data['q'], data['locale'], data['product'], data['max_questions']), 'documents': _document_suggestions(searcher, data['q'], data['locale'], data['product'], data['max_documents']), })
def suggest(request): text = request.body or request.GET.get('q') locale = request.GET.get('locale', settings.WIKI_DEFAULT_LANGUAGE) product = request.GET.get('product') max_questions = request.GET.get('max_questions', 10) max_documents = request.GET.get('max_documents', 10) errors = {} try: max_questions = int(max_questions) except ValueError: errors['max_questions'] = 'This field must be an integer.' try: max_documents = int(max_documents) except ValueError: errors['max_documents'] = 'This field must be an integer.' if text is None: errors['q'] = 'This field is required.' if product is not None and not Product.objects.filter( slug=product).exists(): errors['product'] = 'Could not find product with slug "{0}".'.format( product) if errors: raise GenericAPIException(400, errors) searcher = (es_utils.AnalyzerS().es(urls=settings.ES_URLS).indexes( es_utils.read_index('default'))) return Response({ 'questions': _question_suggestions(searcher, text, locale, product, max_questions), 'documents': _document_suggestions(searcher, text, locale, product, max_documents), })
def suggest(request): if request.DATA and request.GET: raise GenericAPIException( 400, 'Put all parameters either in the querystring or the HTTP request body.' ) serializer = SuggestSerializer(data=(request.DATA or request.GET)) if not serializer.is_valid(): raise GenericAPIException(400, serializer.errors) searcher = (es_utils.AnalyzerS().es(urls=settings.ES_URLS).indexes( es_utils.read_index('default'))) data = serializer.object return Response({ 'questions': _question_suggestions(searcher, data['q'], data['locale'], data['product'], data['max_questions']), 'documents': _document_suggestions(searcher, data['q'], data['locale'], data['product'], data['max_documents']), })
def suggest(request): text = request.body or request.GET.get('q') locale = request.GET.get('locale', settings.WIKI_DEFAULT_LANGUAGE) product = request.GET.get('product') max_questions = request.GET.get('max_questions', 10) max_documents = request.GET.get('max_documents', 10) errors = {} try: max_questions = int(max_questions) except ValueError: errors['max_questions'] = 'This field must be an integer.' try: max_documents = int(max_documents) except ValueError: errors['max_documents'] = 'This field must be an integer.' if text is None: errors['q'] = 'This field is required.' if product is not None and not Product.objects.filter( slug=product).exists(): errors['product'] = 'Could not find product with slug "{0}".'.format( product) if errors: raise GenericAPIException(400, errors) wiki_f = es_utils.F( model='wiki_document', document_category__in=settings.SEARCH_DEFAULT_CATEGORIES, document_locale=locale, document_is_archived=False) questions_f = es_utils.F(model='questions_question', question_is_archived=False, question_is_locked=False, question_has_helpful=True) if product is not None: wiki_f &= es_utils.F(product=product) questions_f &= es_utils.F(product=product) mapping_types = [QuestionMappingType, DocumentMappingType] query_fields = itertools.chain( *[cls.get_query_fields() for cls in mapping_types]) query = {} for field in query_fields: for query_type in ['match', 'match_phrase']: key = '{0}__{1}'.format(field, query_type) query[key] = text # Transform query to be locale aware. query = es_utils.es_query_with_analyzer(query, locale) searcher = (es_utils.AnalyzerS().es(urls=settings.ES_URLS).indexes( es_utils.read_index('default')).doctypes( *[cls.get_mapping_type_name() for cls in mapping_types]).filter(wiki_f | questions_f).query( should=True, **query)) documents = [] questions = [] for result in searcher[:(max_documents + max_questions) * 2]: if result['model'] == 'wiki_document': documents.append({ 'title': result['document_title'], 'slug': result['document_slug'], 'summary': result['document_summary'], }) elif result['model'] == 'questions_question': questions.append({ 'id': result['id'], 'title': result['question_title'], }) if len(documents) >= max_documents and len(questions) >= max_questions: break return Response({ 'questions': questions[:max_questions], 'documents': documents[:max_documents], })
def generate_simple_search(search_form, language, with_highlights=False): """Generates an S given a form :arg search_form: a validated SimpleSearch form :arg language: the language code :arg with_highlights: whether or not to ask for highlights :returns: a fully formed S """ # We use a regular S here because we want to search across # multiple doctypes. searcher = (es_utils.AnalyzerS().es( urls=settings.ES_URLS, timeout=settings.ES_TIMEOUT, use_ssl=settings.ES_USE_SSL, http_auth=settings.ES_HTTP_AUTH, connection_class=RequestsHttpConnection, ).indexes(es_utils.read_index("default"))) cleaned = search_form.cleaned_data doctypes = [] final_filter = es_utils.F() cleaned_q = cleaned["q"] products = cleaned["product"] # Handle wiki filters if cleaned["w"] & constants.WHERE_WIKI: wiki_f = es_utils.F( model="wiki_document", document_category__in=settings.SEARCH_DEFAULT_CATEGORIES, document_locale=language, document_is_archived=False, ) for p in products: wiki_f &= es_utils.F(product=p) doctypes.append(DocumentMappingType.get_mapping_type_name()) final_filter |= wiki_f # Handle question filters if cleaned["w"] & constants.WHERE_SUPPORT: question_f = es_utils.F(model="questions_question", question_is_archived=False, question_has_helpful=True) for p in products: question_f &= es_utils.F(product=p) doctypes.append(QuestionMappingType.get_mapping_type_name()) final_filter |= question_f # Build a filter for those filters and add the other bits to # finish the search searcher = searcher.doctypes(*doctypes) searcher = searcher.filter(final_filter) if cleaned["explain"]: searcher = searcher.explain() if with_highlights: # Set up the highlights. Show the entire field highlighted. searcher = searcher.highlight( "question_content", # support forum "document_summary", # kb pre_tags=["<b>"], post_tags=["</b>"], number_of_fragments=0, ) searcher = apply_boosts(searcher) # Build the query query_fields = chain(*[ cls.get_query_fields() for cls in [DocumentMappingType, QuestionMappingType] ]) query = {} # Create match and match_phrase queries for every field # we want to search. for field in query_fields: for query_type in ["match", "match_phrase"]: query["%s__%s" % (field, query_type)] = cleaned_q # Transform the query to use locale aware analyzers. query = es_utils.es_query_with_analyzer(query, language) searcher = searcher.query(should=True, **query) return searcher
def get_data(self, request): search_form = self.form_class(request.GET) if not search_form.is_valid(): raise GenericAPIException(status.HTTP_400_BAD_REQUEST, _('Invalid search data.')) language = locale_or_default( request.GET.get('language', request.LANGUAGE_CODE)) lang = language.lower() if settings.LANGUAGES_DICT.get(lang): lang_name = settings.LANGUAGES_DICT[lang] else: lang_name = '' page = max(smart_int(request.GET.get('page')), 1) offset = (page - 1) * settings.SEARCH_RESULTS_PER_PAGE searcher = (es_utils.AnalyzerS().es(urls=settings.ES_URLS).indexes( es_utils.read_index('default'))) doctypes = self.get_doctypes() searcher = searcher.doctypes(*doctypes) filters = self.get_filters() searcher = searcher.filter(filters) # Add the simple string query. cleaned_q = search_form.cleaned_data.get('query') if cleaned_q: query_fields = self.get_query_fields() query = {} # Create a simple_query_search query for every field # we want to search. for field in query_fields: query['%s__sqs' % field] = cleaned_q # Transform the query to use locale aware analyzers. query = es_utils.es_query_with_analyzer(query, language) searcher = searcher.query(should=True, **query) try: num_results = min(searcher.count(), settings.SEARCH_MAX_RESULTS) results_per_page = settings.SEARCH_RESULTS_PER_PAGE # If we know there aren't any results, let's cheat and in # doing that, not hit ES again. if num_results == 0: searcher = [] else: # TODO - Can ditch the ComposedList here, but we need # something that paginate can use to figure out the paging. documents = ComposedList() documents.set_count(('results', searcher), num_results) # Get the documents we want to show and add them to # docs_for_page documents = documents[offset:offset + results_per_page] if len(documents) == 0: # If the user requested a page that's beyond the # pagination, then documents is an empty list and # there are no results to show. searcher = [] else: bounds = documents[0][1] searcher = searcher[bounds[0]:bounds[1]] results = [] for i, doc in enumerate(searcher): rank = i + offset result = self.format_result(doc) result['url'] = doc['url'] result['rank'] = rank result['score'] = doc.es_meta.score result['explanation'] = escape( format_explanation(doc.es_meta.explanation)) result['id'] = doc['id'] results.append(result) except es_utils.ES_EXCEPTIONS: raise GenericAPIException(status.HTTP_503_SERVICE_UNAVAILABLE, _('Search Unavailable')) data = { 'num_results': num_results, 'results': results, 'lang_name': lang_name, } if not results: data['message'] = _('No pages matched the search criteria') return data