/*
* 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.record.path.paths;
import java.util.stream.Stream;
import org.apache.nifi.record.path.FieldValue;
import org.apache.nifi.record.path.RecordPathEvaluationContext;
import org.apache.nifi.record.path.filter.RecordPathFilter;
public class PredicatePath extends RecordPathSegment {
private final RecordPathFilter filter;
public PredicatePath(final RecordPathSegment parent, final RecordPathFilter filter, final boolean absolute) {
super("[" + filter + "]", parent, absolute);
this.filter = filter;
}
@Override
public Stream<FieldValue> evaluate(final RecordPathEvaluationContext context) {
final Stream<FieldValue> valueStream = getParentPath().evaluate(context);
return valueStream.flatMap(fieldVal -> {
// For the duration of this Predicate, we want to consider the 'context node' to be
// whatever value is given to us in the field value. We then want to return the 'context node'
// back to what it was before this Predicate.
final FieldValue previousContextNode = context.getContextNode();
context.setContextNode(fieldVal);
try {
// Really what we want to do is filter out Stream<FieldValue> but that becomes very difficult
// to implement for the RecordPathFilter's. So, instead, we pass the FieldValue to field and
// the RecordPathEvaluationContext and receive back a Stream<FieldValue>. Since this is a Predicate,
// though, we don't want to transform our Stream - we just want to filter it. So we handle this by
// mapping the result back to fieldVal. And since this predicate shouldn't return the same field multiple
// times, we will limit the stream to 1 element. We also filter out any FieldValue whose value is null.
// This is done because if we have a predicate like [./iDoNotExist != 'hello'] then the relative path will
// return a value of null and that will be compared to 'hello'. Since they are not equal, the NotEqualsFilter
// will return 'true', so we will get back a FieldValue with a null value. This should not make the Predicate
// true.
return filter.filter(fieldVal, context)
.filter(fv -> fv.getValue() != null)
.limit(1)
.map(ignore -> fieldVal);
} finally {
context.setContextNode(previousContextNode);
}
});
}
}