Skip to content

Commit

Permalink
HSEARCH-2662 Elasticsearch: do not search on all indexes when searchi…
Browse files Browse the repository at this point in the history
…ng on a dynamically sharded index with no shard
  • Loading branch information
yrodiere authored and Sanne committed Apr 13, 2017
1 parent b99c995 commit 4e8b0ca
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 41 deletions.
Expand Up @@ -164,7 +164,13 @@ public String getQueryString() {

@Override
public DocumentExtractor queryDocumentExtractor() {
return new ElasticsearchScrollAPIDocumentExtractor();
IndexSearcher searcher = getOrCreateSearcher();
if ( searcher != null ) {
return new ElasticsearchScrollAPIDocumentExtractor( searcher );
}
else {
return EmptyDocumentExtractor.get();
}
}

SearchResult getSearchResult() {
Expand Down Expand Up @@ -279,46 +285,84 @@ protected Set<String> getSupportedProjectionConstants() {
}

private void execute() {
searcher = new IndexSearcher();

searchResult = searcher.search();
searcher = getOrCreateSearcher();
if ( searcher != null ) {
searchResult = searcher.search();
}
else {
searchResult = EmptySearchResult.get();
}
resultSize = searchResult.getTotalHitCount();
}

private IndexSearcher getOrCreateSearcher() {
if ( searcher != null ) {
return searcher;
}

Map<String, Class<?>> entityTypesByName = new HashMap<>();
Set<String> indexNames = new HashSet<>();
Iterable<Class<?>> queriedEntityTypes = getQueriedEntityTypes();

for ( Class<?> queriedEntityType : queriedEntityTypes ) {
entityTypesByName.put( queriedEntityType.getName(), queriedEntityType );

EntityIndexBinding binding = extendedIntegrator.getIndexBinding( queriedEntityType );
IndexManager[] indexManagers = binding.getIndexManagers();

for ( IndexManager indexManager : indexManagers ) {
if ( !( indexManager instanceof ElasticsearchIndexManager ) ) {
throw LOG.cannotRunEsQueryTargetingEntityIndexedWithNonEsIndexManager(
queriedEntityType,
rawSearchPayload.toString()
);
}

ElasticsearchIndexManager esIndexManager = (ElasticsearchIndexManager) indexManager;
indexNames.add( esIndexManager.getActualIndexName() );
}
}

if ( indexNames.isEmpty() ) {
/*
* In this case we cannot send a request to Elasticsearch,
* because by default it will query all indexes.
*/
return null;
}

this.searcher = new IndexSearcher( entityTypesByName, indexNames );
return searcher;
}

private Iterable<Class<?>> getQueriedEntityTypes() {
if ( indexedTargetedEntities == null || indexedTargetedEntities.isEmpty() ) {
return extendedIntegrator.getIndexBindings().keySet();
}
else {
return indexedTargetedEntities;
}
}

/**
* Stores all information required to execute the query: query string, relevant indices, query parameters, ...
*/
private class IndexSearcher {
private final Map<String, Class<?>> entityTypesByName;
private final Set<String> indexNames;

private final ElasticsearchQueryOptions queryOptions;
private final Map<String, Class<?>> entityTypesByName = new HashMap<>();
private final Map<Class<?>, FieldProjection> idProjectionByEntityType = new HashMap<>();
private final Map<Class<?>, FieldProjection[]> fieldProjectionsByEntityType = new HashMap<>();
private final Set<String> indexNames = new HashSet<>();
private final JsonObject payload;

private IndexSearcher() {
JsonArray typeFilters = new JsonArray();
Iterable<Class<?>> queriedEntityTypes = getQueriedEntityTypes();

for ( Class<?> queriedEntityType : queriedEntityTypes ) {
entityTypesByName.put( queriedEntityType.getName(), queriedEntityType );

EntityIndexBinding binding = extendedIntegrator.getIndexBinding( queriedEntityType );
IndexManager[] indexManagers = binding.getIndexManagers();
private IndexSearcher(Map<String, Class<?>> entityTypesByName, Set<String> indexNames) {
this.entityTypesByName = entityTypesByName;
this.indexNames = indexNames;

for ( IndexManager indexManager : indexManagers ) {
if ( !( indexManager instanceof ElasticsearchIndexManager ) ) {
throw LOG.cannotRunEsQueryTargetingEntityIndexedWithNonEsIndexManager(
queriedEntityType,
rawSearchPayload.toString()
);
}

ElasticsearchIndexManager esIndexManager = (ElasticsearchIndexManager) indexManager;
indexNames.add( esIndexManager.getActualIndexName() );
}

typeFilters.add( getEntityTypeFilter( queriedEntityType ) );
JsonArray typeFilters = new JsonArray();
for ( String typeName : entityTypesByName.keySet() ) {
typeFilters.add( getEntityTypeFilter( typeName ) );
}

try ( ServiceReference<ElasticsearchService> elasticsearchService =
Expand Down Expand Up @@ -404,9 +448,9 @@ private JsonObject getFilteredQuery(JsonElement originalQuery, JsonArray typeFil
return JsonBuilder.object().add( "bool", boolBuilder.build() ).build();
}

private JsonObject getEntityTypeFilter(Class<?> queriedEntityType) {
private JsonObject getEntityTypeFilter(String name) {
JsonObject value = new JsonObject();
value.addProperty( "value", queriedEntityType.getName() );
value.addProperty( "value", name );

JsonObject type = new JsonObject();
type.add( "type", value );
Expand All @@ -428,15 +472,6 @@ private JsonObject getTenantIdFilter() {
return tenantFilter;
}

private Iterable<Class<?>> getQueriedEntityTypes() {
if ( indexedTargetedEntities == null || indexedTargetedEntities.isEmpty() ) {
return extendedIntegrator.getIndexBindings().keySet();
}
else {
return indexedTargetedEntities;
}
}

private void addProjections(JsonBuilder.Object payloadBuilder) {
boolean includeAllSource = false;
JsonBuilder.Array builder = JsonBuilder.array();
Expand Down Expand Up @@ -1167,8 +1202,8 @@ private class ElasticsearchScrollAPIDocumentExtractor implements DocumentExtract
private Integer totalResultCount;
private final Window<EntityInfo> results;

private ElasticsearchScrollAPIDocumentExtractor() {
searcher = new IndexSearcher();
private ElasticsearchScrollAPIDocumentExtractor(IndexSearcher searcher) {
this.searcher = searcher;
queryIndexLimit = ElasticsearchHSQueryImpl.this.maxResults == null
? null : ElasticsearchHSQueryImpl.this.firstResult + ElasticsearchHSQueryImpl.this.maxResults;
ElasticsearchQueryOptions queryOptions = searcher.getQueryOptions();
Expand Down
@@ -0,0 +1,59 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.elasticsearch.impl;

import java.io.IOException;

import org.apache.lucene.search.TopDocs;
import org.hibernate.search.elasticsearch.logging.impl.Log;
import org.hibernate.search.query.engine.spi.DocumentExtractor;
import org.hibernate.search.query.engine.spi.EntityInfo;
import org.hibernate.search.util.logging.impl.LoggerFactory;


/**
* @author Yoann Rodiere
*/
class EmptyDocumentExtractor implements DocumentExtractor {

private static final Log log = LoggerFactory.make( Log.class );

private static final DocumentExtractor INSTANCE = new EmptyDocumentExtractor();

public static DocumentExtractor get() {
return INSTANCE;
}

private EmptyDocumentExtractor() {
// Use get()
}

@Override
public EntityInfo extract(int index) throws IOException {
throw new IndexOutOfBoundsException( "This document extractor is empty" );
}

@Override
public int getFirstIndex() {
return 0;
}

@Override
public int getMaxIndex() {
return -1;
}

@Override
public void close() {
// Nothing to do
}

@Override
public TopDocs getTopDocs() {
throw log.documentExtractorTopDocsUnsupported();
}
}
@@ -0,0 +1,59 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.elasticsearch.impl;

import org.hibernate.search.elasticsearch.work.impl.SearchResult;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/**
* @author Yoann Rodiere
*/
public class EmptySearchResult implements SearchResult {

private static final EmptySearchResult INSTANCE = new EmptySearchResult();

public static SearchResult get() {
return INSTANCE;
}

private EmptySearchResult() {
// Use get()
}

@Override
public JsonArray getHits() {
return new JsonArray();
}

@Override
public int getTotalHitCount() {
return 0;
}

@Override
public JsonObject getAggregations() {
return new JsonObject();
}

@Override
public int getTook() {
return 0;
}

@Override
public boolean getTimedOut() {
return false;
}

@Override
public String getScrollId() {
return null;
}

}

0 comments on commit 4e8b0ca

Please sign in to comment.