Skip to content

Commit

Permalink
Inner fields of arbitrary depth are now supported for index fields. F…
Browse files Browse the repository at this point in the history
…ixed two NullPointerExceptions by skipping cases where the attribute or matcher of an index field do not exist in the model.
  • Loading branch information
davemoore- committed Apr 9, 2018
1 parent 19f7047 commit 75d4fb8
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 13 deletions.
20 changes: 20 additions & 0 deletions src/main/java/io/zentity/model/IndexField.java
Expand Up @@ -13,23 +13,28 @@ public class IndexField {
Arrays.asList("attribute")
);
private static final Pattern REGEX_EMPTY = Pattern.compile("^\\s*$");
private static final Pattern REGEX_PERIOD = Pattern.compile("\\.");

private final String index;
private final String name;
private String path;
private String pathParent;
private String attribute;
private String matcher;

public IndexField(String index, String name, JsonNode json) throws ValidationException {
validateName(name);
this.index = index;
this.name = name;
this.nameToPaths(name);
this.deserialize(json);
}

public IndexField(String index, String name, String json) throws ValidationException, IOException {
validateName(name);
this.index = index;
this.name = name;
this.nameToPaths(name);
this.deserialize(json);
}

Expand All @@ -41,6 +46,14 @@ public String name() {
return this.name;
}

public String path() {
return this.path;
}

public String pathParent() {
return this.pathParent;
}

public String attribute() {
return this.attribute;
}
Expand All @@ -59,6 +72,13 @@ public void matcher(JsonNode value) throws ValidationException {
this.matcher = value.textValue();
}

private void nameToPaths(String name) {
String[] parts = REGEX_PERIOD.split(name);
this.path = "/" + String.join("/", parts);
if (parts.length > 1)
this.pathParent = "/" + String.join("/", Arrays.copyOf(parts, parts.length - 1));
}

private void validateName(String value) throws ValidationException {
if (REGEX_EMPTY.matcher(value).matches())
throw new ValidationException("'indices." + this.index + "' has a field with an empty name.");
Expand Down
38 changes: 25 additions & 13 deletions src/main/java/io/zentity/resolution/Job.java
Expand Up @@ -169,6 +169,15 @@ private String jsonStringFormat(String value) {
return jsonStringQuote(jsonStringEscape(value));
}

private boolean indexFieldHasMatcher(String indexName, String indexFieldName) {
String matcherName = this.model.indices().get(indexName).fields().get(indexFieldName).matcher();
if (matcherName == null)
return false;
if (this.model.matchers().get(matcherName) == null)
return false;
return true;
}

/**
* Determine if we can construct a query for a given resolver on a given index with a given input.
* Each attribute of the resolver must be mapped to a field of the index and have a matcher defined for it.
Expand Down Expand Up @@ -199,7 +208,7 @@ private boolean canQuery(String indexName, String resolverName) {
// The index field must have a matcher defined for it.
boolean hasMatcher = false;
for (String indexFieldName : this.model.indices().get(indexName).attributeIndexFieldsMap().get(attributeName).keySet()) {
if (this.model.indices().get(indexName).fields().get(indexFieldName).matcher() != null) {
if (this.indexFieldHasMatcher(indexName, indexFieldName)) {
hasMatcher = true;
break;
}
Expand Down Expand Up @@ -280,8 +289,7 @@ private void traverse() throws IOException, ValidationException {
for (String indexFieldName : this.model.indices().get(indexName).attributeIndexFieldsMap().get(attributeName).keySet()) {

// Can we use this index field?
boolean hasMatcher = this.model.indices().get(indexName).fields().get(indexFieldName).matcher() != null;
if (!hasMatcher)
if (!this.indexFieldHasMatcher(indexName, indexFieldName))
continue;

// Construct a clause for each input value for this attribute.
Expand Down Expand Up @@ -381,20 +389,24 @@ private void traverse() throws IOException, ValidationException {
TreeMap<String, JsonNode> docAttributes = new TreeMap<>();
for (String indexFieldName : this.model.indices().get(indexName).fields().keySet()) {
String attributeName = this.model.indices().get(indexName).fields().get(indexFieldName).attribute();
if (this.model.attributes().get(attributeName) == null)
continue;
String attributeType = this.model.attributes().get(attributeName).type();
if (!nextInputAttributes.containsKey(attributeName))
nextInputAttributes.put(attributeName, new HashSet<>());
// The index field name might not refer to the _source property.
// If it's not in the _source, remove the last part of the index field name from the dot notation.
// Index field names can reference multi-fields, which are not returned in the _source.
if (!nextInputAttributes.containsKey(attributeName))
nextInputAttributes.put(attributeName, new HashSet<>());
if (!doc.get("_source").has(indexFieldName))
indexFieldName = indexFieldName.split("\\.")[0];
if (doc.get("_source").has(indexFieldName)) {
JsonNode valueNode = doc.get("_source").get(indexFieldName);
docAttributes.put(attributeName, valueNode);
Object value = Attribute.convertType(attributeType, valueNode);
nextInputAttributes.get(attributeName).add(value);
}
String path = this.model.indices().get(indexName).fields().get(indexFieldName).path();
String pathParent = this.model.indices().get(indexName).fields().get(indexFieldName).pathParent();
JsonNode valueNode = doc.get("_source").at(path);
if (valueNode.isMissingNode())
valueNode = doc.get("_source").at(pathParent);
if (valueNode.isMissingNode())
continue;
docAttributes.put(attributeName, valueNode);
Object value = Attribute.convertType(attributeType, valueNode);
nextInputAttributes.get(attributeName).add(value);
}

// Modify doc metadata.
Expand Down

0 comments on commit 75d4fb8

Please sign in to comment.