All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bridgedb.sql.SQLIdMapper Maven / Gradle / Ivy

The newest version!
// BridgeDb,
// An abstraction layer for identifier mapping services, both local and online.
//
// Copyright 2006-2009  BridgeDb developers
// Copyright 2012-2013  Christian Y. A. Brenninkmeijer
// Copyright 2012-2013  OpenPhacts
//
// 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 org.bridgedb.sql;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.bridgedb.DataSource;
import org.bridgedb.IDMapper;
import org.bridgedb.IDMapperCapabilities;
import org.bridgedb.IDMapperException;
import org.bridgedb.Xref;
import org.bridgedb.impl.InternalUtils;
import org.bridgedb.pairs.CodeMapper;
import org.bridgedb.pairs.IdSysCodePair;
import org.bridgedb.utils.BridgeDBException;

/**
 * Builds on the SQLListener to implement the Standard BridgeDB functions of IDMapper and IDMapperCapabilities.
 *
 * This Allows the OPS version to function as any other BridgeDB implementation
 *
 * @author Christian
 */
public class SQLIdMapper extends SQLListener implements IDMapper, IDMapperCapabilities {

    /** 
     * FreeSearch has proven to be very slow over large database so for large database we say it is unsupported.
     */
    private static final int FREESEARCH_CUTOFF = 100000;      
    //Internal parameters
    protected static final int DEFAULT_LIMIT = 1000;
    /**
     * This identifies version of SQL such as MySQL that use "LIMIT" to restrict the number of tuples returned.
     */
    private final boolean useLimit;
    /**
     * This identifies version of SQL such as Virtuoso that use "TOP" to restrict the number of tuples returned.
     */
    private final boolean useTop;
    /**
     * Map between IdSysCodePair and Xref or DataSources 
     */
    protected final CodeMapper codeMapper;
    
    private static final Logger logger = Logger.getLogger(SQLIdMapper.class);

    public SQLIdMapper(boolean dropTables, CodeMapper codeMapper) throws BridgeDBException{
        super(dropTables);
        useLimit = SqlFactory.supportsLimit();
        useTop = SqlFactory.supportsTop();
        this.codeMapper = codeMapper;
     }   

    //*** IDMapper Methods 
    
    @Override
    public Map> mapID(Collection srcXrefs, DataSource... tgtDataSources) throws IDMapperException {
        return InternalUtils.mapMultiFromSingle(this, srcXrefs, tgtDataSources);
    }

    @Override
    public Set mapID(Xref xref, DataSource... tgtDataSources) throws BridgeDBException {
        IdSysCodePair ref = toIdSysCodePair(xref);
        if (ref == null) {
            logger.debug("mapId called with a badXref " + xref);
            return new HashSet();
        }
        String[] tgtSysCodes = toCodes(tgtDataSources);
        Set pairs = mapID(ref, tgtSysCodes);
        return toXrefs(pairs);
    }

