blob: 37941566de2b12a6a1e54580e6166dd1011d503e [file] [log] [blame]
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<!--
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.
-->
<head>
<meta charset="utf-8"/>
<title>ScriptedValidateRecord</title>
<link rel="stylesheet" href="../../../../../css/component-usage.css" type="text/css"/>
<style>
h2 {margin-top: 4em}
h3 {margin-top: 3em}
td {text-align: left}
</style>
</head>
<body>
<h1>ScriptedValidateRecord</h1>
<h3>Description</h3>
<p>
The ScriptedValidateRecord Processor provides the ability to use a scripting language, such as Groovy or Jyton in order to validate Records in an incoming FlowFile.
The provided script will be evaluated against each Record in an incoming FlowFile. Each of those records will then be routed to either the "valid" or "invalid" FlowFile.
As a result, each incoming FlowFile may be broken up into two individual FlowFiles (if some records are valid and some are invalid, according to the script), or the incoming FlowFile
may have all of its Records kept together in a single FlowFile, routed to either "valid" or "invalid" (if all records are valid or if all records are invalid, according to the script).
</p>
<p>
The Processor expects an user defined script in order to determine the validity of the Records.
When creating a script, it is important to note that, unlike ExecuteScript, this Processor does not allow the script itself to expose Properties to be configured or define Relationships.
</p>
<p>
The provided script is evaluated once for each Record that is encountered in the incoming FlowFile. Each time that the script is invoked, it is expected to return a <code>boolean</code> value, which is used to determine if the given Record is valid or not:
For Records the script returns with a <code>true</code> value, the given Record is considered valid and will be included to the outgoing FlowFile which will be routed to the <code>valid</code> Relationship.
For <code>false</code> values the given Record will be added to the FlowFile routed to the <code>invalid</code> Relationship.
Regardless of the number of incoming Records the outgoing Records will be batched. For one incoming FlowFile there could be no more than one FlowFile routed to the <code>valid</code> and the <code>invalid</code> Relationships.
In case of there are no valid or invalid Record presents there will be no transferred FlowFile for the respected Relationship.
In addition to this the incoming FlowFile will be transferred to the <code>original</code> Relationship without change.
If the script returns an object that is not considered as <code>boolean</code>, the incoming FlowFile will be routed to the <code>failure</code> Relationship instead and no FlowFile will be routed to the <code>valid</code> or <code>invalid</code> Relationships.
</p>
<p>
This Processor maintains a Counter: "Records Processed" indicating the number of Records were processed by the Processor.
</p>
<h3>Variable Bindings</h3>
<p>
While the script provided to this Processor does not need to provide boilerplate code or implement any classes/interfaces, it does need some way to access the Records and other information
that it needs in order to perform its task. This is accomplished by using Variable Bindings. Each time that the script is invoked, each of the following variables will be made
available to the script:
</p>
<table>
<tr>
<th>Variable Name</th>
<th>Description</th>
<th>Variable Class</th>
</tr>
<tr>
<td>record</td>
<td>The Record that is to be processed.</td>
<td><a href="https://www.javadoc.io/doc/org.apache.nifi/nifi-record/latest/org/apache/nifi/serialization/record/Record.html">Record</a></td>
</tr>
<tr>
<td>recordIndex</td>
<td>The zero-based index of the Record in the FlowFile.</td>
<td>Long (64-bit signed integer)</td>
</tr>
<tr>
<td>log</td>
<td>The Processor's Logger. Anything that is logged to this logger will be written to the logs as if the Processor itself had logged it. Additionally, a bulletin will be created for any
log message written to this logger (though by default, the Processor will hide any bulletins with a level below WARN).</td>
<td><a href="https://www.javadoc.io/doc/org.apache.nifi/nifi-api/latest/org/apache/nifi/logging/ComponentLog.html">ComponentLog</a></td>
</tr>
<tr>
<td>attributes</td>
<td>Map of key/value pairs that are the Attributes of the FlowFile. Both the keys and the values of this Map are of type String. This Map is immutable.
Any attempt to modify it will result in an UnsupportedOperationException being thrown.</td>
<td>java.util.Map</td>
</tr>
</table>
<h3>Return Value</h3>
<p>
Each time the script is invoked, it is expected to return a <code>boolean</code> value. Return values other than <code>boolean</code>, including <code>null</code> value will be handled as
unexpected script behaviour and handled accordingly: the processing will be interrupted and the incoming FlowFile will be transferred to the <code>failure</code> relationship without further execution.
</p>
<h2>Example Scripts</h2>
<h3>Validating based on position</h3>
<p>
The following script will consider only the first 2 Records as valid.
</p>
<p>
Example Input (CSV):
</p>
<pre>
<code>
company, numberOfTrains
Boston & Maine Railroad, 3
Chesapeake & Ohio Railroad, 2
Pennsylvania Railroad, 4
Reading Railroad, 2
</code>
</pre>
<p>
Example Output (CSV) - valid Relationship:
</p>
<pre>
<code>
company, numberOfTrains
Boston & Maine Railroad, 3
Chesapeake & Ohio Railroad, 2
</code>
</pre>
<p>
Example Output (CSV) - invalid Relationship:
</p>
<pre>
<code>
company, numberOfTrains
Pennsylvania Railroad, 4
Reading Railroad, 2
</code>
</pre>
<p>
Example Script (Groovy):
</p>
<pre>
<code>
return recordIndex < 2 ? true : false
</code>
</pre>
<p>
Example Script (Python):
</p>
<pre>
<code>
_ = True if (recordIndex < 2) else False
</code>
</pre>
<h3>Validating based on Record contents</h3>
<p>
The following script will filter the Records based on their content. Any Records satisfies the condition will be part of
the FlowFile routed to the <code>valid</code> Relationship, others wil lbe routed to the <code>invalid</code> Relationship.
</p>
<p>
Example Input (JSON):
</p>
<pre>
<code>
[
{
"company": "Boston & Maine Railroad",
"numberOfTrains": 3
}, {
"company": "Chesapeake & Ohio Railroad",
"numberOfTrains": -1
}, {
"company": "Pennsylvania Railroad",
"numberOfTrains": 2
}, {
"company": "Reading Railroad",
"numberOfTrains": 4
}
]
</code>
</pre>
<p>
Example Output (CSV) - valid Relationship:
</p>
<pre>
<code>
[
{
"company": "Boston & Maine Railroad",
"numberOfTrains": 3
}, {
"company": "Pennsylvania Railroad",
"numberOfTrains": 2
}, {
"company": "Reading Railroad",
"numberOfTrains": 4
}
]
</code>
</pre>
<p>
Example Output (CSV) - invalid Relationship:
</p>
<pre>
<code>
[
{
"company": "Chesapeake & Ohio Railroad",
"numberOfTrains": -1
}
]
</code>
</pre>
<p>
Example Script (Groovy):
</p>
<pre>
<code>
if (record.getValue("numberOfTrains").toInteger() >= 0) {
return true;
} else {
return false;
}
</code>
</pre>
<p>
Example Script (Python):
</p>
<pre>
<code>
trains = record.getValue("numberOfTrains")
_ = True if (trains >= 0) else False
</code>
</pre>
</body>
</html>