Package ru.org.linux.search

Source Code of ru.org.linux.search.SearchViewer

/*
* Copyright 1998-2014 Linux.org.ru
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/

package ru.org.linux.search;

import com.google.common.base.Strings;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import ru.org.linux.user.User;

import java.util.ArrayList;
import java.util.List;

import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.aggregations.AggregationBuilders.*;

public class SearchViewer {
  public static final int MESSAGE_FRAGMENT = 500;

  private static final int TOPIC_BOOST = 3;
  private static final int RECENT_BOOST = 2;

  public enum SearchRange {
    ALL(null, "темы и комментарии"),
    TOPICS("false", "только темы"),
    COMMENTS("true", "только комментарии");

    private final String param;
    private final String title;

    SearchRange(String param, String title) {
      this.param = param;
      this.title = title;
    }

    private String getValue() {
      return param;
    }

    private String getColumn() {
      return "is_comment";
    }

    public String getTitle() {
      return title;
    }
  }

  public enum SearchInterval {
    MONTH("now/h-1M", "месяц"),
    THREE_MONTH("now/d-3M", "три месяца"),
    YEAR("now/d-1y", "год"),
    THREE_YEAR("now/w-3y", "три года"),
    ALL(null, "весь период");

    private final String range;
    private final String title;

    SearchInterval(String range, String title) {
      this.range = range;
      this.title = title;
    }

    private String getRange() {
      return range;
    }

    public String getTitle() {
      return title;
    }

    private String getColumn() {
      return "postdate";
    }
  }

  public enum SearchOrder {
    RELEVANCE("по релевантности", "_score", SortOrder.DESC),
    DATE("по дате: от новых к старым", "postdate", SortOrder.DESC),
    DATE_OLD_TO_NEW("по дате: от старых к новым", "postdate", SortOrder.ASC);

    private final String name;
    private final String param;
    private final SortOrder order;

    SearchOrder(String name, String param, SortOrder order) {
      this.name = name;
      this.param = param;
      this.order = order;
    }

    public String getName() {
      return name;
    }

    private String getColumn() {
      return param;
    }
  }

  public static final int SEARCH_ROWS = 25;

  private final SearchRequest query;

  public SearchViewer(SearchRequest query) {
    this.query = query;
  }

  private QueryBuilder processQueryString(String queryText) {
    if (queryText.isEmpty()) {
      return matchAllQuery();
    } else {
      CommonTermsQueryBuilder esQuery = commonTerms("_all", queryText);
      esQuery.lowFreqMinimumShouldMatch("2");

      MatchQueryBuilder phraseQuery = matchPhraseQuery("_all", queryText);
      phraseQuery.setLenient(true);

      BoolQueryBuilder boolQuery = boolQuery();
      boolQuery.should(esQuery);
      boolQuery.should(phraseQuery);

      return boolQuery;
    }
  }

  private QueryBuilder boost(QueryBuilder query) {
    FunctionScoreQueryBuilder booster = functionScoreQuery(query);

    booster.add(termFilter("is_comment", "false"), ScoreFunctionBuilders.factorFunction(TOPIC_BOOST));
    booster.add(rangeFilter("postdate").gte("now/d-3y"), ScoreFunctionBuilders.factorFunction(RECENT_BOOST));

    return booster;
  }

  public SearchResponse performSearch(Client client) {
    SearchRequestBuilder request = client.prepareSearch(SearchQueueListener.MESSAGES_INDEX);

    request.setTypes(SearchQueueListener.MESSAGES_TYPE);

    request.addFields(
            "title",
            "topic_title",
            "author",
            "postdate",
            "topic_id",
            "section",
            "message",
            "group",
            "is_comment",
            "tag"
    );

    QueryBuilder esQuery = processQueryString(this.query.getQ());

    request.setSize(SEARCH_ROWS);
    request.setFrom(this.query.getOffset());

    BoolFilterBuilder queryFilter = boolFilter();

    if (this.query.getRange().getValue()!=null) {
      queryFilter.must(termFilter(query.getRange().getColumn(), query.getRange().getValue()));
    }

    if (this.query.getInterval().getRange()!=null) {
      RangeFilterBuilder dateFilter = rangeFilter(query.getInterval().getColumn());

      dateFilter.from(this.query.getInterval().getRange());

      queryFilter.must(dateFilter);
    }

    if (this.query.getUser() != null) {
      User user = this.query.getUser();

      if (this.query.isUsertopic()) {
        queryFilter.must(termFilter("topic_author", user.getNick()));
      } else {
        queryFilter.must(termFilter("author", user.getNick()));
      }
    }

    if (!queryFilter.hasClauses()) {
      request.setQuery(boost(esQuery));
    } else {
      QueryBuilder rootQuery = filteredQuery(esQuery, queryFilter);

      request.setQuery(boost(rootQuery));
    }

    String section = this.query.getSection();
    List<FilterBuilder> postFilters = new ArrayList<>();

    if (!Strings.isNullOrEmpty(section)) {
      postFilters.add(termFilter("section", this.query.getSection()));
    }

    request.addAggregation(
            filter("sections").subAggregation(
                    terms("sections").field("section").size(0).subAggregation(
                            terms("groups").field("group").size(0)
                    )
            ).filter(matchAllFilter())
    );

    request.addAggregation(significantTerms("tags").field("tag").minDocCount(30));

    if (!Strings.isNullOrEmpty(this.query.getGroup())) {
      postFilters.add(termFilter("group", this.query.getGroup()));
    }

    if (!postFilters.isEmpty()) {
      request.setPostFilter(andFilters(postFilters));
    }

    request.addSort(query.getSort().getColumn(), query.getSort().order);

    setupHighlight(request);

    // TODO use Async
    return request.execute().actionGet();
  }

  private FilterBuilder andFilters(List<FilterBuilder> filters) {
    if (filters.size()==1) {
      return filters.get(0);
    } else {
      BoolFilterBuilder postRoot = boolFilter();

      for (FilterBuilder filter : filters) {
        postRoot.must(filter);
      }

      return postRoot;
    }
  }

  private void setupHighlight(SearchRequestBuilder request) {
    HighlightBuilder.Field title = new HighlightBuilder.Field("title");
    title.numOfFragments(0);
    request.addHighlightedField(title);

    HighlightBuilder.Field topicTitle = new HighlightBuilder.Field("topic_title");
    topicTitle.numOfFragments(0);
    request.addHighlightedField(topicTitle);

    HighlightBuilder.Field message = new HighlightBuilder.Field("message");
    message.numOfFragments(1);
    message.fragmentSize(MESSAGE_FRAGMENT);
    request.addHighlightedField(message);

    request.setHighlighterEncoder("html");
    request.setHighlighterPreTags("<em class=search-hl>");
    request.setHighlighterPostTags("</em>");
  }
}
TOP

Related Classes of ru.org.linux.search.SearchViewer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.