/* * 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.search.fetch; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.rest.RestRequest; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Context used to fetch the {@code stored_fields}. */ public class StoredFieldsContext implements Writeable { public static final String _NONE_ = "_none_"; private final List<String> fieldNames; private boolean fetchFields; private StoredFieldsContext(boolean fetchFields) { this.fetchFields = fetchFields; this.fieldNames = null; } private StoredFieldsContext(List<String> fieldNames) { Objects.requireNonNull(fieldNames, "fieldNames must not be null"); this.fetchFields = true; this.fieldNames = new ArrayList<>(fieldNames); } public StoredFieldsContext(StoredFieldsContext other) { this.fetchFields = other.fetchFields(); if (other.fieldNames() != null) { this.fieldNames = new ArrayList<>(other.fieldNames()); } else { this.fieldNames = null; } } public StoredFieldsContext(StreamInput in) throws IOException { this.fetchFields = in.readBoolean(); if (fetchFields) { this.fieldNames = (List<String>) in.readGenericValue(); } else { this.fieldNames = null; } } @Override public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(fetchFields); if (fetchFields) { out.writeGenericValue(fieldNames); } } /** * Gets the field names to load and return as part of the search request. */ public List<String> fieldNames() { return fieldNames; } /** * Adds the field names {@code fieldNames} to the list of fields to load. */ public StoredFieldsContext addFieldNames(List<String> fieldNames) { if (fetchFields == false || fieldNames.contains(_NONE_)) { throw new IllegalArgumentException("cannot combine _none_ with other fields"); } this.fieldNames.addAll(fieldNames); return this; } /** * Adds a field name {@code field} to the list of fields to load. */ public StoredFieldsContext addFieldName(String field) { if (fetchFields == false || _NONE_.equals(field)) { throw new IllegalArgumentException("cannot combine _none_ with other fields"); } this.fieldNames.add(field); return this; } /** * Returns true if the stored fields should be fetched, false otherwise. */ public boolean fetchFields() { return fetchFields; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; StoredFieldsContext that = (StoredFieldsContext) o; if (fetchFields != that.fetchFields) return false; return fieldNames != null ? fieldNames.equals(that.fieldNames) : that.fieldNames == null; } @Override public int hashCode() { int result = fieldNames != null ? fieldNames.hashCode() : 0; result = 31 * result + (fetchFields ? 1 : 0); return result; } public void toXContent(String preferredName, XContentBuilder builder) throws IOException { if (fetchFields == false) { builder.field(preferredName, _NONE_); } if (fieldNames != null) { if (fieldNames.size() == 1) { builder.field(preferredName, fieldNames.get(0)); } else { builder.startArray(preferredName); for (String fieldName : fieldNames) { builder.value(fieldName); } builder.endArray(); } } } public static StoredFieldsContext fromList(List<String> fieldNames) { if (fieldNames.size() == 1 && _NONE_.equals(fieldNames.get(0))) { return new StoredFieldsContext(false); } if (fieldNames.contains(_NONE_)) { throw new IllegalArgumentException("cannot combine _none_ with other fields"); } return new StoredFieldsContext(fieldNames); } public static StoredFieldsContext fromXContent(String fieldName, QueryParseContext context) throws IOException { XContentParser parser = context.parser(); XContentParser.Token token = parser.currentToken(); if (token == XContentParser.Token.VALUE_STRING) { return fromList(Collections.singletonList(parser.text())); } else if (token == XContentParser.Token.START_ARRAY) { ArrayList<String> list = new ArrayList<>(); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { list.add(parser.text()); } return fromList(list); } else { throw new ParsingException(parser.getTokenLocation(), "Expected [" + XContentParser.Token.VALUE_STRING + "] or [" + XContentParser.Token.START_ARRAY + "] in [" + fieldName + "] but found [" + token + "]", parser.getTokenLocation()); } } public static StoredFieldsContext fromRestRequest(String name, RestRequest request) { String sField = request.param(name); if (sField != null) { String[] sFields = Strings.splitStringByCommaToArray(sField); return fromList(Arrays.asList(sFields)); } return null; } }