org.teiid.translator.mongodb.MongoDBExecutionFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.teiid.translator.mongodb.MongoDBExecutionFactory.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */
package org.teiid.translator.mongodb;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;
import java.sql.SQLXML;
import java.util.*;

import javax.resource.cci.ConnectionFactory;

import org.bson.types.Binary;
import org.teiid.core.types.*;
import org.teiid.language.*;
import org.teiid.language.visitor.SQLStringVisitor;
import org.teiid.metadata.FunctionMethod;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.mongodb.MongoDBConnection;
import org.teiid.translator.*;
import org.teiid.translator.jdbc.AliasModifier;
import org.teiid.translator.jdbc.FunctionModifier;

import com.mongodb.*;
import com.mongodb.gridfs.GridFS;
import com.mongodb.gridfs.GridFSDBFile;
import com.mongodb.gridfs.GridFSInputFile;

@Translator(name = "mongodb", description = "MongoDB Translator, reads and writes the data to MongoDB")
public class MongoDBExecutionFactory extends ExecutionFactory<ConnectionFactory, MongoDBConnection> {
    private static final String MONGO = "mongo"; //$NON-NLS-1$
    public static final Version TWO_4 = Version.getVersion("2.4"); //$NON-NLS-1$
    public static final Version TWO_6 = Version.getVersion("2.6"); //$NON-NLS-1$

    public static final String FUNC_GEO_WITHIN = "geoWithin"; //$NON-NLS-1$
    public static final String FUNC_GEO_INTERSECTS = "geoIntersects"; //$NON-NLS-1$
    public static final String FUNC_GEO_NEAR = "geoNear"; //$NON-NLS-1$
    public static final String FUNC_GEO_NEAR_SPHERE = "geoNearSphere"; //$NON-NLS-1$
    public static final String FUNC_GEO_POLYGON_WITHIN = "geoPolygonWithin"; //$NON-NLS-1$
    public static final String FUNC_GEO_POLYGON_INTERSECTS = "geoPolygonIntersects"; //$NON-NLS-1$

    public static final String[] GEOSPATIAL_FUNCTIONS = { FUNC_GEO_WITHIN, FUNC_GEO_INTERSECTS, FUNC_GEO_NEAR,
            FUNC_GEO_NEAR_SPHERE, FUNC_GEO_POLYGON_WITHIN, FUNC_GEO_POLYGON_INTERSECTS };
    public static final String AVOID_PROJECTION = "AVOID_PROJECTION"; //$NON-NLS-1$

    protected Map<String, FunctionModifier> functionModifiers = new TreeMap<String, FunctionModifier>(
            String.CASE_INSENSITIVE_ORDER);
    private Version version = TWO_4;
    private boolean useDisk = true;
    private boolean supportsAggregatesCount = true;

    public MongoDBExecutionFactory() {
        setSupportsOrderBy(true);
        setSupportsSelectDistinct(true);
        setSupportsDirectQueryProcedure(false);
        setSourceRequiredForMetadata(false);
        setSupportsInnerJoins(true);
        setSupportsOuterJoins(true);
        setSupportedJoinCriteria(SupportedJoinCriteria.KEY);
    }

