org.eclipse.birt.data.oda.mongodb.internal.impl.MDbOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.data.oda.mongodb.internal.impl.MDbOperation.java

Source

/*
 *************************************************************************
 * Copyright (c) 2013 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation - initial API and implementation
 *  
 *************************************************************************
 */

package org.eclipse.birt.data.oda.mongodb.internal.impl;

import java.util.logging.Level;

import org.eclipse.birt.data.oda.mongodb.impl.MDbResultSet;
import org.eclipse.birt.data.oda.mongodb.impl.MDbResultSetMetaData;
import org.eclipse.birt.data.oda.mongodb.nls.Messages;
import org.eclipse.datatools.connectivity.oda.OdaException;

import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.Bytes;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MapReduceOutput;
import com.mongodb.ReadPreference;

/**
 * Base class for MongoDB findQuery and command operations.
 * Delegated by QueryModel to handle its operations.
 */
public class MDbOperation {
    private QueryModel m_model;

    private DBCollection m_queryCollection;
    private DBObject m_fieldsObj;
    private DBObject m_queryObj;
    private MDbResultSetMetaData m_rsMetaData;

    private static final MDbOperation sm_factory = new MDbOperation();

    static MDbOperation createQueryOperation(QueryModel queryModel) {
        if (queryModel == null || !queryModel.isValid())
            throw new IllegalArgumentException("null/invalid QueryModel"); //$NON-NLS-1$

        return queryModel.getQueryProperties().hasValidCommandOperation()
                ? sm_factory.new CommandOperation(queryModel)
                : new MDbOperation(queryModel);
    }

    private MDbOperation() {
    }

    private MDbOperation(QueryModel queryModel) {
        m_model = queryModel;
    }

    protected QueryModel getModel() {
        return m_model;
    }

    protected void setResultSetMetaData(MDbResultSetMetaData rsmd) {
        m_rsMetaData = rsmd;
    }

    MDbResultSetMetaData getResultSetMetaData() {
        return m_rsMetaData;
    }

    protected void resetPreparedState() {
        m_queryCollection = null;
        m_fieldsObj = null;
        m_queryObj = null;
        m_rsMetaData = null;
    }

    protected void prepare(DBCollection dbCollection) throws OdaException {
        resetPreparedState();

        // get individual query components from data set properties
        QueryProperties queryProps = getModel().getQueryProperties();

        // handle findQueryExpr property
        DBObject queryObj = queryProps.getFindQueryExprAsParsedObject();

        if (queryObj == null)
            queryObj = new BasicDBObject();

        // specify fields to retrieve
        DBObject fieldsObj = queryProps.getSelectedFieldsAsProjectionKeys();

        try {
            // find search-limited rows to validate the queryObj and 
            // obtain result set metadata on the specified dbCollection
            DBCursor mdRowsCursor = dbCollection.find(queryObj, fieldsObj);

            // set data set query properties on DBCursor
            // to get result set metadata; no need to include sortExpr for getting metadata
            applyPropertiesToCursor(mdRowsCursor, queryProps, true, false);

            m_rsMetaData = new MDbResultSetMetaData(mdRowsCursor, queryProps.getSelectedFieldNames(),
                    queryProps.isAutoFlattening());

            // no exception; the find arguments on specified dbCollection are valid
            m_queryCollection = dbCollection; // could be different, e.g. mapReduce output collection
            m_fieldsObj = fieldsObj;
            m_queryObj = queryObj;
        } catch (RuntimeException ex) {
            DriverUtil.getLogger().log(Level.SEVERE,
                    "Encountered RuntimeException in QueryModel#prepareQuery(DBCollection).", ex); //$NON-NLS-1$
            throw new OdaException(ex);
        }
    }

    protected MDbResultSet execute() throws OdaException {
        if (m_queryObj == null || m_queryCollection == null)
            throw new OdaException(Messages.mDbOp_invalidQueryExpr);

        try {
            DBCursor rowsCursor = m_queryCollection.find(m_queryObj, m_fieldsObj);

            // no search limit applies here; 
            // defer to MDbResultSet to set DBCursor#limit based on its maxRows
            applyPropertiesToCursor(rowsCursor, getModel().getQueryProperties(), false, true);

            return new MDbResultSet(rowsCursor, getResultSetMetaData(), getModel().getQueryProperties());
        } catch (RuntimeException ex) {
            DriverUtil.getLogger().log(Level.SEVERE, "Encountered RuntimeException: ", ex); //$NON-NLS-1$
            throw new OdaException(ex);
        }
    }