    private Set mapID(IdSysCodePair ref, String... tgtSysCodes) throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
            query.append(TARGET_ID_COLUMN_NAME);
                query.append(", ");
            query.append(TARGET_DATASOURCE_COLUMN_NAME);
        query.append(" FROM ");
            query.append(MAPPING_TABLE_NAME);
                query.append(", ");
            query.append(MAPPING_SET_TABLE_NAME);
        appendMappingJoinMapping(query);
        appendSourceIdSysCodePair(query, ref);
        if (tgtSysCodes != null &&tgtSysCodes.length > 0){    
            query.append(" AND ( ");
            query.append(TARGET_DATASOURCE_COLUMN_NAME);
            query.append(" = '");
            query.append(tgtSysCodes[0]);
            query.append("' ");
            for (int i = 1; i < tgtSysCodes.length; i++){
                query.append(" OR ");
                query.append(TARGET_DATASOURCE_COLUMN_NAME);
                query.append(" = '");
                query.append(tgtSysCodes[i]);
                query.append("'");
            }
            query.append(")");
        }
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }     
        Set results = resultSetToIdSysCodePairSet(rs);
        if (tgtSysCodes.length == 0){
           results.add(ref); 
        } else {
            for (String tgtSysCode: tgtSysCodes){
                if (ref.getSysCode().equals(tgtSysCode)){
                    results.add(ref);
                }
            }
        }
        if (results.size() <= 1){
            String targets = "";
            for (String tgtSysCode: tgtSysCodes){
                targets+= tgtSysCode + ", ";
            }
            if (targets.isEmpty()){
                targets = "all DataSources";
            }
            if (logger.isDebugEnabled()){
                if (results.isEmpty()){
                    logger.debug("Unable to map " + ref + " to any results for " + targets);
                } else {
                    logger.debug("Only able to map " + ref + " to itself for " + targets);
                }
            }
        } else {
            if (logger.isDebugEnabled()){
                logger.debug("Mapped " + ref + " to " + results.size() + " results");
            }
        }
        close(statement, rs);
        return results;
    }

 	/**
	 * Get all cross-references for the given entity, restricting the
	 * result to contain only references from the given set of data sources.
	 * @param xref the entity to get cross-references for. 
     * @param tgtDataSource target ID types/data source. Can not be null
	 * @return A Set containing the cross references, or an empty
	 * Set when no cross references could be found. This method does not return null.
	 * @throws BridgeDBException if the mapping service is (temporarily) unavailable 
	 */
    public Set mapID(Xref xref, DataSource tgtDataSource) throws BridgeDBException {
        IdSysCodePair ref = toIdSysCodePair(xref);
        if (ref == null) {
            if (logger.isDebugEnabled()){
                logger.debug("mapId called with a badXref " + xref);
            }
            return new HashSet();
        }
        if (tgtDataSource == null){
            throw new BridgeDBException("Target DataSource can not be null");
        }
        String tgtSysCode = toCode(tgtDataSource);
        Set pairs = mapID(ref, tgtSysCode);
        return toXrefs(pairs);
    }

    private Set mapID(IdSysCodePair ref, String tgtSysCode) throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
            query.append(TARGET_ID_COLUMN_NAME);
                query.append(", ");
            query.append(TARGET_DATASOURCE_COLUMN_NAME);
        query.append(" FROM ");
            query.append(MAPPING_TABLE_NAME);
                query.append(", ");
            query.append(MAPPING_SET_TABLE_NAME);
        appendMappingJoinMapping(query);
        appendSourceIdSysCodePair(query, ref);
        query.append(" AND ");
            query.append(TARGET_DATASOURCE_COLUMN_NAME);
            query.append(" = '");
            query.append(tgtSysCode);
            query.append("' ");

        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
        Set pairs = resultSetToIdSysCodePairSet(rs);
        if (ref.getSysCode().equals(tgtSysCode)){
            pairs.add(ref);
        }
        close(statement, rs);
        return pairs;
    }

    @Override
    public boolean xrefExists(Xref xref) throws BridgeDBException {
        IdSysCodePair ref = toIdSysCodePair(xref);
        if (ref == null) {
            return false;
        }
        return IdSysCodePairExists(ref);
   }

   protected boolean IdSysCodePairExists(IdSysCodePair ref) throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
        appendTopConditions(query, 0, 1); 
        query.append(TARGET_ID_COLUMN_NAME);
        query.append(" FROM ");
        query.append(MAPPING_TABLE_NAME);
        query.append(", ");
        query.append(MAPPING_SET_TABLE_NAME);
        query.append(" WHERE ");
        query.append(MAPPING_SET_ID_COLUMN_NAME);
        query.append(" = ");
        query.append(MAPPING_SET_DOT_ID_COLUMN_NAME);
        appendSourceIdSysCodePair(query, ref);
        appendLimitConditions(query,0, 1);
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
            boolean result = rs.next();
            if (logger.isDebugEnabled()){
                logger.debug(ref + " exists = " + result);
            }
            close(statement, rs);
            return result;
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
   }

    @Override
    public Set freeSearch(String text, int limit) throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
        appendTopConditions(query, 0, limit); 
        query.append(SOURCE_ID_COLUMN_NAME);
        query.append(" as ");
        query.append(TARGET_ID_COLUMN_NAME);
        query.append(", ");
        query.append(SOURCE_DATASOURCE_COLUMN_NAME);
        query.append(" as ");
        query.append(TARGET_DATASOURCE_COLUMN_NAME);
        query.append(" FROM ");
        query.append(MAPPING_TABLE_NAME);
        query.append(", ");
        query.append(MAPPING_SET_TABLE_NAME);
        query.append(" WHERE ");
        query.append(MAPPING_SET_ID_COLUMN_NAME);
        query.append(" = ");
        query.append(MAPPING_SET_DOT_ID_COLUMN_NAME);
        query.append(" AND ");
        query.append(SOURCE_ID_COLUMN_NAME);
        query.append(" = '");
            query.append(text);
            query.append("' ");
        appendLimitConditions(query,0, limit);
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
        Set pairs = resultSetToIdSysCodePairSet(rs);
        if (logger.isDebugEnabled()){
            logger.debug("Freesearch for " + text + " gave " + pairs.size() + " results");
        }
        close(statement, rs);
        return toXrefs(pairs);
    }

    @Override
    public IDMapperCapabilities getCapabilities() {
        return this;
    }

    //BridgeDB expects that once close is called isConnected will return false
    private boolean isConnected = true;
    
    @Override
    /** {@inheritDoc} */
    public void close() {
        isConnected = false;
        closeConnection();
    }

    @Override
    /** {@inheritDoc} */
    public boolean isConnected() { 
        if (isConnected){
            try {
                sqlAccess.getConnection();
                return true;
            } catch (BridgeDBException ex) {
                return false;
            }
        }
        if (logger.isDebugEnabled()){
            logger.debug("isConnected() returned  " + isConnected);
        }
        return isConnected; 
    }
    // ***IDMapperCapabilities
    
    @Override
    public boolean isFreeSearchSupported() {
        if (logger.isDebugEnabled()){
            logger.debug("isFreeSearchSupported() returned  true");
        }
        return true;
    }

    @Override
    public Set getSupportedSrcDataSources() throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
        query.append(SOURCE_DATASOURCE_COLUMN_NAME);
        query.append(" as ");
        query.append(SYSCODE_COLUMN_NAME);
        query.append(" FROM ");
        query.append(MAPPING_SET_TABLE_NAME);
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
        Set results = resultSetToDataSourceSet(rs);
        if (logger.isDebugEnabled()){
            logger.debug("getSupportedSrcDataSources() returned " + results.size() + " results");
        }
        close(statement, rs);
        return results;        
    }

    @Override
    public Set getSupportedTgtDataSources() throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
        query.append(TARGET_DATASOURCE_COLUMN_NAME);
        query.append(" as ");
        query.append(SYSCODE_COLUMN_NAME);
        query.append(" FROM ");
        query.append(MAPPING_SET_TABLE_NAME);
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
        Set results = resultSetToDataSourceSet(rs);
        if (logger.isDebugEnabled()){
            logger.debug("getSupportedTgtDataSources() returned " + results.size() + " results");
        }
        close(statement, rs);
        return results;        
    }

    @Override
    public boolean isMappingSupported(DataSource src, DataSource tgt) throws BridgeDBException {
        StringBuilder query = new StringBuilder();
        query.append("SELECT ");
        query.append(ID_COLUMN_NAME);
        query.append(" FROM ");
        query.append(MAPPING_SET_TABLE_NAME);
        query.append(" WHERE ");
        query.append(SOURCE_DATASOURCE_COLUMN_NAME);
        query.append(" = '");
            query.append(getDataSourceKey(src));
            query.append("' ");        
        query.append(" AND ");
        query.append(TARGET_DATASOURCE_COLUMN_NAME);
        query.append(" = '");
            query.append(getDataSourceKey(tgt));
            query.append("' ");        
        
        Statement statement = this.createStatement();
        ResultSet rs = null;
        try {
            rs = statement.executeQuery(query.toString());
            boolean result = rs.next();
            if (logger.isDebugEnabled()){
                logger.debug("isMappingSupported " + src + " to " + tgt + " is " + result);
            }
            close(statement, rs);
            return result;
        } catch (SQLException ex) {
            close(statement, rs);
            throw new BridgeDBException("Unable to run query. " + query, ex);
        }    
    }

    @Override
    public String getProperty(String key) {
        String query = "SELECT DISTINCT " + PROPERTY_COLUMN_NAME 
                + " FROM " + PROPERTIES_TABLE_NAME 
                + " WHERE " + KEY_COLUMN_NAME + " = '" + key + "'";
        Statement statement = null;
        ResultSet rs = null;
        try {
            statement = this.createStatement();
            rs = statement.executeQuery(query);
            if (rs.next()){
                String result = rs.getString("property");
                if (logger.isDebugEnabled()){
                    logger.debug("property " + key + " is " + result);
                }
                close(statement, rs);
                return result;
            } else {
                if (logger.isDebugEnabled()){
                    logger.warn("No property " + key + " found! ");
                }
                close(statement, rs);
                return null;
            }
        } catch (Exception ex) {
            close(statement, rs);
            return null;
        }
    }

    @Override
    public Set getKeys() {
        HashSet results = new HashSet();
        String query = "SELECT " + KEY_COLUMN_NAME
                + " FROM " + PROPERTIES_TABLE_NAME
                + " WHERE " + IS_PUBLIC_COLUMN_NAME + " = 1"; //one works where isPublic is a boolean
        Statement statement = null;
        ResultSet rs = null;
        try {
            statement = this.createStatement();
            rs = statement.executeQuery(query);
            while (rs.next()){
                results.add(rs.getString(KEY_COLUMN_NAME));
            }
            if (logger.isDebugEnabled()){
                logger.warn("getKeys() returned " + results.size() + " keys! ");
            }
            close(statement, rs);
            return results;
        } catch (Exception ex) {
            close(statement, rs);
            logger.error("Error getting keys ", ex);
            return null;
        }
    }

    //**** Support methods 
    
    /**
     * Check if the Xref is invalid in some way.
     * 

* For example if it is null or if either the Id or DataSource part is null. * @param ref - the xref to check * @return if the xref whether or not the xref is valid */ protected final boolean badXref(Xref ref) { if (ref == null) return true; if (ref.getId() == null || ref.getId().isEmpty()) return true; if (ref.getDataSource() == null ) return true; return false; } /** * Add a condition to the query that only mappings with a specific source IdSysCodePair should be used. * @param query Query to add to. * @param ref IdSysCodePair that forms the base of the condition. */ protected final void appendSourceIdSysCodePair(StringBuilder query, IdSysCodePair ref){ query.append(" AND "); query.append(SOURCE_ID_COLUMN_NAME); query.append(" = '"); query.append(ref.getId()); query.append("' "); query.append(" AND "); query.append(SOURCE_DATASOURCE_COLUMN_NAME); query.append(" = '"); query.append(ref.getSysCode()); query.append("' "); } /** * Add a condition to the query that only mappings with a specific source Xref should be used. * @param query Query to add to. * @param id - identifier of the Xref * @param sysCode - system code of the source database of the Xref */ protected final void appendSourceXref(StringBuilder query, String id, String sysCode){ query.append(" AND "); query.append(SOURCE_ID_COLUMN_NAME); query.append(" = '"); query.append(id); query.append("' "); query.append(" AND "); query.append(SOURCE_DATASOURCE_COLUMN_NAME); query.append(" = '"); query.append(sysCode); query.append("' "); } /** * Converts a ResultSet to a Set of individual Xrefs. * @throws BridgeDBException */ final Set resultSetToIdSysCodePairSet(ResultSet rs) throws BridgeDBException { HashSet results = new HashSet(); try { while (rs.next()){ String id = rs.getString(TARGET_ID_COLUMN_NAME); String sysCode = rs.getString(TARGET_DATASOURCE_COLUMN_NAME); IdSysCodePair pair = new IdSysCodePair(id, sysCode); results.add(pair); } return results; } catch (SQLException ex) { throw new BridgeDBException("Unable to parse results.", ex); } } /** * Converts a ResultSet to a Set of BridgeDB DataSources by obtaining the SysCode from the ResultsSet a * and looking the DataSource up in the DataSource Registry. *

* All DataSources have been preloaded by loadDataSources() called during the super constructor. * @param rs * @return * @throws BridgeDBException */ private Set resultSetToDataSourceSet(ResultSet rs) throws BridgeDBException { HashSet results = new HashSet(); try { while (rs.next()){ String sysCode = rs.getString(SYSCODE_COLUMN_NAME); results.add(DataSource.getExistingBySystemCode(sysCode)); } return results; } catch (SQLException ex) { throw new BridgeDBException("Unable to parse results.", ex); } } /** * Adds the limit condition if this is the support method for limitng the number of results. *

* System such as MYSQL use a Limit clause at the end of the query. Where applicable this is added here. * @param query Query to add Limit to * @param position The offset of the fragment of results to return * @param limit The size of the fragment of results to return */ protected void appendLimitConditions(StringBuilder query, Integer position, Integer limit){ if (useLimit){ if (position == null) { position = 0; } if (limit == null){ limit = DEFAULT_LIMIT; } query.append(" LIMIT " + position + ", " + limit); } } /** * Adds the top condition if this is the support method for limitng the number of results. *

* System such as Virtuosos use a Top clause directly after the select. Where applicable this is added here. * @param query Query to add TOP to * @param position The offset of the fragment of results to return * @param limit The size of the fragment of results to return */ protected void appendTopConditions(StringBuilder query, Integer position, Integer limit){ if (useTop){ if (position == null) { position = 0; } if (limit == null){ limit = DEFAULT_LIMIT; } query.append("TOP " + position + ", " + limit + " "); } } protected final void appendMappingJoinMapping(StringBuilder query){ query.append(" WHERE "); query.append(MAPPING_SET_ID_COLUMN_NAME); query.append(" = "); query.append(MAPPING_SET_DOT_ID_COLUMN_NAME); } protected final String[] toCodes(DataSource[] tgtDataSources) { if (tgtDataSources == null || tgtDataSources.length == 0){ return new String[0]; } String[] results = new String[tgtDataSources.length]; for (int i = 0; i < tgtDataSources.length; i++){ results[i] = tgtDataSources[i].getSystemCode(); } return results; } protected final IdSysCodePair toIdSysCodePair(Xref xref) throws BridgeDBException { if (xref == null || xref.getId() == null || xref.getDataSource() == null){ return null; } return codeMapper.toIdSysCodePair(xref); } protected final Set toXrefs(Set pairs) throws BridgeDBException { HashSet results = new HashSet(); for (IdSysCodePair pair:pairs){ results.add(codeMapper.toXref(pair)); } return results; } protected final String toCode(DataSource tgtDataSource) { if (tgtDataSource == null){ return null; } return tgtDataSource.getSystemCode(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy