/* * 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.mongodb; import com.mongodb.DB; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.hadoop.MongoInputFormat; import com.mongodb.hadoop.MongoOutputFormat; import notaql.NotaQL; import notaql.datamodel.AtomValue; import notaql.datamodel.BooleanValue; import notaql.datamodel.ObjectValue; import notaql.datamodel.Value; import notaql.engines.Engine; import notaql.engines.EngineEvaluator; import notaql.engines.mongodb.datamodel.ValueConverter; import notaql.engines.mongodb.model.vdata.HashFunctionVData; import notaql.engines.mongodb.model.vdata.ListCountFunctionVData; import notaql.engines.mongodb.model.vdata.ObjectIdFunctionVData; import notaql.engines.mongodb.parser.path.MongoDBInputPathParser; import notaql.engines.mongodb.parser.path.MongoDBOutputPathParser; 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.hadoop.conf.Configuration; import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import org.bson.BSONObject; import scala.Tuple2; import java.net.ConnectException; import java.net.UnknownHostException; 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 MongoDBEngineEvaluator implements EngineEvaluator { private final TransformationParser parser; private final boolean noQuery; private Engine engine; private String databaseName; private String collectionName; private MongoClient mongoClient = null; private DB database = null; private final static Logger logger = Logger.getLogger(MongoDBEngineEvaluator.class.getName()); public MongoDBEngineEvaluator(Engine engine, TransformationParser parser, Map<String, AtomValue<?>> params) { this.engine = engine; this.parser = parser; if(!params.containsKey("database_name") || !params.containsKey("collection_name")) throw new EvaluationException( "MongoDB engine expects the following parameters on initialization: " + engine.getArguments().stream().collect(Collectors.joining(", ")) ); this.databaseName = params.get("database_name").getValue().toString(); this.collectionName = params.get("collection_name").getValue().toString(); this.noQuery = (Boolean)params.getOrDefault("no_query", new BooleanValue(false)).getValue(); } @Override public InputPathParser getInputPathParser() { return new MongoDBInputPathParser(parser); } @Override public OutputPathParser getOutputPathParser() { return new MongoDBOutputPathParser(parser); } /** * Uses the Hadoop API to get the input objects. Then uses the geneesc framewes to do tes evaluesion.es * * @param transformation * @return */ @Override public JavaRDD<ObjectValue> evaluate(Transformation transformation) { JavaSparkContext sc = NotaQL.SparkFactory.getSparkContext(); String mongoDBHost = "mongodb://" + NotaQL.prop.getProperty("mongodb_host", "localhost") + ":27017/"; Configuration config = new Configuration(); config.set("mongo.input.uri", mongoDBHost + databaseName + "." + collectionName); // add partial filter to query in mongodb if(!noQuery && transformation.getInPredicate() != null) { final BSONObject query = FilterTranslator.toMongoDBQuery(transformation.getInPredicate()); logger.info("Sending query to MongoDB: " + query.toString()); config.set("mongo.input.query", query.toString()); } final SparkTransformationEvaluator evaluator = new SparkTransformationEvaluator(transformation); JavaPairRDD<Object, BSONObject> mongoRDD = sc.newAPIHadoopRDD(config, MongoInputFormat.class, Object.class, BSONObject.class); // convert all objects in rdd to inner format final JavaRDD<Value> converted = mongoRDD.map(t -> ValueConverter.convertToNotaQL(t._2)); // filter the ones not fulfilling the input filter (queries of MongoDB are less expressive than NotaQL) 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(); String mongoDBHost = "mongodb://" + NotaQL.prop.getProperty("mongodb_host", "localhost") + ":27017/"; Configuration config = new Configuration(); config.set("mongo.output.uri", mongoDBHost + databaseName + "." + collectionName); JavaPairRDD<Object,BSONObject> output = result.mapToPair( o -> new Tuple2<>(null, (DBObject)ValueConverter.convertFromNotaQL(o)) ); if(NotaQL.prop.getProperty("log_output") != null && NotaQL.prop.getProperty("log_output").equals("true")) output.foreach(t -> logger.info("Storing object: " + t._2.toString())); else logger.info("Storing objects."); output.saveAsNewAPIHadoopFile("file:///notapplicable", Object.class, Object.class, MongoOutputFormat.class, config); logger.info("Stored result."); } private void connect() throws ConnectException { try { mongoClient = new MongoClient(NotaQL.prop.getProperty("mongodb_host", "localhost")); database = mongoClient.getDB(databaseName); } catch (UnknownHostException e) { throw new ConnectException(e.toString()); } } private void disconnect() { mongoClient.close(); } @Override public ConstructorVData getConstructor(String name) { return null; } @Override public FunctionVData getFunction(String name) { if(name.equals("LIST_COUNT")) return new ListCountFunctionVData(); if(name.equals("HASH")) return new HashFunctionVData(); if(name.equals("OBJECT_ID")) return new ObjectIdFunctionVData(); return null; } }