    /* 
     * Applies data set query properties and hints on DBCursor.
     */
    private void applyPropertiesToCursor(DBCursor rowsCursor, QueryProperties queryProps,
            boolean includeMetaDataSearchLimit, boolean includeSortExpr) {
        if (includeMetaDataSearchLimit) {
            Integer searchLimit = getModel().getEffectiveMDSearchLimit(queryProps);
            if (searchLimit > 0)
                rowsCursor.limit(searchLimit);
        }

        applyPropertiesToCursor(rowsCursor, queryProps, includeSortExpr);
    }

    /**
     * Applies data set query properties and hints on DBCursor, except
     * for cursor limit.
     * @see #applyPropertiesToCursor(DBCursor,QueryProperties,boolean,boolean)
     */
    static void applyPropertiesToCursor(DBCursor rowsCursor, QueryProperties queryProps, boolean includeSortExpr) {
        if (includeSortExpr) // normally done only when executing a query to get full result set
        {
            DBObject sortExprObj = null;
            try {
                sortExprObj = queryProps.getSortExprAsParsedObject();
            } catch (OdaException ex) {
                // log warning and ignore
                DriverUtil.getLogger().log(Level.WARNING,
                        Messages.bind("Unable to parse the user-defined Sort Expression: {0}", //$NON-NLS-1$
                                queryProps.getSortExpr()), ex);
            }

            if (sortExprObj != null)
                rowsCursor.sort(sortExprObj);
        }

        ReadPreference readPref = queryProps.getTaggableReadPreference();
        if (readPref != null)
            rowsCursor.setReadPreference(readPref);

        if (queryProps.getBatchSize() > 0)
            rowsCursor.batchSize(queryProps.getBatchSize());

        if (queryProps.getNumDocsToSkip() > 0)
            rowsCursor.skip(queryProps.getNumDocsToSkip());

        DBObject hintObj = queryProps.getIndexHintsAsParsedObject();
        if (hintObj != null)
            rowsCursor.hint(hintObj);
        else // try to pass the hint string value as is
        {
            String hintValue = queryProps.getIndexHints();
            if (!hintValue.isEmpty())
                rowsCursor.hint(hintValue);
        }

        if (queryProps.hasNoTimeOut())
            rowsCursor.addOption(Bytes.QUERYOPTION_NOTIMEOUT);
        if (queryProps.isPartialResultsOk())
            rowsCursor.addOption(Bytes.QUERYOPTION_PARTIAL);
    }

    protected QueryProperties getEffectiveProperties() {
        return getModel().getQueryProperties();
    }

    /* 
     * Extends MDbOperation to handle MongoDB commands.
     */
    public class CommandOperation extends MDbOperation {
        private Iterable<DBObject> m_cmdResultObjs;
        private boolean m_hasOutputCollection;

        private CommandOperation(QueryModel queryModel) {
            super(queryModel);
        }

        protected void resetPreparedState() {
            m_cmdResultObjs = null;
            m_hasOutputCollection = false;
            super.resetPreparedState();
        }

        protected void prepare(DBCollection dbCollection) throws OdaException {
            resetPreparedState();

            QueryProperties queryProps = getModel().getQueryProperties();
            QueryModel.validateCommandSyntax(queryProps.getOperationType(), queryProps.getOperationExpression());

            // call the specified command
            Iterable<DBObject> cmdResults = null;
            if (queryProps.hasAggregateCommand())
                cmdResults = callAggregateCmd(dbCollection, queryProps);
            else if (queryProps.hasRunCommand())
                cmdResults = callDBCommand(getModel().getConnectedDB(), queryProps);
            else if (queryProps.hasMapReduceCommand()) {
                MapReduceOutput mapReduceOut = callMapReduceCmd(dbCollection, queryProps);
                DBCollection mapReduceCollection = mapReduceOut.getOutputCollection();
                if (mapReduceCollection != null) {
                    // run query on the output collection
                    super.prepare(mapReduceCollection);
                    m_hasOutputCollection = true;
                    return;
                }

                cmdResults = mapReduceOut.results();
            }

            if (cmdResults == null)
                return;

            setResultSetMetaData(
                    new MDbResultSetMetaData(cmdResults, getModel().getEffectiveMDSearchLimit(queryProps),
                            queryProps.getSelectedFieldNames(), queryProps.isAutoFlattening()));

            // no exception thus far, ok to cache the command result objects
            m_cmdResultObjs = cmdResults;
        }

        protected MDbResultSet execute() throws OdaException {
            if (m_cmdResultObjs != null)
                return getCommandResults();

            return super.execute(); // default operation type
        }

