com.hazelcast.query.impl.getters.MultiResult Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
*
* Licensed 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 com.hazelcast.query.impl.getters;
import java.util.ArrayList;
import java.util.List;
/**
* Represents multiple results from a single attribute extraction.
*
* MultiResult is an aggregate of results that is returned if the ValueExtractor returns multiple results due to a
* reduce operation executed on a hierarchy of values.
*
* It sounds counter-intuitive, but a single extraction may return multiple values when arrays or collections are
* involved.
*
* Let's have a look at the following data structure:
*
* class Swap {
* Leg legs[2];
* }
* class Leg {
* String currency;
* }
*
*
* The following extraction of the currency attribute legs[any].currency
results in two currencies for each
* Leg. In order to return both values in one result of the extract operation both currencies are returned in a
* single MultiResult object where each result contains a name of the currency.
* It allows the user to operate on multiple "reduced" values as if they were single-values.
*
* Let's have a look at the following queries:
*
* - leg[1].currency = 'EUR'
* - leg[any].currency = 'EUR'
*
* In the first query, the extraction will return just one currency, whereas the extraction in the second query will
* return a MultiResult containing two currencies.
* During the evaluation of the "=" equals predicate the MultiResult will be "unfolded" and the condition will
* evaluated against all currencies from the MultiResult. If there is "any" currency that matches the condition the
* whole predicate will be evaluated to "true" and the matching Swap will be returned.
* As a result all Swaps will be returned where there's at lease one Leg with EUR currency.
*
* Other examples:
*
* - legs -> returns one 'single-value' result -> a collection of values
* - legs[0] -> returns one 'single result' - a Leg object
* - legs[0].currency -> returns one 'multi value' result - an array of Legs
* - legs[any] -> returns a MultiResult - that contains a collection of Leg objects
* - legs[any].currency -> returns a MultiResult - that contains a collection of String objects
*
* @param type of the underlying result store in the MultiResult
*/
public class MultiResult {
private List results;
/**
* Indicates that the result of a multi-result evaluation using [any] operator
* didn't reach any data since the field where the [any] operator is used was null or empty.
*
* Allows differentiating whether there is a null value in the MultiResult
* that is a result of a null or empty target in the expression.
*
* For query evaluation the difference is not important - and we need to return null in both cases there.
* For aggregations it makes a difference and we need this context knowledge there.
*/
private boolean nullOrEmptyTarget;
public MultiResult() {
this.results = new ArrayList();
}
public MultiResult(List results) {
this.results = results;
}
/**
* @param result result to be added to this MultiResult
*/
public void add(T result) {
results.add(result);
}
public void addNullOrEmptyTarget() {
if (!nullOrEmptyTarget) {
// we don't want to store more than one null if we reach a null/empty target
// it's enough to have one for the query evaluation (it's for null matching)
nullOrEmptyTarget = true;
results.add(null);
}
}
/**
* @return a mutable underlying list of collected results
*/
public List getResults() {
return results;
}
/**
* @return true if the MultiResult is empty; false otherwise
*/
public boolean isEmpty() {
return results.isEmpty();
}
public boolean isNullEmptyTarget() {
return nullOrEmptyTarget;
}
public void setNullOrEmptyTarget(boolean nullOrEmptyTarget) {
this.nullOrEmptyTarget = nullOrEmptyTarget;
}
}