/* * Copyright 2014, Tuplejump Inc. * * Licensed 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 com.tuplejump.stargate.lucene; import com.google.common.base.Splitter; import com.tuplejump.stargate.cassandra.CassandraUtils; import org.apache.cassandra.config.ColumnDefinition; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.FieldType; import org.apache.lucene.queryparser.flexible.standard.config.NumericConfig; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.map.*; import org.codehaus.jackson.map.module.SimpleModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * User: satya * <p> * This is used to get index options and field options to apply to the lucene based cassandra secondary indexes. */ public class Options implements Serializable { public static final Logger logger = LoggerFactory.getLogger(Options.class); public static final String DUMMY_DIR = "_DUMMY_"; public static String defaultIndexesDir = System.getProperty("sg.index.dir", DUMMY_DIR); public static final ObjectMapper inputMapper = new ObjectMapper(); static { inputMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); inputMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); inputMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); inputMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); inputMapper.configure(SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS, false); SimpleModule module = new SimpleModule("LowerCaseKeyDeserializer", new org.codehaus.jackson.Version(1, 9, 0, null)); module.addKeyDeserializer(Object.class, new LowerCaseKeyDeserializer()); module.addKeyDeserializer(Map.class, new LowerCaseKeyDeserializer()); module.addSerializer(FieldType.class, new FieldTypeSerializer()); module.addSerializer(Analyzer.class, new AnalyzerSerializer()); inputMapper.registerModule(module); if (defaultIndexesDir.equals(DUMMY_DIR)) { try { String dataDir = CassandraUtils.getDataDirs()[0]; if (!dataDir.endsWith(File.separator)) { dataDir = dataDir + File.separator; } defaultIndexesDir = dataDir + "sgindex"; } catch (Exception e) { throw new RuntimeException(e); } } } public static class FieldTypeSerializer extends JsonSerializer<FieldType> { @Override public void serialize(FieldType value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { gen.writeString(value.toString()); } } public static class AnalyzerSerializer extends JsonSerializer<Analyzer> { @Override public void serialize(Analyzer value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { gen.writeString(value.getClass().getName()); } } static class LowerCaseKeyDeserializer extends KeyDeserializer { @Override public Object deserializeKey(String key, DeserializationContext ctx) throws IOException { return key.toLowerCase(); } } public final Map<String, Properties> fields; public final Properties primary; public final Map<String, NumericConfig> numericFieldOptions; public final Map<String, FieldType> fieldDocValueTypes; public final Map<String, FieldType> collectionFieldDocValueTypes; public final Map<String, FieldType> fieldTypes; public final Map<String, FieldType[]> collectionFieldTypes; public final Set<String> nestedFields; public final Map<String, Type> types; public final Map<String, ColumnDefinition> clusteringKeysIndexed; public final Map<String, ColumnDefinition> partitionKeysIndexed; public final Set<String> indexedColumnNames; public final Analyzer analyzer; public final String defaultField; public Options(Properties primary, Map<String, NumericConfig> numericFieldOptions, Map<String, FieldType> fieldDocValueTypes, Map<String, FieldType> collectionFieldDocValueTypes, Map<String, FieldType> fieldTypes, Map<String, FieldType[]> collectionFieldTypes, Map<String, Type> types, Set<String> nestedFields, Map<String, ColumnDefinition> clusteringKeysIndexed, Map<String, ColumnDefinition> partitionKeysIndexed, Set<String> indexedColumnNames, Analyzer analyzer, String defaultField) { this.primary = primary; this.fields = primary.getFields(); this.numericFieldOptions = numericFieldOptions; this.fieldDocValueTypes = fieldDocValueTypes; this.collectionFieldDocValueTypes = collectionFieldDocValueTypes; this.fieldTypes = fieldTypes; this.collectionFieldTypes = collectionFieldTypes; this.nestedFields = nestedFields; this.types = types; this.clusteringKeysIndexed = clusteringKeysIndexed; this.partitionKeysIndexed = partitionKeysIndexed; this.indexedColumnNames = indexedColumnNames; this.analyzer = analyzer; this.defaultField = defaultField; } public String describeAsJson() throws IOException { return inputMapper.writeValueAsString(this); } public Properties getProperties(String fieldName) { if (fieldName.contains(".")) { Iterable<String> parts = Splitter.on('.').splitToList(fieldName); return getProps(primary, parts); } return fields.get(fieldName); } public static Properties getProps(Properties rootMapping, Iterable<String> fieldName) { Iterator<String> parts = fieldName.iterator(); //init current as primary field properties if (rootMapping == null) return null; Properties props = rootMapping; while (parts.hasNext() && props != null) { //as we go down the tree String key = parts.next(); //find if the current key has properties associated props = props.getFields().get(key); } //return the props if found or null return props; } public boolean isObject(String fieldName) { Properties props = fields.get(fieldName); return props != null && Type.object.equals(props.getType()); } public boolean shouldIndex(String fieldName) { return fieldTypes.containsKey(fieldName) || fieldDocValueTypes.containsKey(fieldName) || collectionFieldTypes.containsKey(fieldName) || collectionFieldDocValueTypes.containsKey(fieldName); } public boolean containsDocValues() { return !(fieldDocValueTypes.isEmpty() && collectionFieldDocValueTypes.isEmpty()); } }