        private MDbResultSet getCommandResults() throws OdaException {
            if (m_cmdResultObjs == null)
                throw new OdaException(Messages.mDbOp_noCmdResults);

            return new MDbResultSet(m_cmdResultObjs.iterator(), getResultSetMetaData(),
                    getModel().getQueryProperties());
        }

        protected QueryProperties getEffectiveProperties() {
            // Command operations do not apply the queryExpr and sortExpr, 
            // unless a MapReduce op has output to a collection
            QueryProperties queryProps = super.getEffectiveProperties();
            if (m_hasOutputCollection)
                return queryProps;

            // remove n/a queryExpr and sortExpr, if exists, from effective properties
            if (queryProps.getFindQueryExpr().isEmpty() && queryProps.getSortExpr().isEmpty())
                return queryProps; // nothing to remove

            QueryProperties effectiveProps = QueryProperties.copy(queryProps);
            effectiveProps.setFindQueryExpr(null);
            effectiveProps.setSortExpr(null);
            return effectiveProps;
        }
    }

    static Iterable<DBObject> callAggregateCmd(DBCollection dbCollection, QueryProperties queryProps)
            throws OdaException {
        if (!queryProps.hasAggregateCommand())
            return null;
        DBObject operationExprObj = queryProps.getOperationExprAsParsedObject(true);
        if (operationExprObj == null)
            return null;

        // convert user-specified operation expression to operation pipeline
        DBObject firstOp = QueryProperties.getFirstObjectSet(operationExprObj);
        if (firstOp == null)
            return null; // no valid DBObject operation

        DBObject[] addlOps = QueryProperties.getSecondaryObjectSets(operationExprObj);

        // aggregation $limit and $skip operators applies to the number 
        // of documents in the *input* pipeline, and thus cannot be used to apply
        // the searchLimit and numSkipDocuments properties defined for data set

        // $match and $sort pipeline operators are built in an aggregate command

        // execute the aggregate command
        AggregationOutput output;
        try {
            output = addlOps != null ? dbCollection.aggregate(firstOp, addlOps) : dbCollection.aggregate(firstOp);
            output.getCommandResult().throwOnError();
            return output.results();
        } catch (RuntimeException ex) {
            OdaException odaEx = new OdaException(Messages.mDbOp_aggrCmdFailed);
            odaEx.initCause(ex);
            throw odaEx;
        }
    }

    static MapReduceOutput callMapReduceCmd(DBCollection dbCollection, QueryProperties queryProps)
            throws OdaException {
        if (!queryProps.hasMapReduceCommand())
            return null;
        DBObject command = queryProps.getOperationExprAsParsedObject(false);
        if (command == null)
            return null;

        // check if mapreduce key is already specified in user-defined expression
        DBObject mapReduceCmd;
        if (command.containsField(QueryModel.MAP_REDUCE_CMD_KEY)
                || command.containsField(QueryModel.MAP_REDUCE_CMD_KEY2))
            mapReduceCmd = command;
        else {
            // add MapReduce input collection as first entry in command Map 
            mapReduceCmd = new BasicDBObject(QueryModel.MAP_REDUCE_CMD_KEY, dbCollection.getName());
            mapReduceCmd.putAll(command); // copy existing command entries
        }

        // mapReduce command's optional "limit" parameter applies to the number 
        // of documents in the *input* collection, and thus cannot be used to apply
        // the searchLimit property defined for data set

        // execute the mapreduce command
        MapReduceOutput output;
        try {
            output = dbCollection.mapReduce(mapReduceCmd);
            output.getCommandResult().throwOnError();
            return output;
        } catch (RuntimeException ex) {
            OdaException odaEx = new OdaException(
                    Messages.bind(Messages.mDbOp_mapReduceCmdFailed, queryProps.getOperationExpression()));
            odaEx.initCause(ex);
            throw odaEx;
        }
    }

    @SuppressWarnings("unchecked")
    static Iterable<DBObject> callDBCommand(DB connectedDB, QueryProperties queryProps) throws OdaException {
        if (!queryProps.hasRunCommand())
            return null;
        DBObject command = queryProps.getOperationExprAsParsedObject(false);
        if (command == null)
            return null;

        try {
            CommandResult cmdResult = connectedDB.command(command);
            cmdResult.throwOnError();

            // wrap the commandResult DBObject in an Iterable
            BasicDBList resultList = new BasicDBList();
            resultList.add(cmdResult);
            Object resultObject = resultList;
            return (Iterable<DBObject>) resultObject;
        } catch (RuntimeException ex) {
            OdaException odaEx = new OdaException(
                    Messages.bind(Messages.mDbOp_dbCmdFailed, queryProps.getOperationExpression()));
            odaEx.initCause(ex);
            throw odaEx;
        }
    }

}