package org.apache.blur.analysis; /** * 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. */ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.blur.analysis.type.AclDiscoverFieldTypeDefinition; import org.apache.blur.analysis.type.AclReadFieldTypeDefinition; import org.apache.blur.analysis.type.DateFieldTypeDefinition; import org.apache.blur.analysis.type.DoubleFieldTypeDefinition; import org.apache.blur.analysis.type.FieldLessFieldTypeDefinition; import org.apache.blur.analysis.type.FloatFieldTypeDefinition; import org.apache.blur.analysis.type.IntFieldTypeDefinition; import org.apache.blur.analysis.type.LongFieldTypeDefinition; import org.apache.blur.analysis.type.NumericFieldTypeDefinition; import org.apache.blur.analysis.type.ReadMaskFieldTypeDefinition; import org.apache.blur.analysis.type.StoredFieldTypeDefinition; import org.apache.blur.analysis.type.StringFieldTypeDefinition; import org.apache.blur.analysis.type.TextFieldTypeDefinition; import org.apache.blur.analysis.type.spatial.BaseSpatialFieldTypeDefinition; import org.apache.blur.analysis.type.spatial.SpatialPointVectorStrategyFieldTypeDefinition; import org.apache.blur.analysis.type.spatial.SpatialRecursivePrefixTreeStrategyFieldTypeDefinition; import org.apache.blur.analysis.type.spatial.SpatialTermQueryPrefixTreeStrategyFieldTypeDefinition; import org.apache.blur.log.Log; import org.apache.blur.log.LogFactory; import org.apache.blur.thrift.generated.Column; import org.apache.blur.thrift.generated.Record; import org.apache.blur.utils.BlurConstants; import org.apache.hadoop.conf.Configuration; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.AnalyzerWrapper; import org.apache.lucene.analysis.core.KeywordAnalyzer; import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.FieldType; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.apache.lucene.search.SortField; public abstract class BaseFieldManager extends FieldManager { private static final Log LOG = LogFactory.getLog(BaseFieldManager.class); private static final Map<String, String> EMPTY_MAP = new HashMap<String, String>(); private static final boolean DEFAULT_MULTI_VALUE_FIELD_VALUE = true; private static final Comparator<FieldTypeDefinition> POST_PROCESSING_COMPARATOR = new Comparator<FieldTypeDefinition>() { @Override public int compare(FieldTypeDefinition o1, FieldTypeDefinition o2) { return Integer.compare(o2.getPostProcessingPriority(), o1.getPostProcessingPriority()); } }; private final ConcurrentMap<String, Set<String>> _columnToSubColumn = new ConcurrentHashMap<String, Set<String>>(); private final ConcurrentMap<String, FieldTypeDefinition> _fieldNameToDefMap = new ConcurrentHashMap<String, FieldTypeDefinition>(); // This is loaded at object creation and never changed again. private final Map<String, Class<? extends FieldTypeDefinition>> _typeMap = new ConcurrentHashMap<String, Class<? extends FieldTypeDefinition>>(); private final Analyzer _baseAnalyzerForQuery; private final Analyzer _baseAnalyzerForIndex; private final String _fieldLessField; private final Map<String, String> _defaultMissingFieldProps; private final String _defaultMissingFieldType; private final boolean _defaultMissingFieldLessIndexing; private final boolean _strict; private final FieldTypeDefinition _fieldLessFieldTypeDefinition; private final Configuration _configuration; private final KeywordAnalyzer _keywordAnalyzer = new KeywordAnalyzer(); public static FieldType ID_TYPE; static { ID_TYPE = new FieldType(); ID_TYPE.setIndexed(true); ID_TYPE.setTokenized(false); ID_TYPE.setOmitNorms(true); ID_TYPE.setStored(true); ID_TYPE.freeze(); } private static final FieldType SUPER_FIELD_TYPE; static { SUPER_FIELD_TYPE = new FieldType(TextField.TYPE_NOT_STORED); SUPER_FIELD_TYPE.setOmitNorms(true); } public BaseFieldManager(String fieldLessField, final Analyzer defaultAnalyzerForQuerying, Configuration configuration) throws IOException { this(fieldLessField, defaultAnalyzerForQuerying, true, null, false, null, configuration); } public BaseFieldManager(String fieldLessField, final Analyzer defaultAnalyzerForQuerying, boolean strict, String defaultMissingFieldType, boolean defaultMissingFieldLessIndexing, Map<String, String> defaultMissingFieldProps, Configuration configuration) throws IOException { registerType(TextFieldTypeDefinition.class); registerType(StringFieldTypeDefinition.class); registerType(StoredFieldTypeDefinition.class); registerType(IntFieldTypeDefinition.class); registerType(LongFieldTypeDefinition.class); registerType(DoubleFieldTypeDefinition.class); registerType(FloatFieldTypeDefinition.class); registerType(DateFieldTypeDefinition.class); registerType(SpatialPointVectorStrategyFieldTypeDefinition.class); registerType(SpatialTermQueryPrefixTreeStrategyFieldTypeDefinition.class); registerType(SpatialRecursivePrefixTreeStrategyFieldTypeDefinition.class); registerType(AclReadFieldTypeDefinition.class); registerType(AclDiscoverFieldTypeDefinition.class); registerType(ReadMaskFieldTypeDefinition.class); _fieldLessField = fieldLessField; _strict = strict; _defaultMissingFieldLessIndexing = defaultMissingFieldLessIndexing; _defaultMissingFieldType = defaultMissingFieldType; _defaultMissingFieldProps = defaultMissingFieldProps; _configuration = configuration; _fieldLessFieldTypeDefinition = new FieldLessFieldTypeDefinition(); _baseAnalyzerForQuery = new AnalyzerWrapper() { @Override protected Analyzer getWrappedAnalyzer(String fieldName) { if (isBuiltInField(fieldName)) { return _keywordAnalyzer; } FieldTypeDefinition fieldTypeDefinition; try { fieldTypeDefinition = getFieldTypeDefinition(fieldName); } catch (IOException e) { throw new RuntimeException(e); } if (fieldTypeDefinition == null) { return defaultAnalyzerForQuerying; } return fieldTypeDefinition.getAnalyzerForQuery(fieldName); } @Override protected TokenStreamComponents wrapComponents(String fieldName, TokenStreamComponents components) { return components; } }; _baseAnalyzerForIndex = new AnalyzerWrapper() { @Override protected Analyzer getWrappedAnalyzer(String fieldName) { FieldTypeDefinition fieldTypeDefinition; try { fieldTypeDefinition = getFieldTypeDefinition(getFieldNameRealNameIfReadMask(fieldName)); } catch (IOException e) { throw new RuntimeException(e); } if (fieldTypeDefinition == null) { throw new RuntimeException("Field [" + fieldName + "] not found."); } return fieldTypeDefinition.getAnalyzerForIndex(fieldName); } @Override protected TokenStreamComponents wrapComponents(String fieldName, TokenStreamComponents components) { return components; } }; } protected String getFieldNameRealNameIfReadMask(String fieldName) { if (fieldName.endsWith("$" + ReadMaskFieldTypeDefinition.INTERNAL_FIELDNAME)) { return fieldName.substring(0, fieldName.lastIndexOf('$')); } return fieldName; } protected boolean isBuiltInField(String fieldName) { if (fieldName.equals(BlurConstants.ROW_ID)) { return true; } else if (fieldName.equals(BlurConstants.RECORD_ID)) { return true; } else if (fieldName.equals(BlurConstants.FAMILY)) { return true; } return false; } @Override public Set<String> getFieldNames() throws IOException { return new TreeSet<String>(_fieldNameToDefMap.keySet()); } @Override public void loadFromStorage() throws IOException { List<String> fieldNamesToLoad = getFieldNamesToLoad(); for (String fieldName : fieldNamesToLoad) { if (!_fieldNameToDefMap.containsKey(fieldName)) { tryToLoad(fieldName); } } } @Override public void registerType(Class<? extends FieldTypeDefinition> c) { try { FieldTypeDefinition fieldTypeDefinition = c.newInstance(); String name = fieldTypeDefinition.getName(); if (_typeMap.containsKey(name)) { throw new RuntimeException("Type [" + name + "] is already registered."); } _typeMap.put(name, c); } catch (InstantiationException e) { throw new RuntimeException("The default constructor of the class [" + c + "] is missing.", e); } catch (IllegalAccessException e) { throw new RuntimeException("The scope of the class [" + c + "] is not public.", e); } } @Override public List<Field> getFields(String rowId, Record record) throws IOException { List<Field> fields = new ArrayList<Field>(); String family = record.getFamily(); if (family == null || family.isEmpty()) { family = BlurConstants.DEFAULT_FAMILY; } List<Column> columns = record.getColumns(); addDefaultFields(fields, rowId, record); addFieldExistance(fields, record); Map<String, Integer> fieldCounts = new HashMap<String, Integer>(); List<FieldTypeDefinition> postProcessingFieldTypes = new ArrayList<FieldTypeDefinition>(); for (Column column : columns) { String name = column.getName(); String value = column.getValue(); if (value == null || name == null) { continue; } FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(family, column); if (fieldTypeDefinition == null) { if (isStrict()) { LOG.error("Family [{0}] Column [{1}] not defined", family, column); throw new IOException("Family [" + family + "] Column [" + column + "] not defined"); } addColumnDefinition(family, name, null, getDefaultMissingFieldLessIndexing(), getDefaultMissingFieldType(), false, DEFAULT_MULTI_VALUE_FIELD_VALUE, getDefaultMissingFieldProps()); fieldTypeDefinition = getFieldTypeDefinition(family, column); } if (fieldTypeDefinition.isPostProcessingSupported()) { postProcessingFieldTypes.add(fieldTypeDefinition); } String fieldName = fieldTypeDefinition.getFieldName(); Integer count = fieldCounts.get(fieldName); if (count == null) { count = 1; } else { count = count + 1; } fieldCounts.put(fieldName, count); getAndAddFields(fields, family, column, fieldTypeDefinition, count); Collection<String> subColumns = getSubColumns(family, column); if (subColumns != null) { for (String subName : subColumns) { FieldTypeDefinition subFieldTypeDefinition = getFieldTypeDefinition(family, column, subName); fieldName = subFieldTypeDefinition.getFieldName(); count = fieldCounts.get(fieldName); if (count == null) { count = 1; } else { count = count + 1; } getAndAddFields(fields, family, column, subName, subFieldTypeDefinition, count); } } } if (!postProcessingFieldTypes.isEmpty()) { Collections.sort(postProcessingFieldTypes, POST_PROCESSING_COMPARATOR); Iterable<? extends IndexableField> iterable = fields; for (FieldTypeDefinition fieldTypeDefinition : postProcessingFieldTypes) { iterable = fieldTypeDefinition.executePostProcessing(iterable); } return toList(iterable); } else { return fields; } } private List<Field> toList(Iterable<? extends IndexableField> iterable) { List<Field> fields = new ArrayList<Field>(); for (IndexableField field : iterable) { fields.add((Field) field); } return fields; } private void addFieldExistance(List<Field> fields, Record record) { String family = record.getFamily(); if (family == null) { family = BlurConstants.DEFAULT_FAMILY; } for (Column column : record.getColumns()) { String name = column.getName(); String value = column.getValue(); if (value == null || name == null) { continue; } fields.add(new StringField(BlurConstants.FIELDS, family + "." + name, Store.NO)); } } private void getAndAddFields(List<Field> fields, String family, Column column, String subName, FieldTypeDefinition fieldTypeDefinition, int count) throws IOException { if (count > 1 && !fieldTypeDefinition.isMultiValueField()) { throw new IOException("Field [" + fieldTypeDefinition.getFieldName() + "] does not allow more than one column value."); } for (Field field : fieldTypeDefinition.getFieldsForSubColumn(family, column, subName)) { fields.add(field); } } private void addDefaultFields(List<Field> fields, String rowId, Record record) { String family = record.getFamily(); String recordId = record.getRecordId(); validateNotNull(rowId, BlurConstants.ROW_ID); validateNotNull(recordId, BlurConstants.RECORD_ID); if (family == null) { fields.add(new Field(BlurConstants.FAMILY, BlurConstants.DEFAULT_FAMILY, ID_TYPE)); } else { fields.add(new Field(BlurConstants.FAMILY, family, ID_TYPE)); } fields.add(new Field(BlurConstants.ROW_ID, rowId, ID_TYPE)); fields.add(new Field(BlurConstants.RECORD_ID, recordId, ID_TYPE)); } private void validateNotNull(String value, String fieldName) { if (value != null) { return; } throw new IllegalArgumentException("Field [" + fieldName + "] cannot be null."); } private void getAndAddFields(List<Field> fields, String family, Column column, FieldTypeDefinition fieldTypeDefinition, int count) throws IOException { if (count > 1 && !fieldTypeDefinition.isMultiValueField()) { throw new IOException("Field [" + fieldTypeDefinition.getFieldName() + "] does not allow more than one column value."); } for (Field field : fieldTypeDefinition.getFieldsForColumn(family, column)) { fields.add(field); } if (fieldTypeDefinition.isFieldLessIndexed()) { addFieldLessIndex(fields, column.getValue()); } } private void addFieldLessIndex(List<Field> fields, String value) { fields.add(new Field(_fieldLessField, value, SUPER_FIELD_TYPE)); } private FieldTypeDefinition getFieldTypeDefinition(String family, Column column, String subName) throws IOException { return getFieldTypeDefinition(getSubColumnName(family, column, subName)); } private String getSubColumnName(String family, Column column, String subName) { return getColumnName(family, column) + "." + subName; } private String getColumnName(String family, Column column) { return getColumnName(family, column.getName()); } private String getColumnName(String family, String columnName) { return family + "." + columnName; } private Collection<String> getSubColumns(String family, Column column) { return _columnToSubColumn.get(getColumnName(family, column)); } private FieldTypeDefinition getFieldTypeDefinition(String family, Column column) throws IOException { return getFieldTypeDefinition(getColumnName(family, column)); } @Override public boolean addColumnDefinition(String family, String columnName, String subColumnName, boolean fieldLessIndexed, String fieldType, boolean sortable, boolean multiValueField, Map<String, String> props) throws IOException { if (family == null) { family = BlurConstants.DEFAULT_FAMILY; } String baseFieldName = family + "." + columnName; String fieldName; if (subColumnName != null) { FieldTypeDefinition primeFieldTypeDefinition = getFieldTypeDefinition(baseFieldName); if (primeFieldTypeDefinition == null) { throw new IllegalArgumentException("Base column of [" + baseFieldName + "] not found, please add base before adding sub column."); } if (fieldLessIndexed) { throw new IllegalArgumentException("Subcolumn of [" + subColumnName + "] from base of [" + baseFieldName + "] cannot be added with fieldLessIndexing set to true."); } fieldName = baseFieldName + "." + subColumnName; } else { fieldName = baseFieldName; } return addFieldTypeDefinition(family, columnName, subColumnName, fieldName, fieldLessIndexed, fieldType, sortable, multiValueField, props); } private boolean addFieldTypeDefinition(String family, String columnName, String subColumnName, String fieldName, boolean fieldLessIndexed, String fieldType, boolean sortable, boolean multiValueField, Map<String, String> props) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(fieldName); if (fieldTypeDefinition != null) { return false; } fieldTypeDefinition = newFieldTypeDefinition(fieldName, fieldLessIndexed, fieldType, sortable, multiValueField, props); synchronized (_fieldNameToDefMap) { boolean alternateFieldNamesSharedAcrossInstances = fieldTypeDefinition .isAlternateFieldNamesSharedAcrossInstances(); for (String alternateFieldName : fieldTypeDefinition.getAlternateFieldNames()) { if (alternateFieldNamesSharedAcrossInstances && _fieldNameToDefMap.containsKey(alternateFieldName)) { FieldTypeDefinition ftd = _fieldNameToDefMap.get(alternateFieldName); if (!ftd.getName().equals(fieldTypeDefinition.getName())) { throw new IllegalArgumentException("Alternate fieldName collision of [" + alternateFieldName + "] from field type definition [" + fieldTypeDefinition + "], this field type definition cannot be added."); } } else if (_fieldNameToDefMap.containsKey(alternateFieldName)) { throw new IllegalArgumentException("Alternate fieldName collision of [" + alternateFieldName + "] from field type definition [" + fieldTypeDefinition + "], this field type definition cannot be added."); } } setFields(fieldTypeDefinition, family, columnName, subColumnName, fieldLessIndexed, fieldType, props); if (!tryToStore(fieldTypeDefinition, fieldName)) { return false; } registerFieldTypeDefinition(fieldName, fieldTypeDefinition); } return true; } private void setFields(FieldTypeDefinition fieldTypeDefinition, String family, String columnName, String subColumnName, boolean fieldLessIndexed, String fieldType, Map<String, String> props) { fieldTypeDefinition.setFamily(family); fieldTypeDefinition.setColumnName(columnName); fieldTypeDefinition.setSubColumnName(subColumnName); fieldTypeDefinition.setFieldLessIndexed(fieldLessIndexed); fieldTypeDefinition.setFieldType(fieldType); fieldTypeDefinition.setProperties(props); } protected void registerFieldTypeDefinition(String fieldName, FieldTypeDefinition fieldTypeDefinition) { _fieldNameToDefMap.put(fieldName, fieldTypeDefinition); for (String alternateFieldName : fieldTypeDefinition.getAlternateFieldNames()) { _fieldNameToDefMap.put(alternateFieldName, fieldTypeDefinition); } String subColumnName = getSubColumnName(fieldName); if (subColumnName != null) { String baseFieldName = getBaseFieldName(fieldName); Set<String> subColumnNames = _columnToSubColumn.get(baseFieldName); if (subColumnNames == null) { subColumnNames = getConcurrentSet(); _columnToSubColumn.put(baseFieldName, subColumnNames); } subColumnNames.add(subColumnName); } } private String getSubColumnName(String fieldName) { int lastIndexOf = fieldName.lastIndexOf('.'); int indexOf = fieldName.indexOf('.'); if (indexOf == lastIndexOf) { return null; } return fieldName.substring(lastIndexOf + 1); } private String getBaseFieldName(String fieldName) { return fieldName.substring(0, fieldName.lastIndexOf('.')); } protected FieldTypeDefinition newFieldTypeDefinition(String fieldName, boolean fieldLessIndexed, String fieldType, boolean sortable, boolean multiValueField, Map<String, String> props) { if (fieldType == null) { throw new IllegalArgumentException("Field type can not be null."); } Class<? extends FieldTypeDefinition> clazz = _typeMap.get(fieldType); if (clazz == null) { throw new IllegalArgumentException("FieldType of [" + fieldType + "] was not found."); } FieldTypeDefinition fieldTypeDefinition; try { fieldTypeDefinition = clazz.newInstance(); } catch (InstantiationException e) { LOG.error("Unknown error trying to create a type of [{0}] from class [{1}]", e, fieldType, clazz); throw new RuntimeException(e); } catch (IllegalAccessException e) { LOG.error("Unknown error trying to create a type of [{0}] from class [{1}]", e, fieldType, clazz); throw new RuntimeException(e); } if (props == null) { fieldTypeDefinition.configure(fieldName, EMPTY_MAP, _configuration); } else { fieldTypeDefinition.configure(fieldName, props, _configuration); } fieldTypeDefinition.setSortEnable(sortable); fieldTypeDefinition.setFieldLessIndexed(fieldLessIndexed); fieldTypeDefinition.setMultiValueField(multiValueField); return fieldTypeDefinition; } protected abstract boolean tryToStore(FieldTypeDefinition fieldTypeDefinition, String fieldName) throws IOException; protected abstract void tryToLoad(String fieldName) throws IOException; protected abstract List<String> getFieldNamesToLoad() throws IOException; private Set<String> getConcurrentSet() { return Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); } @Override public boolean isValidColumnDefinition(String family, String columnName) throws IOException { String fieldName = getColumnName(family, columnName); FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(fieldName); if (fieldTypeDefinition == null) { return false; } return true; } @Override public Analyzer getAnalyzerForIndex(String fieldName) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(fieldName); if (fieldTypeDefinition == null) { throw new AnalyzerNotFoundException(fieldName); } return fieldTypeDefinition.getAnalyzerForIndex(fieldName); } @Override public Analyzer getAnalyzerForQuery(String fieldName) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(fieldName); if (fieldTypeDefinition == null) { throw new AnalyzerNotFoundException(fieldName); } return fieldTypeDefinition.getAnalyzerForQuery(fieldName); } public void addColumnDefinitionGisPointVector(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, SpatialPointVectorStrategyFieldTypeDefinition.NAME, false, false, null); } public void addColumnDefinitionGisRecursivePrefixTree(String family, String columnName) throws IOException { Map<String, String> props = new HashMap<String, String>(); props.put(BaseSpatialFieldTypeDefinition.SPATIAL_PREFIX_TREE, BaseSpatialFieldTypeDefinition.GEOHASH_PREFIX_TREE); addColumnDefinition(family, columnName, null, false, SpatialRecursivePrefixTreeStrategyFieldTypeDefinition.NAME, false, false, props); } public void addColumnDefinitionDate(String family, String columnName, String format) throws IOException { Map<String, String> props = new HashMap<String, String>(); props.put(DateFieldTypeDefinition.DATE_FORMAT, format); addColumnDefinition(family, columnName, null, false, DateFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, props); } public void addColumnDefinitionInt(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, IntFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionLong(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, LongFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionFloat(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, FloatFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionDouble(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, DoubleFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionString(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, StringFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionText(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, false, TextFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } public void addColumnDefinitionTextFieldLess(String family, String columnName) throws IOException { addColumnDefinition(family, columnName, null, true, TextFieldTypeDefinition.NAME, false, DEFAULT_MULTI_VALUE_FIELD_VALUE, null); } @Override public Analyzer getAnalyzerForQuery() { return _baseAnalyzerForQuery; } @Override public Analyzer getAnalyzerForIndex() { return _baseAnalyzerForIndex; } @Override public boolean isFieldLessIndexed(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return false; } return fieldTypeDefinition.isFieldLessIndexed(); } @Override public Boolean checkSupportForFuzzyQuery(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return null; } return fieldTypeDefinition.checkSupportForFuzzyQuery(); } @Override public Boolean checkSupportForPrefixQuery(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return null; } return fieldTypeDefinition.checkSupportForPrefixQuery(); } @Override public Boolean checkSupportForWildcardQuery(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return null; } return fieldTypeDefinition.checkSupportForWildcardQuery(); } @Override public Boolean checkSupportForRegexQuery(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return null; } return fieldTypeDefinition.checkSupportForRegexQuery(); } @Override public Boolean checkSupportForCustomQuery(String field) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { return null; } return fieldTypeDefinition.checkSupportForCustomQuery(); } @Override public Query getNewRangeQuery(String field, String part1, String part2, boolean startInclusive, boolean endInclusive) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition != null && fieldTypeDefinition.isNumeric()) { NumericFieldTypeDefinition numericFieldTypeDefinition = (NumericFieldTypeDefinition) fieldTypeDefinition; return numericFieldTypeDefinition.getNewRangeQuery(field, part1, part2, startInclusive, endInclusive); } return null; } @Override public Query getTermQueryIfNumeric(String field, String text) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition != null && fieldTypeDefinition.isNumeric()) { NumericFieldTypeDefinition numericFieldTypeDefinition = (NumericFieldTypeDefinition) fieldTypeDefinition; return numericFieldTypeDefinition.getNewRangeQuery(field, text, text, true, true); } return null; } @Override public FieldTypeDefinition getFieldTypeDefinition(String field) throws IOException { if (field.equals(_fieldLessField)) { return _fieldLessFieldTypeDefinition; } FieldTypeDefinition fieldTypeDefinition = _fieldNameToDefMap.get(field); if (fieldTypeDefinition == null) { tryToLoad(field); fieldTypeDefinition = _fieldNameToDefMap.get(field); } return fieldTypeDefinition; } @Override public SortField getSortField(String field, boolean reverse) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { throw new IOException("Field [" + field + "] is missing."); } if (fieldTypeDefinition.checkSupportForSorting()) { if (fieldTypeDefinition.isSortEnable()) { return fieldTypeDefinition.getSortField(reverse); } throw new IOException("Field [" + field + "] does not have sorting enabled."); } throw new IOException("Field [" + field + "] does not support sorting."); } @Override public Query getCustomQuery(String field, String text) throws IOException { FieldTypeDefinition fieldTypeDefinition = getFieldTypeDefinition(field); if (fieldTypeDefinition == null) { throw new IOException("Field [" + field + "] is missing."); } return fieldTypeDefinition.getCustomQuery(text); } @Override public String getFieldLessFieldName() { return _fieldLessField; } @Override public Map<String, String> getDefaultMissingFieldProps() { return _defaultMissingFieldProps; } @Override public String getDefaultMissingFieldType() { return _defaultMissingFieldType; } @Override public boolean getDefaultMissingFieldLessIndexing() { return _defaultMissingFieldLessIndexing; } @Override public boolean isStrict() { return _strict; } @Override public String resolveField(String field) { if (_fieldNameToDefMap.get(field) != null || isBuiltInField(field) || field.equals(_fieldLessField)) { return field; } String newField = BlurConstants.DEFAULT_FAMILY + "." + field; if (_fieldNameToDefMap.get(newField) != null) { return newField; } return field; } }