/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.search.elasticsearch.internal.groupby; import com.liferay.portal.kernel.search.DocumentImpl; import com.liferay.portal.kernel.search.GeoDistanceSort; import com.liferay.portal.kernel.search.GroupBy; import com.liferay.portal.kernel.search.QueryConfig; import com.liferay.portal.kernel.search.SearchContext; import com.liferay.portal.kernel.search.Sort; import com.liferay.portal.kernel.search.geolocation.GeoLocationPoint; import com.liferay.portal.kernel.search.highlight.HighlightUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.search.elasticsearch.groupby.GroupByTranslator; import com.liferay.portal.search.elasticsearch.internal.pagination.Pagination; import java.util.Collection; import java.util.HashSet; import java.util.Optional; import java.util.Set; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsBuilder; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.GeoDistanceSortBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.osgi.service.component.annotations.Component; /** * @author Michael C. Han */ @Component(immediate = true, service = GroupByTranslator.class) public class DefaultGroupByTranslator implements GroupByTranslator { @Override public void translate( SearchRequestBuilder searchRequestBuilder, SearchContext searchContext, int start, int end) { GroupBy groupBy = searchContext.getGroupBy(); TermsBuilder termsBuilder = AggregationBuilders.terms( GROUP_BY_AGGREGATION_PREFIX + groupBy.getField()); termsBuilder = termsBuilder.field(groupBy.getField()); TopHitsBuilder topHitsBuilder = getTopHitsBuilder( searchContext, start, end, groupBy); termsBuilder.subAggregation(topHitsBuilder); searchRequestBuilder.addAggregation(termsBuilder); } protected void addHighlightedField( TopHitsBuilder topHitsBuilder, QueryConfig queryConfig, String fieldName) { topHitsBuilder.addHighlightedField( fieldName, queryConfig.getHighlightFragmentSize(), queryConfig.getHighlightSnippetSize()); String localizedFieldName = DocumentImpl.getLocalizedName( queryConfig.getLocale(), fieldName); topHitsBuilder.addHighlightedField( localizedFieldName, queryConfig.getHighlightFragmentSize(), queryConfig.getHighlightSnippetSize()); } protected void addHighlights( TopHitsBuilder topHitsBuilder, QueryConfig queryConfig) { if (!queryConfig.isHighlightEnabled()) { return; } for (String highlightFieldName : queryConfig.getHighlightFieldNames()) { addHighlightedField( topHitsBuilder, queryConfig, highlightFieldName); } topHitsBuilder.setHighlighterPostTags( HighlightUtil.HIGHLIGHT_TAG_CLOSE); topHitsBuilder.setHighlighterPreTags(HighlightUtil.HIGHLIGHT_TAG_OPEN); topHitsBuilder.setHighlighterRequireFieldMatch( queryConfig.isHighlightRequireFieldMatch()); } protected void addSorts(TopHitsBuilder topHitsBuilder, Sort[] sorts) { if (ArrayUtil.isEmpty(sorts)) { return; } Set<String> sortFieldNames = new HashSet<>(sorts.length); for (Sort sort : sorts) { if (sort == null) { continue; } String sortFieldName = DocumentImpl.getSortFieldName( sort, "_score"); if (sortFieldNames.contains(sortFieldName)) { continue; } sortFieldNames.add(sortFieldName); SortOrder sortOrder = SortOrder.ASC; if (sort.isReverse() || sortFieldName.equals("_score")) { sortOrder = SortOrder.DESC; } SortBuilder sortBuilder = null; if (sortFieldName.equals("_score")) { sortBuilder = SortBuilders.scoreSort(); } else if (sort.getType() == Sort.GEO_DISTANCE_TYPE) { GeoDistanceSort geoDistanceSort = (GeoDistanceSort)sort; GeoDistanceSortBuilder geoDistanceSortBuilder = SortBuilders.geoDistanceSort(sortFieldName); geoDistanceSortBuilder.geoDistance(GeoDistance.DEFAULT); for (GeoLocationPoint geoLocationPoint : geoDistanceSort.getGeoLocationPoints()) { geoDistanceSortBuilder.point( geoLocationPoint.getLatitude(), geoLocationPoint.getLongitude()); } Collection<String> geoHashes = geoDistanceSort.getGeoHashes(); if (!geoHashes.isEmpty()) { geoDistanceSort.addGeoHash( geoHashes.toArray(new String[geoHashes.size()])); } sortBuilder = geoDistanceSortBuilder; } else { FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort( sortFieldName); fieldSortBuilder.unmappedType("string"); sortBuilder = fieldSortBuilder; } sortBuilder.order(sortOrder); topHitsBuilder.addSort(sortBuilder); } } protected TopHitsBuilder getTopHitsBuilder( SearchContext searchContext, int start, int end, GroupBy groupBy) { TopHitsBuilder topHitsBuilder = AggregationBuilders.topHits( TOP_HITS_AGGREGATION_NAME); Pagination pagination = new Pagination(start, end); Optional<Integer> fromOptional; int groupByStart = groupBy.getStart(); if (groupByStart != 0) { fromOptional = Optional.of(groupByStart); } else { fromOptional = pagination.getFrom(); } fromOptional.ifPresent(topHitsBuilder::setFrom); Optional<Integer> sizeOptional; int groupBySize = groupBy.getSize(); if (groupBySize != 0) { sizeOptional = Optional.of(groupBySize); } else { sizeOptional = pagination.getSize(); } sizeOptional.ifPresent(topHitsBuilder::setSize); addHighlights(topHitsBuilder, searchContext.getQueryConfig()); addSorts(topHitsBuilder, searchContext.getSorts()); return topHitsBuilder; } }