/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you 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 org.elasticsearch.action.preparespec; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.script.SharedMethods; import org.elasticsearch.search.SearchExtRegistry; import org.elasticsearch.search.SearchRequestParsers; import org.elasticsearch.search.aggregations.AggregatorParsers; import org.elasticsearch.search.suggest.Suggesters; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; public class TransportPrepareSpecAction extends HandledTransportAction<PrepareSpecRequest, PrepareSpecResponse> { private Client client; private final IndicesQueriesRegistry queryRegistry; private final AggregatorParsers aggParsers; private final Suggesters suggesters; private final SearchExtRegistry searchExtRegistry; private final ParseFieldMatcher parseFieldMatcher; @Inject public TransportPrepareSpecAction(Settings settings, ThreadPool threadPool, TransportService transportService, ActionFilters actionFilters, IndicesQueriesRegistry queryRegistry, SearchRequestParsers searchRequestParsers, SearchExtRegistry searchExtRegistry, IndexNameExpressionResolver indexNameExpressionResolver, Client client) { super(settings, PrepareSpecAction.NAME, threadPool, transportService, actionFilters, indexNameExpressionResolver, PrepareSpecRequest::new); this.client = client; this.queryRegistry = queryRegistry; this.aggParsers = searchRequestParsers.aggParsers; this.suggesters = searchRequestParsers.suggesters; this.searchExtRegistry = searchExtRegistry; this.parseFieldMatcher = new ParseFieldMatcher(settings); } @Override protected void doExecute(final PrepareSpecRequest request, final ActionListener<PrepareSpecResponse> listener) { Tuple<Boolean, List<FieldSpecRequest>> fieldSpecRequests = null; try { fieldSpecRequests = parseFieldSpecRequests(queryRegistry, aggParsers, suggesters, searchExtRegistry, parseFieldMatcher, request.source()); } catch (IOException e) { listener.onFailure(e); } final FieldSpecActionListener fieldSpecActionListener = new FieldSpecActionListener(fieldSpecRequests.v2().size(), listener, fieldSpecRequests.v1()); for (final FieldSpecRequest fieldSpecRequest : fieldSpecRequests.v2()) { fieldSpecRequest.process(fieldSpecActionListener, client); } } static Tuple<Boolean, List<FieldSpecRequest>> parseFieldSpecRequests(IndicesQueriesRegistry queryRegistry, AggregatorParsers aggParsers, Suggesters suggesters, SearchExtRegistry searchExtRegistry, ParseFieldMatcher parseFieldMatcher, String source) throws IOException { List<FieldSpecRequest> fieldSpecRequests = new ArrayList<>(); Map<String, Object> parsedSource = SharedMethods.getSourceAsMap(source); if (parsedSource.get("features") == null) { throw new ElasticsearchException("reatures are missing in prepare spec request"); } boolean sparse = getSparse(parsedSource.get("sparse")); @SuppressWarnings("unchecked") ArrayList<Map<String, Object>> actualFeatures = (ArrayList<Map<String, Object>>) parsedSource.get("features"); for (Map<String, Object> field : actualFeatures) { String type = (String) field.remove("type"); if (type == null) { throw new ElasticsearchException("type parameter is missing in prepare spec request"); } if (type.equals("string")) { fieldSpecRequests.add(StringFieldSpecRequestFactory.createStringFieldSpecRequest(queryRegistry, aggParsers, suggesters, parseFieldMatcher, searchExtRegistry, field)); } else { throw new UnsupportedOperationException("I am working as quick as I can! But I have not done it for " + type + " yet."); } } return new Tuple<>(sparse, fieldSpecRequests); } public static boolean getSparse(Object sparse) { if (sparse == null) { return false; } if (sparse instanceof Boolean) { return (Boolean) sparse; } if (sparse instanceof String) { if (sparse.equals("false")) { return false; } if (sparse.equals("true")) { return true; } } throw new IllegalStateException("don't know what sparse: " + sparse + " means!"); } public static class FieldSpecActionListener implements ActionListener<FieldSpec> { private final int numResponses; private ActionListener<PrepareSpecResponse> listener; private final boolean sparse; private int currentResponses; final List<FieldSpec> fieldSpecs = new ArrayList<>(); public FieldSpecActionListener(int numResponses, ActionListener<PrepareSpecResponse> listener, boolean sparse) { this.numResponses = numResponses; this.listener = listener; this.sparse = sparse; } @Override public void onResponse(FieldSpec fieldSpec) { fieldSpecs.add(fieldSpec); currentResponses++; if (currentResponses == numResponses) { try { int length = 0; for (FieldSpec fS : fieldSpecs) { length += fS.getLength(); } listener.onResponse(new PrepareSpecResponse(createSpecSource(fieldSpecs, sparse, length).bytes(), length)); } catch (IOException e) { listener.onFailure(e); } } } public static XContentBuilder createSpecSource(List<FieldSpec> fieldSpecs, boolean sparse, int length) throws IOException { XContentBuilder sourceBuilder = jsonBuilder(); sourceBuilder.startObject(); sourceBuilder.field("sparse", sparse); sourceBuilder.startArray("features"); for (FieldSpec fieldSpec : fieldSpecs) { fieldSpec.toXContent(sourceBuilder, ToXContent.EMPTY_PARAMS); } sourceBuilder.endArray(); sourceBuilder.field("length", Integer.toString(length)); sourceBuilder.endObject(); return sourceBuilder; } @Override public void onFailure(Exception throwable) { currentResponses = numResponses; listener.onFailure(throwable); } } }