/*
* Copyright 2015 by Thomas Lottermann
*
* 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 notaql.engines.json;
import notaql.NotaQL;
import notaql.datamodel.AtomValue;
import notaql.datamodel.ObjectValue;
import notaql.datamodel.Value;
import notaql.engines.Engine;
import notaql.engines.EngineEvaluator;
import notaql.engines.json.datamodel.ValueConverter;
import notaql.engines.json.path.JSONInputPathParser;
import notaql.engines.json.path.JSONOutputPathParser;
import notaql.evaluation.SparkTransformationEvaluator;
import notaql.model.EvaluationException;
import notaql.model.Transformation;
import notaql.model.vdata.ConstructorVData;
import notaql.model.vdata.FunctionVData;
import notaql.parser.TransformationParser;
import notaql.parser.path.InputPathParser;
import notaql.parser.path.OutputPathParser;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.json.JSONObject;
import java.net.ConnectException;
import java.util.HashSet;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Created by thomas on 23.02.15.
*/
public class JSONEngineEvaluator implements EngineEvaluator {
private final TransformationParser parser;
private Engine engine;
private String path;
private final static Logger logger = Logger.getLogger(JSONEngineEvaluator.class.getName());
public JSONEngineEvaluator(Engine engine, TransformationParser parser, Map<String, AtomValue<?>> params) {
this.engine = engine;
this.parser = parser;
if (!params.keySet().equals(new HashSet<>(engine.getArguments())))
throw new EvaluationException(
"JSON engine expects the following parameter on initialization: " +
engine.getArguments().stream().collect(Collectors.joining(", "))
);
this.path = params.get("path").getValue().toString();
}
@Override
public InputPathParser getInputPathParser() {
return new JSONInputPathParser(parser);
}
@Override
public OutputPathParser getOutputPathParser() {
return new JSONOutputPathParser(parser);
}
/**
* Uses the Hadoop API to get the input objects. Then uses the generic framework to do the evaluation.
*
* @param transformation
* @return
*/
@Override
public JavaRDD<ObjectValue> evaluate(Transformation transformation) {
JavaSparkContext sc = NotaQL.SparkFactory.getSparkContext();
final SparkTransformationEvaluator evaluator = new SparkTransformationEvaluator(transformation);
final JavaRDD<Object> jsonRDD = sc.textFile(path)
.filter(s -> !s.equals("[") && !s.equals("]")) // if it consists of an array containing objects: just ignore the array TODO: this is kind of Q&D
.map(JSONObject::new);
// convert all objects in rdd to inner format
final JavaRDD<Value> converted = jsonRDD.map(ValueConverter::convertToNotaQL);
// filter the ones not fulfilling the input filter
final JavaRDD<Value> filtered = converted.filter(v -> transformation.satisfiesInPredicate((ObjectValue) v));
// process all input
return evaluator.process(filtered);
}
/**
* Uses the Hadoop API to store results
* @param result
*/
@Override
public void store(JavaRDD<ObjectValue> result) {
logger.info("Storing result.");
JavaSparkContext sc = NotaQL.SparkFactory.getSparkContext();
JavaRDD<Object> output = result.map(ValueConverter::convertFromNotaQL);
if(NotaQL.prop.getProperty("log_output") != null && NotaQL.prop.getProperty("log_output").equals("true"))
output.foreach(s -> logger.info("Storing object: " + s.toString()));
else
logger.info("Storing objects.");
output.map(Object::toString).saveAsTextFile(path);
logger.info("Stored result.");
}
private void connect() throws ConnectException {
}
private void disconnect() {
}
@Override
public ConstructorVData getConstructor(String name) {
return null;
}
@Override
public FunctionVData getFunction(String name) {
return null;
}
}