blob: e17adc6da4f59a0c2bbbba0c488c81017af251bb [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.metron.solr.schema;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.apache.metron.common.utils.JSONUtils;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
public class SchemaTranslator {
public static final String TAB = " ";
public static final String PREAMBLE="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<!--\n" +
" Licensed to the Apache Software Foundation (ASF) under one or more\n" +
" contributor license agreements. See the NOTICE file distributed with\n" +
" this work for additional information regarding copyright ownership.\n" +
" The ASF licenses this file to You under the Apache License, Version 2.0\n" +
" (the \"License\"); you may not use this file except in compliance with\n" +
" the License. You may obtain a copy of the License at\n" +
"\n" +
" http://www.apache.org/licenses/LICENSE-2.0\n" +
"\n" +
" Unless required by applicable law or agreed to in writing, software\n" +
" distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
" See the License for the specific language governing permissions and\n" +
" limitations under the License.\n" +
"-->";
public static final String VERSION_FIELD =
"<field name=\"_version_\" type=\"" + SolrFields.LONG.solrType.getName() + "\" indexed=\"false\" stored=\"false\"/>";
public static final String ROOT_FIELD =
"<field name=\"_root_\" type=\"" + SolrFields.STRING.solrType.getName() + "\" indexed=\"true\" stored=\"false\" docValues=\"false\" />";
public static final String DYNAMIC_FIELD_CATCHALL = "<dynamicField name=\"*\" type=\"ignored\" multiValued=\"false\" docValues=\"true\"/>";
public static final String UNIQUE_KEY = "<uniqueKey>guid</uniqueKey>";
public static final String PROPERTIES_KEY = "properties";
public static final String DYNAMIC_TEMPLATES_KEY = "dynamic_templates";
public static final String SCHEMA_FORMAT="<schema name=\"%s\" version=\"1.6\">";
public static final String TEMPLATE_KEY = "template";
public enum SolrFields {
STRING( new FieldType("string", "solr.StrField").sortMissingLast()
, ImmutableSet.of("text", "keyword")),
BOOLEAN( new FieldType("boolean", "solr.BoolField").sortMissingLast()
, ImmutableSet.of("boolean")),
INTEGER( new FieldType("pint", "solr.IntPointField").docValues()
, ImmutableSet.of("integer")),
FLOAT( new FieldType("pfloat", "solr.FloatPointField").docValues()
, ImmutableSet.of("float")),
LONG( new FieldType("plong", "solr.LongPointField").docValues()
, ImmutableSet.of("long")),
DOUBLE( new FieldType("pdouble", "solr.DoublePointField").docValues()
, ImmutableSet.of("double")),
BINARY( new FieldType("bytes", "solr.BinaryField").docValues()
, ImmutableSet.of("binary")),
LOCATION( new FieldType("location", "solr.LatLonPointSpatialField").docValues()
, ImmutableSet.of("geo_point")),
IP(new FieldType("ip", "solr.StrField").sortMissingLast()
, ImmutableSet.of("ip")),
TIMESTAMP(new FieldType("timestamp", "solr.LongPointField").docValues()
, ImmutableSet.of("date")),
IGNORE( new FieldType("ignored", "solr.StrField").multiValued(), new HashSet<>())
;
FieldType solrType;
Set<String> elasticsearchTypes;
SolrFields(FieldType solrType, Set<String> elasticsearchTypes) {
this.solrType = solrType;
this.elasticsearchTypes = elasticsearchTypes;
}
public String getTypeDeclaration() {
return solrType.toString();
}
public static SolrFields byElasticsearchType(String type) {
for(SolrFields f : values()) {
if(f.elasticsearchTypes.contains(type)) {
return f;
}
}
return null;
}
public static void printTypes(PrintWriter pw) {
for(SolrFields f : values()) {
pw.println(TAB + f.getTypeDeclaration());
}
}
}
public static String normalizeField(String fieldName) {
return fieldName.replace(':', '.');
}
public static void processProperties(PrintWriter pw, Map<String, Object> properties) {
for(Map.Entry<String, Object> property : properties.entrySet()) {
String fieldName = normalizeField(property.getKey());
System.out.println("Processing property: " + fieldName);
if(fieldName.equals("guid")) {
pw.println(TAB + "<field name=\"guid\" type=\"" + SolrFields.STRING.solrType.getName()
+ "\" indexed=\"true\" stored=\"true\" required=\"true\" multiValued=\"false\" />");
}
else {
String type = (String) ((Map<String, Object>) property.getValue()).get("type");
SolrFields solrField = SolrFields.byElasticsearchType(type);
if(solrField == null) {
System.out.println("Skipping " + fieldName + " because I can't find solr type for " + type);
continue;
}
pw.println(TAB + String.format("<field name=\"%s\" type=\"%s\" indexed=\"true\" stored=\"true\" />", fieldName, solrField.solrType.getName()));
}
}
}
public static void processDynamicMappings(PrintWriter pw, List<Map<String, Object>> properties) {
for(Map<String, Object> dynamicProperty : properties) {
for(Map.Entry<String, Object> dynamicFieldDef : dynamicProperty.entrySet()) {
System.out.println("Processing dynamic property: " + dynamicFieldDef.getKey());
Map<String, Object> def = (Map<String, Object>) dynamicFieldDef.getValue();
String match = (String) def.get("match");
if(match == null) {
match = (String) def.get("path_match");
}
match = normalizeField(match);
String type = (String)((Map<String, Object>)def.get("mapping")).get("type");
SolrFields solrField = SolrFields.byElasticsearchType(type);
if(solrField == null) {
System.out.println("Skipping " + match + " because I can't find solr type for " + type);
continue;
}
if(solrField == null) {
throw new IllegalStateException("Unable to find associated solr type for " + type + " with dynamic property " + solrField);
}
pw.println(TAB + String.format("<dynamicField name=\"%s\" type=\"%s\" multiValued=\"false\" docValues=\"true\"/>", match, solrField.solrType.getName()));
}
}
}
public static void translate(PrintWriter pw, Map<String, Object> template) {
pw.println(PREAMBLE);
System.out.println("Processing " + template.getOrDefault(TEMPLATE_KEY, "unknown template"));
Map<String, Object> mappings = (Map<String, Object>) template.getOrDefault("mappings", new HashMap<>());
if (mappings.size() != 1) {
System.err.println("Unable to process mappings. We expect exactly 1 mapping, there are " + mappings.size() + " mappings specified");
}
String docName = Iterables.getFirst(mappings.keySet(), null);
pw.println(String.format(SCHEMA_FORMAT, docName));
pw.println(TAB + VERSION_FIELD);
pw.println(TAB + ROOT_FIELD);
for (Map.Entry<String, Object> docTypeToMapping : mappings.entrySet()) {
System.out.println("Processing " + docTypeToMapping.getKey() + " doc type");
Map<String, Object> actualMappings = (Map<String, Object>) docTypeToMapping.getValue();
Map<String, Object> properties = (Map<String, Object>) actualMappings.getOrDefault(PROPERTIES_KEY, new HashMap<>());
processProperties(pw, properties);
List<Map<String, Object>> dynamicMappings = (List<Map<String, Object>>) actualMappings.getOrDefault(DYNAMIC_TEMPLATES_KEY, new ArrayList<>());
processDynamicMappings(pw, dynamicMappings);
pw.println(TAB + DYNAMIC_FIELD_CATCHALL);
pw.println(TAB + UNIQUE_KEY);
SolrFields.printTypes(pw);
}
pw.println("</schema>");
pw.flush();
}
public static void main(String... argv) throws IOException {
String templateFile = argv[0];
String schemaFile = argv[1];
Map<String, Object> template = JSONUtils.INSTANCE.load(new File(templateFile), JSONUtils.MAP_SUPPLIER);
try(PrintWriter pw = new PrintWriter(new File(schemaFile))) {
translate(pw, template);
}
}
}