    @SuppressWarnings("nls")
    @Override
    public void start() throws TranslatorException {
        super.start();

        registerFunctionModifier("+", new AliasModifier("$add"));//$NON-NLS-1$ //$NON-NLS-2$
        registerFunctionModifier("-", new AliasModifier("$subtract"));//$NON-NLS-1$ //$NON-NLS-2$
        registerFunctionModifier("*", new AliasModifier("$multiply"));//$NON-NLS-1$ //$NON-NLS-2$
        registerFunctionModifier("/", new AliasModifier("$divide"));//$NON-NLS-1$ //$NON-NLS-2$

        registerFunctionModifier(SourceSystemFunctions.CONCAT, new AliasModifier("$concat"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.SUBSTRING, new AliasModifier("$substr"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.SUBSTRING, new FunctionModifier() {
            @Override
            public List<?> translate(Function function) {
                function.setName("$substr"); //$NON-NLS-1$

                ArrayList<Expression> params = new ArrayList<Expression>();

                params.add(function.getParameters().get(0));

                // MongoDB is zero base index; Teiid is 1 based;
                params.add(LanguageFactory.INSTANCE.createFunction("-",
                        new Expression[] { function.getParameters().get(1),
                                LanguageFactory.INSTANCE.createLiteral(1, TypeFacility.RUNTIME_TYPES.INTEGER) },
                        TypeFacility.RUNTIME_TYPES.INTEGER));

                if (function.getParameters().size() == 2) {
                    function.getParameters().add(LanguageFactory.INSTANCE
                            .createLiteral(DataTypeManager.MAX_STRING_LENGTH, TypeFacility.RUNTIME_TYPES.INTEGER));
                }

                params.add(function.getParameters().get(2));

                function.getParameters().clear();
                function.getParameters().addAll(params);
                return null;
            }
        });

        registerFunctionModifier(SourceSystemFunctions.LCASE, new AliasModifier("$toLower"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.UCASE, new AliasModifier("$toUpper"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.DAYOFYEAR, new AliasModifier("$dayOfYear"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.DAYOFMONTH, new AliasModifier("$dayOfMonth"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.DAYOFWEEK, new AliasModifier("$dayOfWeek"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.YEAR, new AliasModifier("$year"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.MONTH, new AliasModifier("$month"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.WEEK, new AliasModifier("$week"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.HOUR, new AliasModifier("$hour"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.MINUTE, new AliasModifier("$minute"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.SECOND, new AliasModifier("$second"));//$NON-NLS-1$
        registerFunctionModifier(SourceSystemFunctions.IFNULL, new AliasModifier("$ifNull")); //$NON-NLS-1$      

        FunctionMethod method = null;
        method = addPushDownFunction(MONGO, FUNC_GEO_INTERSECTS, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.STRING,
                TypeFacility.RUNTIME_NAMES.DOUBLE + "[][]"); //$NON-NLS-1$ 
        method.setProperty(AVOID_PROJECTION, "true");
        method = addPushDownFunction(MONGO, FUNC_GEO_WITHIN, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.STRING,
                TypeFacility.RUNTIME_NAMES.DOUBLE + "[][]"); //$NON-NLS-1$ 
        method.setProperty(AVOID_PROJECTION, "true");
        if (getVersion().compareTo(MongoDBExecutionFactory.TWO_6) >= 0) {
            method = addPushDownFunction(MONGO, FUNC_GEO_NEAR, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                    TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE + "[]", //$NON-NLS-1$
                    TypeFacility.RUNTIME_NAMES.INTEGER, TypeFacility.RUNTIME_NAMES.INTEGER);
            method.setProperty(AVOID_PROJECTION, "true");
            method = addPushDownFunction(MONGO, FUNC_GEO_NEAR_SPHERE, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                    TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE + "[]", //$NON-NLS-1$
                    TypeFacility.RUNTIME_NAMES.INTEGER, TypeFacility.RUNTIME_NAMES.INTEGER);
            method.setProperty(AVOID_PROJECTION, "true");
        } else {
            method = addPushDownFunction(MONGO, FUNC_GEO_NEAR, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                    TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE + "[]", //$NON-NLS-1$
                    TypeFacility.RUNTIME_NAMES.INTEGER);
            method.setProperty(AVOID_PROJECTION, "true");
            method = addPushDownFunction(MONGO, FUNC_GEO_NEAR_SPHERE, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                    TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE + "[]", //$NON-NLS-1$
                    TypeFacility.RUNTIME_NAMES.INTEGER);
            method.setProperty(AVOID_PROJECTION, "true");
        }
        method = addPushDownFunction(MONGO, FUNC_GEO_POLYGON_INTERSECTS, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE,
                TypeFacility.RUNTIME_NAMES.DOUBLE, TypeFacility.RUNTIME_NAMES.DOUBLE,
                TypeFacility.RUNTIME_NAMES.DOUBLE);
        method.setProperty(AVOID_PROJECTION, "true");
        method = addPushDownFunction(MONGO, FUNC_GEO_POLYGON_WITHIN, TypeFacility.RUNTIME_NAMES.BOOLEAN,
                TypeFacility.RUNTIME_NAMES.STRING, TypeFacility.RUNTIME_NAMES.DOUBLE,
                TypeFacility.RUNTIME_NAMES.DOUBLE, TypeFacility.RUNTIME_NAMES.DOUBLE,
                TypeFacility.RUNTIME_NAMES.DOUBLE);
        method.setProperty(AVOID_PROJECTION, "true");

        registerFunctionModifier(FUNC_GEO_NEAR, new AliasModifier("$near"));//$NON-NLS-1$
        registerFunctionModifier(FUNC_GEO_NEAR_SPHERE, new AliasModifier("$nearSphere"));//$NON-NLS-1$
        registerFunctionModifier(FUNC_GEO_WITHIN, new AliasModifier("$geoWithin"));//$NON-NLS-1$
        registerFunctionModifier(FUNC_GEO_INTERSECTS, new AliasModifier("$geoIntersects"));//$NON-NLS-1$
        registerFunctionModifier(FUNC_GEO_POLYGON_INTERSECTS, new GeoPolygonFunctionModifier("$geoIntersects"));//$NON-NLS-1$
        registerFunctionModifier(FUNC_GEO_POLYGON_WITHIN, new GeoPolygonFunctionModifier("$geoWithin"));//$NON-NLS-1$
    }

    private static class GeoPolygonFunctionModifier extends FunctionModifier {
        private String functionName;

        public GeoPolygonFunctionModifier(String name) {
            this.functionName = name;
        }

        @Override
        public List<?> translate(Function function) {
            List<Expression> args = function.getParameters();
            Expression north = args.get(1);
            Expression east = args.get(2);
            Expression west = args.get(3);
            Expression south = args.get(4);

            ArrayList<Expression> points = new ArrayList<Expression>();
            points.add(new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, Arrays.asList(west, north)));
            points.add(new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, Arrays.asList(east, north)));
            points.add(new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, Arrays.asList(east, south)));
            points.add(new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, Arrays.asList(west, south)));
            points.add(new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, Arrays.asList(west, north)));

            Expression coordinates = new org.teiid.language.Array(TypeFacility.RUNTIME_TYPES.DOUBLE, points);

            Function func = LanguageFactory.INSTANCE.createFunction(this.functionName,
                    Arrays.asList(args.get(0),
                            LanguageFactory.INSTANCE.createLiteral("Polygon", TypeFacility.RUNTIME_TYPES.STRING), //$NON-NLS-1$
                            coordinates),
                    Boolean.class);
            return Arrays.asList(func);
        }
    }

    @TranslatorProperty(display = "Database Version", description = "Database Version")
    public String getDatabaseVersion() {
        return this.version.toString();
    }

    Version getVersion() {
        return this.version;
    }

    @TranslatorProperty(display = "Use Disk", description = "Use disk for aggregation processing in MongoDB", advanced = true)
    public boolean useDisk() {
        return this.useDisk;
    }

    public void setUseDisk(boolean useDisk) {
        this.useDisk = useDisk;
    }

    /**
     * Sets the database version.  See also {@link #getVersion()}
     * @param version
     */
    public void setDatabaseVersion(String version) {
        this.version = Version.getVersion(version);
    }

    public void setDatabaseVersion(Version version) {
        this.version = version;
    }

    @Override
    public MetadataProcessor<MongoDBConnection> getMetadataProcessor() {
        return new MongoDBMetadataProcessor();
    }

    public void registerFunctionModifier(String name, FunctionModifier modifier) {
        this.functionModifiers.put(name, modifier);
    }

    public Map<String, FunctionModifier> getFunctionModifiers() {
        return this.functionModifiers;
    }

    @Override
    public ResultSetExecution createResultSetExecution(QueryExpression command, ExecutionContext executionContext,
            RuntimeMetadata metadata, MongoDBConnection connection) throws TranslatorException {
        return new MongoDBQueryExecution(this, command, executionContext, metadata, connection);
    }

    @Override
    public ProcedureExecution createProcedureExecution(Call command, ExecutionContext executionContext,
            RuntimeMetadata metadata, MongoDBConnection connection) throws TranslatorException {
        String nativeQuery = command.getMetadataObject().getProperty(SQLStringVisitor.TEIID_NATIVE_QUERY, false);
        if (nativeQuery != null) {
            return new MongoDBDirectQueryExecution(command.getArguments(), command, executionContext, metadata,
                    connection, nativeQuery, false, this);
        }
        throw new TranslatorException(MongoDBPlugin.Util.gs(MongoDBPlugin.Event.TEIID18011));
    }

    @Override
    public UpdateExecution createUpdateExecution(Command command, ExecutionContext executionContext,
            RuntimeMetadata metadata, MongoDBConnection connection) throws TranslatorException {
        return new MongoDBUpdateExecution(this, command, executionContext, metadata, connection);
    }

    @Override
    public ProcedureExecution createDirectExecution(List<Argument> arguments, Command command,
            ExecutionContext executionContext, RuntimeMetadata metadata, MongoDBConnection connection)
            throws TranslatorException {
        return new MongoDBDirectQueryExecution(arguments.subList(1, arguments.size()), command, executionContext,
                metadata, connection, (String) arguments.get(0).getArgumentValue().getValue(), true, this);
    }

    @Override
    public boolean useAnsiJoin() {
        return true;
    }

    @Override
    public boolean supportsSelfJoins() {
        return true;
    }

    @Override
    public boolean supportsCompareCriteriaEquals() {
        return true;
    }

    @Override
    public boolean supportsCompareCriteriaOrdered() {
        return true;
    }

    @Override
    public boolean supportsLikeCriteria() {
        return true;
    }

    @Override
    public boolean supportsOrCriteria() {
        return true;
    }

    @Override
    public boolean supportsOrderByUnrelated() {
        return true;
    }

    @Override
    public boolean supportsGroupBy() {
        return true;
    }

    @Override
    public boolean supportsHaving() {
        return true;
    }

    @Override
    public boolean supportsAggregatesSum() {
        return true;
    }

    @Override
    public boolean supportsAggregatesAvg() {
        return true;
    }

    @Override
    public boolean supportsAggregatesMin() {
        return true;
    }

    @Override
    public boolean supportsAggregatesMax() {
        return true;
    }

    @TranslatorProperty(display = "Supports Count(expr)", description = "Supports Aggregate function count with expression", advanced = true)
    public void setSupportsAggregatesCount(boolean value) {
        this.supportsAggregatesCount = value;
    }

    @Override
    public boolean supportsAggregatesCount() {
        return supportsAggregatesCount;
    }

    @Override
    public boolean supportsAggregatesCountStar() {
        return true;
    }

    @Override
    public List<String> getSupportedFunctions() {
        List<String> supportedFunctions = new ArrayList<String>();
        supportedFunctions.addAll(getDefaultSupportedFunctions());

        supportedFunctions.add(SourceSystemFunctions.CONCAT);
        supportedFunctions.add(SourceSystemFunctions.SUBSTRING);
        supportedFunctions.add(SourceSystemFunctions.LCASE);
        supportedFunctions.add(SourceSystemFunctions.UCASE);
        supportedFunctions.add(SourceSystemFunctions.DAYOFYEAR);
        supportedFunctions.add(SourceSystemFunctions.DAYOFMONTH);
        supportedFunctions.add(SourceSystemFunctions.DAYOFWEEK);
        supportedFunctions.add(SourceSystemFunctions.YEAR);
        supportedFunctions.add(SourceSystemFunctions.MONTH);
        supportedFunctions.add(SourceSystemFunctions.WEEK);
        supportedFunctions.add(SourceSystemFunctions.HOUR);
        supportedFunctions.add(SourceSystemFunctions.MINUTE);
        supportedFunctions.add(SourceSystemFunctions.SECOND);
        supportedFunctions.add(SourceSystemFunctions.IFNULL);

        return supportedFunctions;
    }

    public List<String> getDefaultSupportedFunctions() {
        return Arrays.asList(new String[] { "+", "-", "*", "/", "%" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
    }

    @Override
    public boolean supportsInCriteria() {
        return true;
    }

    @Override
    public boolean supportsNotCriteria() {
        return true;
    }

    @Override
    public boolean supportsRowLimit() {
        return true;
    }

    @Override
    public boolean supportsIsNullCriteria() {
        return true;
    }

    @Override
    public boolean supportsRowOffset() {
        return true;
    }

    @Override
    public boolean supportsBulkUpdate() {
        return false;
    }

    @Override
    public boolean supportsLikeRegex() {
        return true;
    }

    @Override
    public boolean supportsSelectExpression() {
        return true;
    }

    @Override
    public boolean supportsOnlyLiteralComparison() {
        return true;
    }

    /**
     * @param field
     * @param expectedClass
     * @return
     * @throws TranslatorException 
     */
    public Object retrieveValue(Object value, Class<?> expectedClass, DB mongoDB, String fqn, String colName)
            throws TranslatorException {
        if (value == null) {
            return null;
        }

        if (value.getClass().equals(expectedClass)) {
            return value;
        }

        if (value instanceof DBRef) {
            Object obj = ((DBRef) value).getId();
            if (obj instanceof BasicDBObject) {
                BasicDBObject bdb = (BasicDBObject) obj;
                return bdb.get(colName);
            }
            return obj;
        } else if (value instanceof java.util.Date && expectedClass.equals(java.sql.Date.class)) {
            return new java.sql.Date(((java.util.Date) value).getTime());
        } else if (value instanceof java.util.Date && expectedClass.equals(java.sql.Timestamp.class)) {
            return new java.sql.Timestamp(((java.util.Date) value).getTime());
        } else if (value instanceof java.util.Date && expectedClass.equals(java.sql.Time.class)) {
            return new java.sql.Time(((java.util.Date) value).getTime());
        } else if (value instanceof String && expectedClass.equals(BigDecimal.class)) {
            return new BigDecimal((String) value);
        } else if (value instanceof String && expectedClass.equals(BigInteger.class)) {
            return new BigInteger((String) value);
        } else if (value instanceof String && expectedClass.equals(Character.class)) {
            return new Character(((String) value).charAt(0));
        } else if (value instanceof String && expectedClass.equals(BinaryType.class)) {
            return new BinaryType(((String) value).getBytes());
        } else if (value instanceof String && expectedClass.equals(Blob.class)) {
            GridFS gfs = new GridFS(mongoDB, fqn);
            final GridFSDBFile resource = gfs.findOne((String) value);
            if (resource == null) {
                return null;
            }
            return new BlobImpl(new InputStreamFactory() {
                @Override
                public InputStream getInputStream() throws IOException {
                    return resource.getInputStream();
                }
            });
        } else if (value instanceof String && expectedClass.equals(Clob.class)) {
            GridFS gfs = new GridFS(mongoDB, fqn);
            final GridFSDBFile resource = gfs.findOne((String) value);
            if (resource == null) {
                return null;
            }
            return new ClobImpl(new InputStreamFactory() {
                @Override
                public InputStream getInputStream() throws IOException {
                    return resource.getInputStream();
                }
            }, -1);
        } else if (value instanceof String && expectedClass.equals(SQLXML.class)) {
            GridFS gfs = new GridFS(mongoDB, fqn);
            final GridFSDBFile resource = gfs.findOne((String) value);
            if (resource == null) {
                return null;
            }
            return new SQLXMLImpl(new InputStreamFactory() {
                @Override
                public InputStream getInputStream() throws IOException {
                    return resource.getInputStream();
                }
            });
        } else if (value instanceof BasicDBList) {
            BasicDBList arrayValues = (BasicDBList) value;
            //array
            if (expectedClass.isArray() && !(arrayValues.get(0) instanceof BasicDBObject)) {
                Class arrayType = expectedClass.getComponentType();
                Object array = Array.newInstance(arrayType, arrayValues.size());
                for (int i = 0; i < arrayValues.size(); i++) {
                    Object arrayItem = retrieveValue(arrayValues.get(i), arrayType, mongoDB, fqn, colName);
                    Array.set(array, i, arrayItem);
                }
                value = array;
            }
        } else if (value instanceof org.bson.types.ObjectId) {
            org.bson.types.ObjectId id = (org.bson.types.ObjectId) value;
            value = id.toStringBabble();
        } else {
            Transform transform = DataTypeManager.getTransform(value.getClass(), expectedClass);
            if (transform != null) {
                try {
                    value = transform.transform(value, expectedClass);
                } catch (TransformationException e) {
                    throw new TranslatorException(e);
                }
            }
        }
        return value;
    }

    /**
     * Mongodb only supports certain data types, Teiid need to serialize them in other compatible
     * formats, and convert them back while reading them.
     * @param value
     * @return
     */
    public Object convertToMongoType(Object value, DB mongoDB, String fqn) throws TranslatorException {
        if (value == null) {
            return null;
        }

        try {
            if (value instanceof BigDecimal) {
                return ((BigDecimal) value).doubleValue();
            } else if (value instanceof BigInteger) {
                return ((BigInteger) value).doubleValue();
            } else if (value instanceof Character) {
                return ((Character) value).toString();
            } else if (value instanceof java.sql.Date) {
                return new java.util.Date(((java.sql.Date) value).getTime());
            } else if (value instanceof java.sql.Time) {
                return new java.util.Date(((java.sql.Time) value).getTime());
            } else if (value instanceof java.sql.Timestamp) {
                return new java.util.Date(((java.sql.Timestamp) value).getTime());
            } else if (value instanceof BinaryType) {
                return new Binary(((BinaryType) value).getBytes());
            } else if (value instanceof byte[]) {
                return new Binary((byte[]) value);
            } else if (value instanceof Blob) {
                String uuid = UUID.randomUUID().toString();
                GridFS gfs = new GridFS(mongoDB, fqn);
                GridFSInputFile gfsFile = gfs.createFile(((Blob) value).getBinaryStream());
                gfsFile.setFilename(uuid);
                gfsFile.save();
                return uuid;
            } else if (value instanceof Clob) {
                String uuid = UUID.randomUUID().toString();
                GridFS gfs = new GridFS(mongoDB, fqn);
                GridFSInputFile gfsFile = gfs.createFile(((Clob) value).getAsciiStream());
                gfsFile.setFilename(uuid);
                gfsFile.save();
                return uuid;
            } else if (value instanceof SQLXML) {
                String uuid = UUID.randomUUID().toString();
                GridFS gfs = new GridFS(mongoDB, fqn);
                GridFSInputFile gfsFile = gfs.createFile(((SQLXML) value).getBinaryStream());
                gfsFile.setFilename(uuid);
                gfsFile.save();
                return uuid;
            } else if (value instanceof Object[]) {
                BasicDBList list = new BasicDBList();
                for (Object obj : (Object[]) value) {
                    list.add(obj);
                }
                return list;
            }
            return value;
        } catch (SQLException e) {
            throw new TranslatorException(e);
        }
    }

    public AggregationOptions getOptions(int batchSize) {
        if (this.version.equals(TWO_6)) {
            return AggregationOptions.builder().batchSize(batchSize)
                    .outputMode(AggregationOptions.OutputMode.CURSOR).allowDiskUse(useDisk()).build();
        }
        return AggregationOptions.builder().batchSize(batchSize).outputMode(AggregationOptions.OutputMode.INLINE)
                .build();
    }
}