/*
* RESTHeart - the Web API for MongoDB
* Copyright (C) SoftInstigate Srl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.restheart.handlers.aggregation;
import java.util.ArrayList;
import java.util.List;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.restheart.handlers.metadata.InvalidMetadataException;
import org.restheart.utils.JsonUtils;
/**
* represents a map reduce.
*
* @author Andrea Di Cesare {@literal <andrea@softinstigate.com>}
*/
public class AggregationPipeline extends AbstractAggregationOperation {
private final BsonArray stages;
public static final String STAGES_ELEMENT_NAME = "stages";
/**
* @param properties the json properties object. It must include the
* following properties:
* <ul>
* <li><code>stages</code></li>
* </ul>
* <strong>Note</strong> that the dollar prefixed operators in the stages
* must be underscore escaped, e.g. "_$exits"
* <p>
* Example: <code>
*
* aggregations: [
* {
* "type":"pipeline",
* "uri":"test_ap",
* "stages":
* [
* {"_$match": { "name": { "_$exists": true}}},
* {"_$group": { "_id": "$name", "avg_age": {"_$avg": "$age"} }}
* ]
* }]
* </code>
* @throws org.restheart.handlers.metadata.InvalidMetadataException
*/
public AggregationPipeline(BsonDocument properties)
throws InvalidMetadataException {
super(properties);
BsonValue _stages = properties.get(STAGES_ELEMENT_NAME);
if (_stages == null || !_stages.isArray()) {
throw new InvalidMetadataException("query /" + getUri()
+ "has invalid '" + STAGES_ELEMENT_NAME
+ "': " + _stages
+ "; must be an array of stage objects");
}
// chekcs that the _stages array elements are all documents
if (_stages.asArray().stream()
.anyMatch(s -> !s.isDocument())) {
throw new InvalidMetadataException("query /" + getUri()
+ "has invalid '" + STAGES_ELEMENT_NAME
+ "': " + _stages
+ "; must be an array of stage objects");
}
this.stages = _stages.asArray();
}
/**
* @return the stages
*/
public BsonArray getStages() {
return stages;
}
/**
* @param vars RequestContext.getAggregationVars()
* @return the stages, with unescaped operators and bound variables
* @throws org.restheart.handlers.metadata.InvalidMetadataException
* @throws org.restheart.metadata.hooks.QueryVariableNotBoundException
*/
public List<BsonDocument> getResolvedStagesAsList(BsonDocument vars)
throws InvalidMetadataException, QueryVariableNotBoundException {
BsonArray replacedStages = bindAggregationVariables(
JsonUtils.unescapeKeys(stages), vars)
.asArray();
List<BsonDocument> ret = new ArrayList<>();
replacedStages.stream().filter((stage) -> (stage.isDocument()))
.forEach((stage) -> {
ret.add(stage.asDocument());
});
return ret;
}
}