Beispiel #1
0
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']),
    })
Beispiel #2
0
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),
    })
Beispiel #3
0
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']),
    })
Beispiel #4
0
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],
    })
Beispiel #5
0
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
Beispiel #6
0
    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