NIFI-40: Added new fields to index/search on
diff --git a/nifi/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/SearchableFields.java b/nifi/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/SearchableFields.java
index 97c9880..81c7413 100644
--- a/nifi/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/SearchableFields.java
+++ b/nifi/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/SearchableFields.java
@@ -36,6 +36,7 @@
public static final SearchableField EventType = new NamedSearchableField("EventType", "eventType", "Event Type", false);
public static final SearchableField TransitURI = new NamedSearchableField("TransitURI", "transitUri", "Transit URI", false);
public static final SearchableField ComponentID = new NamedSearchableField("ProcessorID", "processorId", "Component ID", false);
+ public static final SearchableField ComponentType = new NamedSearchableField("ComponentType", "componentType", "Component Type", false);
public static final SearchableField AlternateIdentifierURI = new NamedSearchableField("AlternateIdentifierURI", "alternateIdentifierUri", "Alternate Identifier URI", false);
public static final SearchableField FileSize = new NamedSearchableField("FileSize", "fileSize", "File Size", false, SearchableFieldType.DATA_SIZE);
public static final SearchableField Details = new NamedSearchableField("Details", "details", "Details", false, SearchableFieldType.STRING);
@@ -55,7 +56,7 @@
static {
final SearchableField[] searchableFields = new SearchableField[]{
EventTime, FlowFileUUID, Filename, EventType, TransitURI,
- ComponentID, AlternateIdentifierURI, FileSize, Relationship, Details,
+ ComponentID, ComponentType, AlternateIdentifierURI, FileSize, Relationship, Details,
LineageStartDate, LineageIdentifier, ContentClaimSection, ContentClaimContainer, ContentClaimIdentifier,
ContentClaimOffset, SourceQueueIdentifier};
diff --git a/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryLexer.g b/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryLexer.g
index 0815475..4b12c13 100644
--- a/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryLexer.g
+++ b/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryLexer.g
@@ -81,7 +81,6 @@
DESC : 'DESC' | 'desc' | 'Desc';
GROUP_BY : 'GROUP BY' | 'group by' | 'Group By';
EVENT : 'EVENT' | 'event' | 'Event';
-RELATIONSHIP : 'RELATIONSHIP' | 'relationship' | 'Relationship';
// Operators
@@ -118,7 +117,11 @@
FILESIZE : 'SIZE' | 'size' | 'Size';
TYPE : 'TYPE' | 'type' | 'Type';
COMPONENT_ID : 'COMPONENTID' | 'componentid' | 'ComponentId' | 'componentId' | 'componentID' | 'ComponentID';
+COMPONENT_TYPE : 'COMPONENTTYPE' | 'componenttype' | 'ComponentType' | 'componentType';
UUID : 'UUID' | 'uuid' | 'Uuid';
+RELATIONSHIP : 'RELATIONSHIP' | 'relationship' | 'Relationship';
+DETAILS : 'DETAILS' | 'details' | 'Details';
+
// Event Types
RECEIVE : 'RECEIVE' | 'receive' | 'Receive';
diff --git a/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryParser.g b/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryParser.g
index 25410bb..88ce3e8 100644
--- a/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryParser.g
+++ b/nifi/nifi-commons/nifi-provenance-query-language/src/main/antlr3/org/apache/nifi/pql/ProvenanceQueryParser.g
@@ -75,7 +75,7 @@
selectClause : SELECT^ selectable (COMMA! selectable)*;
-selectable : function | (selectableSource ( (DOT! eventProperty^) | (LBRACKET! attribute^ RBRACKET!) )?);
+selectable : (function^ | (selectableSource ( (DOT! eventProperty^) | (LBRACKET! attribute^ RBRACKET!) )?)^) (AS IDENTIFIER)?;
selectableSource : EVENT | IDENTIFIER;
diff --git a/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/ProvenanceQuery.java b/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/ProvenanceQuery.java
index 4dda39b..808ae8b 100644
--- a/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/ProvenanceQuery.java
+++ b/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/ProvenanceQuery.java
@@ -1,42 +1,6 @@
package org.apache.nifi.pql;
-import static org.apache.nifi.pql.ProvenanceQueryParser.AND;
-import static org.apache.nifi.pql.ProvenanceQueryParser.ASC;
-import static org.apache.nifi.pql.ProvenanceQueryParser.ATTRIBUTE;
-import static org.apache.nifi.pql.ProvenanceQueryParser.AVG;
-import static org.apache.nifi.pql.ProvenanceQueryParser.COMPONENT_ID;
-import static org.apache.nifi.pql.ProvenanceQueryParser.COUNT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.DAY;
-import static org.apache.nifi.pql.ProvenanceQueryParser.EQUALS;
-import static org.apache.nifi.pql.ProvenanceQueryParser.EVENT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.EVENT_PROPERTY;
-import static org.apache.nifi.pql.ProvenanceQueryParser.FILESIZE;
-import static org.apache.nifi.pql.ProvenanceQueryParser.FROM;
-import static org.apache.nifi.pql.ProvenanceQueryParser.GROUP_BY;
-import static org.apache.nifi.pql.ProvenanceQueryParser.GT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.HOUR;
-import static org.apache.nifi.pql.ProvenanceQueryParser.IDENTIFIER;
-import static org.apache.nifi.pql.ProvenanceQueryParser.LIMIT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.LT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.MATCHES;
-import static org.apache.nifi.pql.ProvenanceQueryParser.MINUTE;
-import static org.apache.nifi.pql.ProvenanceQueryParser.MONTH;
-import static org.apache.nifi.pql.ProvenanceQueryParser.NOT;
-import static org.apache.nifi.pql.ProvenanceQueryParser.NOT_EQUALS;
-import static org.apache.nifi.pql.ProvenanceQueryParser.NUMBER;
-import static org.apache.nifi.pql.ProvenanceQueryParser.OR;
-import static org.apache.nifi.pql.ProvenanceQueryParser.ORDER_BY;
-import static org.apache.nifi.pql.ProvenanceQueryParser.RELATIONSHIP;
-import static org.apache.nifi.pql.ProvenanceQueryParser.SECOND;
-import static org.apache.nifi.pql.ProvenanceQueryParser.STARTS_WITH;
-import static org.apache.nifi.pql.ProvenanceQueryParser.STRING_LITERAL;
-import static org.apache.nifi.pql.ProvenanceQueryParser.SUM;
-import static org.apache.nifi.pql.ProvenanceQueryParser.TIMESTAMP;
-import static org.apache.nifi.pql.ProvenanceQueryParser.TRANSIT_URI;
-import static org.apache.nifi.pql.ProvenanceQueryParser.TYPE;
-import static org.apache.nifi.pql.ProvenanceQueryParser.UUID;
-import static org.apache.nifi.pql.ProvenanceQueryParser.WHERE;
-import static org.apache.nifi.pql.ProvenanceQueryParser.YEAR;
+import static org.apache.nifi.pql.ProvenanceQueryParser.*;
import java.io.IOException;
import java.util.ArrayList;
@@ -72,6 +36,8 @@
import org.apache.nifi.pql.evaluation.conversion.StringToLongEvaluator;
import org.apache.nifi.pql.evaluation.extraction.AttributeEvaluator;
import org.apache.nifi.pql.evaluation.extraction.ComponentIdEvaluator;
+import org.apache.nifi.pql.evaluation.extraction.ComponentTypeEvaluator;
+import org.apache.nifi.pql.evaluation.extraction.DetailsEvaluator;
import org.apache.nifi.pql.evaluation.extraction.RelationshipEvaluator;
import org.apache.nifi.pql.evaluation.extraction.SizeEvaluator;
import org.apache.nifi.pql.evaluation.extraction.TimestampEvaluator;
@@ -140,7 +106,16 @@
private ProvenanceQuery(final Tree tree, final String pql, final Collection<SearchableField> searchableFields, final Collection<SearchableField> searchableAttributes) {
this.tree = tree;
this.pql = pql;
- this.searchableFields = searchableFields == null ? null : Collections.unmodifiableSet(new HashSet<>(searchableFields));
+
+ if ( searchableFields == null ) {
+ this.searchableFields = null;
+ } else {
+ final Set<SearchableField> addressableFields = new HashSet<>(searchableFields);
+ addressableFields.add(SearchableFields.EventTime);
+ addressableFields.add(SearchableFields.EventType);
+ this.searchableFields = Collections.unmodifiableSet(addressableFields);
+ }
+
if (searchableAttributes == null) {
this.searchableAttributes = null;
} else {
@@ -296,6 +271,13 @@
}
private String getLabel(final Tree tree) {
+ if ( tree.getChildCount() > 0 ) {
+ final Tree childTree = tree.getChild(tree.getChildCount() - 1);
+ if ( childTree.getType() == AS ) {
+ return childTree.getChild(0).getText();
+ }
+ }
+
final int type = tree.getType();
switch (type) {
@@ -313,6 +295,17 @@
return tree.getText();
}
+
+ private void ensureSearchable(final SearchableField field, final boolean addToReferencedFields) {
+ if ( searchableFields != null && !searchableFields.contains(field) ) {
+ throw new ProvenanceQueryLanguageException("Query cannot reference " + field.getFriendlyName() + " because this field is not searchable by the repository");
+ }
+ if ( addToReferencedFields ) {
+ referencedFields.add(field.getSearchableFieldName());
+ }
+ }
+
+
private OperandEvaluator<?> buildOperandEvaluator(final Tree tree, final Clause clause) {
// When events are pulled back from an index, for efficiency purposes, we may want to know which
// fields to pull back. The fields in the WHERE clause are irrelevant because they are not shown
@@ -323,61 +316,32 @@
case EVENT_PROPERTY:
switch (tree.getChild(0).getType()) {
case FILESIZE:
- if ( searchableFields != null && !searchableFields.contains(SearchableFields.FileSize) ) {
- throw new ProvenanceQueryLanguageException("Query cannot reference FileSize because this field is not searchable by the repository");
- }
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.FileSize.getSearchableFieldName());
- }
+ ensureSearchable(SearchableFields.FileSize, isReferenceInteresting);
return new SizeEvaluator();
case TRANSIT_URI:
- if ( searchableFields != null && !searchableFields.contains(SearchableFields.TransitURI) ) {
- throw new ProvenanceQueryLanguageException("Query cannot reference TransitURI because this field is not searchable by the repository");
- }
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.TransitURI.getSearchableFieldName());
- }
+ ensureSearchable(SearchableFields.TransitURI, isReferenceInteresting);
return new TransitUriEvaluator();
case TIMESTAMP:
- // time is always indexed
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.EventTime.getSearchableFieldName());
- }
+ ensureSearchable(SearchableFields.EventTime, isReferenceInteresting);
return new TimestampEvaluator();
case TYPE:
- // type is always indexed so no need to check it
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.EventType.getSearchableFieldName());
- }
- return new TypeEvaluator();
+ ensureSearchable(SearchableFields.EventType, isReferenceInteresting);
+ return new TypeEvaluator();
case COMPONENT_ID:
- if ( searchableFields != null && !searchableFields.contains(SearchableFields.ComponentID) ) {
- throw new ProvenanceQueryLanguageException("Query cannot reference Component ID because this field is not searchable by the repository");
- }
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.ComponentID.getSearchableFieldName());
- }
-
+ ensureSearchable(SearchableFields.ComponentID, isReferenceInteresting);
return new ComponentIdEvaluator();
- // TODO: Allow Component Type to be indexed and searched
+ case COMPONENT_TYPE:
+ ensureSearchable(SearchableFields.ComponentType, isReferenceInteresting);
+ return new ComponentTypeEvaluator();
case RELATIONSHIP:
- if ( searchableFields != null && !searchableFields.contains(SearchableFields.Relationship) ) {
- throw new ProvenanceQueryLanguageException("Query cannot reference Relationship because this field is not searchable by the repository");
- }
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.Relationship.getSearchableFieldName());
- }
-
+ ensureSearchable(SearchableFields.Relationship, isReferenceInteresting);
return new RelationshipEvaluator();
case UUID:
- if ( searchableFields != null && !searchableFields.contains(SearchableFields.FlowFileUUID) ) {
- throw new ProvenanceQueryLanguageException("Query cannot reference FlowFile UUID because this field is not searchable by the repository");
- }
- if ( isReferenceInteresting ) {
- referencedFields.add(SearchableFields.FlowFileUUID.getSearchableFieldName());
- }
-
+ ensureSearchable(SearchableFields.FlowFileUUID, isReferenceInteresting);
return new UuidEvaluator();
+ case DETAILS:
+ ensureSearchable(SearchableFields.Details, isReferenceInteresting);
+ return new DetailsEvaluator();
default:
// TODO: IMPLEMENT
throw new UnsupportedOperationException("Haven't implemented extraction of property " + tree.getChild(0).getText());
diff --git a/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/evaluation/extraction/DetailsEvaluator.java b/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/evaluation/extraction/DetailsEvaluator.java
new file mode 100644
index 0000000..de69316
--- /dev/null
+++ b/nifi/nifi-commons/nifi-provenance-query-language/src/main/java/org/apache/nifi/pql/evaluation/extraction/DetailsEvaluator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.nifi.pql.evaluation.extraction;
+
+import org.apache.nifi.pql.evaluation.OperandEvaluator;
+import org.apache.nifi.provenance.ProvenanceEventRecord;
+
+public class DetailsEvaluator implements OperandEvaluator<String> {
+
+ @Override
+ public String evaluate(final ProvenanceEventRecord record) {
+ return record.getDetails();
+ }
+
+ @Override
+ public int getEvaluatorType() {
+ return org.apache.nifi.pql.ProvenanceQueryParser.DETAILS;
+ }
+
+ @Override
+ public Class<String> getType() {
+ return String.class;
+ }
+
+}
diff --git a/nifi/nifi-commons/nifi-provenance-query-language/src/test/java/org/apache/nifi/pql/TestQuery.java b/nifi/nifi-commons/nifi-provenance-query-language/src/test/java/org/apache/nifi/pql/TestQuery.java
index 3317c21..e640419 100644
--- a/nifi/nifi-commons/nifi-provenance-query-language/src/test/java/org/apache/nifi/pql/TestQuery.java
+++ b/nifi/nifi-commons/nifi-provenance-query-language/src/test/java/org/apache/nifi/pql/TestQuery.java
@@ -19,6 +19,7 @@
import org.apache.nifi.util.NiFiProperties;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
public class TestQuery {
@@ -117,6 +118,19 @@
dump(ProvenanceQuery.execute("SELECT Event['filename'], SUM(Event.size) GROUP BY Event['filename']", repo));
}
+ @Test
+ @Ignore("Not entirely implemented yet")
+ public void testAlias() throws IOException {
+ createRecords();
+ final ProvenanceQuery query = ProvenanceQuery.compile("SELECT SUM(Event.Size) AS TotalSize, COUNT(Event) AS NumEvents", null, null);
+
+ final ProvenanceResultSet rs = query.execute(repo);
+ dump(rs);
+
+ assertEquals(2, rs.getLabels().size());
+ assertEquals("TotalSize", rs.getLabels().get(0));
+ assertEquals("NumEvents", rs.getLabels().get(1));
+ }
@Test
public void testGroupBy() throws IOException {
diff --git a/nifi/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-journaling-provenance-repository/src/main/java/org/apache/nifi/provenance/journaling/index/LuceneIndexWriter.java b/nifi/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-journaling-provenance-repository/src/main/java/org/apache/nifi/provenance/journaling/index/LuceneIndexWriter.java
index 1f84891..4c14c05 100644
--- a/nifi/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-journaling-provenance-repository/src/main/java/org/apache/nifi/provenance/journaling/index/LuceneIndexWriter.java
+++ b/nifi/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-journaling-provenance-repository/src/main/java/org/apache/nifi/provenance/journaling/index/LuceneIndexWriter.java
@@ -147,6 +147,7 @@
addField(doc, SearchableFields.FlowFileUUID, event.getFlowFileUuid(), STORE_FIELDS);
addField(doc, SearchableFields.Filename, attributes.get(CoreAttributes.FILENAME.key()), STORE_FIELDS);
addField(doc, SearchableFields.ComponentID, event.getComponentId(), STORE_FIELDS);
+ addField(doc, SearchableFields.ComponentType, event.getComponentType(), STORE_FIELDS);
addField(doc, SearchableFields.AlternateIdentifierURI, event.getAlternateIdentifierUri(), STORE_FIELDS);
addField(doc, SearchableFields.EventType, event.getEventType().name(), STORE_FIELDS);
addField(doc, SearchableFields.Relationship, event.getRelationship(), STORE_FIELDS);
@@ -176,8 +177,10 @@
doc.add(new LongField(IndexedFieldNames.BLOCK_INDEX, location.getBlockIndex(), Store.YES));
doc.add(new LongField(IndexedFieldNames.EVENT_ID, location.getEventId(), Store.YES));
- for (final String lineageIdentifier : event.getLineageIdentifiers()) {
- addField(doc, SearchableFields.LineageIdentifier, lineageIdentifier, STORE_FIELDS);
+ if ( nonAttributeSearchableFields.contains(SearchableFields.LineageIdentifier) ) {
+ for (final String lineageIdentifier : event.getLineageIdentifiers()) {
+ addField(doc, SearchableFields.LineageIdentifier, lineageIdentifier, STORE_FIELDS);
+ }
}
// If it's event is a FORK, or JOIN, add the FlowFileUUID for all child/parent UUIDs.