blob: 82a6454a525abcae2ab6ab0a9d1ce219d29e4044 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.lucene.document;
import java.util.HashMap;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer; // javadocs
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexableFieldType;
import org.apache.lucene.index.PointValues;
/**
* Describes the properties of a field.
*/
public class FieldType implements IndexableFieldType {
private boolean stored;
private boolean tokenized = true;
private boolean storeTermVectors;
private boolean storeTermVectorOffsets;
private boolean storeTermVectorPositions;
private boolean storeTermVectorPayloads;
private boolean omitNorms;
private IndexOptions indexOptions = IndexOptions.NONE;
private boolean frozen;
private DocValuesType docValuesType = DocValuesType.NONE;
private int dimensionCount;
private int indexDimensionCount;
private int dimensionNumBytes;
private Map<String, String> attributes;
/**
* Create a new mutable FieldType with all of the properties from <code>ref</code>
*/
public FieldType(IndexableFieldType ref) {
this.stored = ref.stored();
this.tokenized = ref.tokenized();
this.storeTermVectors = ref.storeTermVectors();
this.storeTermVectorOffsets = ref.storeTermVectorOffsets();
this.storeTermVectorPositions = ref.storeTermVectorPositions();
this.storeTermVectorPayloads = ref.storeTermVectorPayloads();
this.omitNorms = ref.omitNorms();
this.indexOptions = ref.indexOptions();
this.docValuesType = ref.docValuesType();
this.dimensionCount = ref.pointDimensionCount();
this.indexDimensionCount = ref.pointIndexDimensionCount();
this.dimensionNumBytes = ref.pointNumBytes();
if (ref.getAttributes() != null) {
this.attributes = new HashMap<>(ref.getAttributes());
}
// Do not copy frozen!
}
/**
* Create a new FieldType with default properties.
*/
public FieldType() {
}
/**
* Throws an exception if this FieldType is frozen. Subclasses should
* call this within setters for additional state.
*/
protected void checkIfFrozen() {
if (frozen) {
throw new IllegalStateException("this FieldType is already frozen and cannot be changed");
}
}
/**
* Prevents future changes. Note, it is recommended that this is called once
* the FieldTypes's properties have been set, to prevent unintentional state
* changes.
*/
public void freeze() {
this.frozen = true;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setStored(boolean)
*/
@Override
public boolean stored() {
return this.stored;
}
/**
* Set to <code>true</code> to store this field.
* @param value true if this field should be stored.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #stored()
*/
public void setStored(boolean value) {
checkIfFrozen();
this.stored = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>true</code>.
* @see #setTokenized(boolean)
*/
public boolean tokenized() {
return this.tokenized;
}
/**
* Set to <code>true</code> to tokenize this field's contents via the
* configured {@link Analyzer}.
* @param value true if this field should be tokenized.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #tokenized()
*/
public void setTokenized(boolean value) {
checkIfFrozen();
this.tokenized = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setStoreTermVectors(boolean)
*/
@Override
public boolean storeTermVectors() {
return this.storeTermVectors;
}
/**
* Set to <code>true</code> if this field's indexed form should be also stored
* into term vectors.
* @param value true if this field should store term vectors.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #storeTermVectors()
*/
public void setStoreTermVectors(boolean value) {
checkIfFrozen();
this.storeTermVectors = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setStoreTermVectorOffsets(boolean)
*/
@Override
public boolean storeTermVectorOffsets() {
return this.storeTermVectorOffsets;
}
/**
* Set to <code>true</code> to also store token character offsets into the term
* vector for this field.
* @param value true if this field should store term vector offsets.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #storeTermVectorOffsets()
*/
public void setStoreTermVectorOffsets(boolean value) {
checkIfFrozen();
this.storeTermVectorOffsets = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setStoreTermVectorPositions(boolean)
*/
@Override
public boolean storeTermVectorPositions() {
return this.storeTermVectorPositions;
}
/**
* Set to <code>true</code> to also store token positions into the term
* vector for this field.
* @param value true if this field should store term vector positions.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #storeTermVectorPositions()
*/
public void setStoreTermVectorPositions(boolean value) {
checkIfFrozen();
this.storeTermVectorPositions = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setStoreTermVectorPayloads(boolean)
*/
@Override
public boolean storeTermVectorPayloads() {
return this.storeTermVectorPayloads;
}
/**
* Set to <code>true</code> to also store token payloads into the term
* vector for this field.
* @param value true if this field should store term vector payloads.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #storeTermVectorPayloads()
*/
public void setStoreTermVectorPayloads(boolean value) {
checkIfFrozen();
this.storeTermVectorPayloads = value;
}
/**
* {@inheritDoc}
* <p>
* The default is <code>false</code>.
* @see #setOmitNorms(boolean)
*/
@Override
public boolean omitNorms() {
return this.omitNorms;
}
/**
* Set to <code>true</code> to omit normalization values for the field.
* @param value true if this field should omit norms.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #omitNorms()
*/
public void setOmitNorms(boolean value) {
checkIfFrozen();
this.omitNorms = value;
}
/**
* {@inheritDoc}
* <p>
* The default is {@link IndexOptions#DOCS_AND_FREQS_AND_POSITIONS}.
* @see #setIndexOptions(IndexOptions)
*/
@Override
public IndexOptions indexOptions() {
return this.indexOptions;
}
/**
* Sets the indexing options for the field:
* @param value indexing options
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #indexOptions()
*/
public void setIndexOptions(IndexOptions value) {
checkIfFrozen();
if (value == null) {
throw new NullPointerException("IndexOptions must not be null");
}
this.indexOptions = value;
}
/**
* Enables points indexing.
*/
public void setDimensions(int dimensionCount, int dimensionNumBytes) {
this.setDimensions(dimensionCount, dimensionCount, dimensionNumBytes);
}
/**
* Enables points indexing with selectable dimension indexing.
*/
public void setDimensions(int dimensionCount, int indexDimensionCount, int dimensionNumBytes) {
if (dimensionCount < 0) {
throw new IllegalArgumentException("dimensionCount must be >= 0; got " + dimensionCount);
}
if (dimensionCount > PointValues.MAX_DIMENSIONS) {
throw new IllegalArgumentException("dimensionCount must be <= " + PointValues.MAX_DIMENSIONS + "; got " + dimensionCount);
}
if (indexDimensionCount < 0) {
throw new IllegalArgumentException("indexDimensionCount must be >= 0; got " + indexDimensionCount);
}
if (indexDimensionCount > dimensionCount) {
throw new IllegalArgumentException("indexDimensionCount must be <= dimensionCount: " + dimensionCount + "; got " + indexDimensionCount);
}
if (indexDimensionCount > PointValues.MAX_INDEX_DIMENSIONS) {
throw new IllegalArgumentException("indexDimensionCount must be <= " + PointValues.MAX_INDEX_DIMENSIONS + "; got " + indexDimensionCount);
}
if (dimensionNumBytes < 0) {
throw new IllegalArgumentException("dimensionNumBytes must be >= 0; got " + dimensionNumBytes);
}
if (dimensionNumBytes > PointValues.MAX_NUM_BYTES) {
throw new IllegalArgumentException("dimensionNumBytes must be <= " + PointValues.MAX_NUM_BYTES + "; got " + dimensionNumBytes);
}
if (dimensionCount == 0) {
if (indexDimensionCount != 0) {
throw new IllegalArgumentException("when dimensionCount is 0, indexDimensionCount must be 0; got " + indexDimensionCount);
}
if (dimensionNumBytes != 0) {
throw new IllegalArgumentException("when dimensionCount is 0, dimensionNumBytes must be 0; got " + dimensionNumBytes);
}
} else if (indexDimensionCount == 0) {
throw new IllegalArgumentException("when dimensionCount is > 0, indexDimensionCount must be > 0; got " + indexDimensionCount);
} else if (dimensionNumBytes == 0) {
if (dimensionCount != 0) {
throw new IllegalArgumentException("when dimensionNumBytes is 0, dimensionCount must be 0; got " + dimensionCount);
}
}
this.dimensionCount = dimensionCount;
this.indexDimensionCount = indexDimensionCount;
this.dimensionNumBytes = dimensionNumBytes;
}
@Override
public int pointDimensionCount() {
return dimensionCount;
}
@Override
public int pointIndexDimensionCount() {
return indexDimensionCount;
}
@Override
public int pointNumBytes() {
return dimensionNumBytes;
}
/**
* Puts an attribute value.
* <p>
* This is a key-value mapping for the field that the codec can use
* to store additional metadata.
* <p>
* If a value already exists for the field, it will be replaced with
* the new value. This method is not thread-safe, user must not add attributes
* while other threads are indexing documents with this field type.
*
* @lucene.experimental
*/
public String putAttribute(String key, String value) {
checkIfFrozen();
if (attributes == null) {
attributes = new HashMap<>();
}
return attributes.put(key, value);
}
@Override
public Map<String, String> getAttributes() {
return attributes;
}
/** Prints a Field for human consumption. */
@Override
public String toString() {
StringBuilder result = new StringBuilder();
if (stored()) {
result.append("stored");
}
if (indexOptions != IndexOptions.NONE) {
if (result.length() > 0)
result.append(",");
result.append("indexed");
if (tokenized()) {
result.append(",tokenized");
}
if (storeTermVectors()) {
result.append(",termVector");
}
if (storeTermVectorOffsets()) {
result.append(",termVectorOffsets");
}
if (storeTermVectorPositions()) {
result.append(",termVectorPosition");
}
if (storeTermVectorPayloads()) {
result.append(",termVectorPayloads");
}
if (omitNorms()) {
result.append(",omitNorms");
}
if (indexOptions != IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) {
result.append(",indexOptions=");
result.append(indexOptions);
}
}
if (dimensionCount != 0) {
if (result.length() > 0) {
result.append(",");
}
result.append("pointDimensionCount=");
result.append(dimensionCount);
result.append(",pointIndexDimensionCount=");
result.append(indexDimensionCount);
result.append(",pointNumBytes=");
result.append(dimensionNumBytes);
}
if (docValuesType != DocValuesType.NONE) {
if (result.length() > 0) {
result.append(",");
}
result.append("docValuesType=");
result.append(docValuesType);
}
return result.toString();
}
/**
* {@inheritDoc}
* <p>
* The default is <code>null</code> (no docValues)
* @see #setDocValuesType(DocValuesType)
*/
@Override
public DocValuesType docValuesType() {
return docValuesType;
}
/**
* Sets the field's DocValuesType
* @param type DocValues type, or null if no DocValues should be stored.
* @throws IllegalStateException if this FieldType is frozen against
* future modifications.
* @see #docValuesType()
*/
public void setDocValuesType(DocValuesType type) {
checkIfFrozen();
if (type == null) {
throw new NullPointerException("DocValuesType must not be null");
}
docValuesType = type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + dimensionCount;
result = prime * result + indexDimensionCount;
result = prime * result + dimensionNumBytes;
result = prime * result + ((docValuesType == null) ? 0 : docValuesType.hashCode());
result = prime * result + indexOptions.hashCode();
result = prime * result + (omitNorms ? 1231 : 1237);
result = prime * result + (storeTermVectorOffsets ? 1231 : 1237);
result = prime * result + (storeTermVectorPayloads ? 1231 : 1237);
result = prime * result + (storeTermVectorPositions ? 1231 : 1237);
result = prime * result + (storeTermVectors ? 1231 : 1237);
result = prime * result + (stored ? 1231 : 1237);
result = prime * result + (tokenized ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
FieldType other = (FieldType) obj;
if (dimensionCount != other.dimensionCount) return false;
if (indexDimensionCount != other.indexDimensionCount) return false;
if (dimensionNumBytes != other.dimensionNumBytes) return false;
if (docValuesType != other.docValuesType) return false;
if (indexOptions != other.indexOptions) return false;
if (omitNorms != other.omitNorms) return false;
if (storeTermVectorOffsets != other.storeTermVectorOffsets) return false;
if (storeTermVectorPayloads != other.storeTermVectorPayloads) return false;
if (storeTermVectorPositions != other.storeTermVectorPositions) return false;
if (storeTermVectors != other.storeTermVectors) return false;
if (stored != other.stored) return false;
if (tokenized != other.tokenized) return false;
return true;
}
}