package de.danielbasedow.prospecter.core.schema;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
import de.danielbasedow.prospecter.core.analysis.Analyzer;
import de.danielbasedow.prospecter.core.index.*;
import de.danielbasedow.prospecter.core.persistence.DummyQueryStorage;
import de.danielbasedow.prospecter.core.persistence.MapDBStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;
/**
* Build a Schema from a JSON configuration.
*/
public class SchemaBuilderJSON implements SchemaBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(SchemaBuilderJSON.class);
private Schema schema;
private ObjectNode root;
private final String schemaDirectory;
/**
* Build Schema from JSON String
*
* @param json raw JSON string
* @throws SchemaConfigurationError
*/
public SchemaBuilderJSON(String json) throws SchemaConfigurationError {
schemaDirectory = "";
ObjectMapper objectMapper = new ObjectMapper();
try {
LOGGER.debug("parsing json");
root = (ObjectNode) objectMapper.readTree(json);
} catch (IOException e) {
LOGGER.error(e.getLocalizedMessage());
throw new SchemaConfigurationError("Could not parse schema JSON.");
}
schema = new SchemaImpl();
}
/**
* Build Schema from a JSON file
*
* @param file file to read JSON from
* @throws SchemaConfigurationError
*/
public SchemaBuilderJSON(File file) throws SchemaConfigurationError {
schemaDirectory = file.getParent();
ObjectMapper objectMapper = new ObjectMapper();
try {
LOGGER.debug("parsing schema " + file.getAbsoluteFile());
root = (ObjectNode) objectMapper.readTree(file);
} catch (IOException e) {
LOGGER.error(e.getLocalizedMessage());
throw new SchemaConfigurationError("Could not parse schema JSON from file " + file.getAbsoluteFile());
}
schema = new SchemaImpl();
}
private void parseJSON() throws SchemaConfigurationError {
try {
ObjectNode fields = (ObjectNode) root.get("fields");
Iterator<Map.Entry<String, JsonNode>> iterator = fields.fields();
while (iterator.hasNext()) {
Map.Entry<String, JsonNode> entry = iterator.next();
FieldIndex index = buildField(entry.getKey(), entry.getValue());
schema.addFieldIndex(index.getName(), index);
}
JsonNode persistence = root.get("persistence");
//as a default set DummyQueryStorage. Only if persistence options are provided overwrite it
schema.setQueryStorage(new DummyQueryStorage());
if (persistence != null && persistence.getNodeType() == JsonNodeType.OBJECT) {
//if persistence key doesn't exist there is no persistence
JsonNode file = persistence.get("file");
if (file != null && file.getNodeType() == JsonNodeType.STRING) {
schema.setQueryStorage(new MapDBStore(new File(schemaDirectory, file.asText())));
} else {
LOGGER.error("unable to get file name for query storage. Continuing with no persistence!");
}
}
} catch (Exception e) {
throw new SchemaConfigurationError("Could not parse JSON tree", e);
}
schema.init();
}
protected FieldIndex buildField(String fieldName, JsonNode node) throws SchemaConfigurationError {
FieldIndex index = null;
String type = node.get("type").asText();
LOGGER.debug("building field '" + fieldName + "' with type: '" + type + "'");
if ("FullText".equals(type)) {
Analyzer analyzer;
try {
analyzer = getAnalyzer(node.get("options"));
} catch (Exception e) {
throw new SchemaConfigurationError("Couldn't create the analyzer instance.", e);
}
index = new FullTextIndex(fieldName, analyzer);
} else if ("Integer".equals(type)) {
index = new IntegerIndex(fieldName);
} else if ("GeoDistance".equals(type)) {
index = new GeoDistanceIndex(fieldName);
} else if ("String".equals(type)) {
index = new StringIndex(fieldName);
} else if ("Long".equals(type)) {
index = new LongIndex(fieldName);
} else if ("Double".equals(type)) {
index = new DoubleIndex(fieldName);
} else if ("DateTime".equals(type)) {
JsonNode format = node.get("format");
DateFormat df;
if (format != null) {
LOGGER.debug("using custom date format '" + format.asText() + "'");
df = new SimpleDateFormat(format.asText());
} else {
LOGGER.debug("using default date format iso 8601");
df = new ISO8601DateFormat();
}
index = new DateTimeIndex(fieldName, df);
}
return index;
}
private Analyzer getAnalyzer(JsonNode options) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String analyzerName = "de.danielbasedow.prospecter.core.analysis.LuceneStandardAnalyzer";
if (options != null && options.getNodeType() == JsonNodeType.OBJECT) {
JsonNode analyzerNode = options.get("analyzer");
if (analyzerNode != null && analyzerNode.getNodeType() == JsonNodeType.STRING) {
analyzerName = analyzerNode.asText();
}
}
LOGGER.info("using analyzer " + analyzerName);
Class class_ = Class.forName(analyzerName);
Method factory = class_.getMethod("make", JsonNode.class);
return (Analyzer) factory.invoke(null, options);
}
@Override
public Schema getSchema() throws SchemaConfigurationError {
parseJSON();
return schema;
}
}