annis.sqlgen.FindSqlGenerator.java Source code

Java tutorial

Introduction

Here is the source code for annis.sqlgen.FindSqlGenerator.java

Source

/*
 * Copyright 2009-2011 Collaborative Research Centre SFB 632
 *
 * 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 annis.sqlgen;

import static annis.sqlgen.TableAccessStrategy.NODE_TABLE;
import static annis.sqlgen.TableAccessStrategy.CORPUS_TABLE;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.dao.DataAccessException;

import annis.service.objects.Match;
import annis.model.QueryNode;
import annis.ql.parser.QueryData;
import annis.service.internal.QueryService;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.LinkedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Generates identifers for salt which are needed for the
 * {@link QueryService#subgraph(java.lang.String, java.lang.String, java.lang.String)}
 *
 * @author Benjamin Weienfels
 */
public class FindSqlGenerator extends AbstractUnionSqlGenerator<List<Match>>
        implements SelectClauseSqlGenerator<QueryData>, OrderByClauseSqlGenerator<QueryData> {

    private static final Logger log = LoggerFactory.getLogger(FindSqlGenerator.class);

    // optimize DISTINCT operation in SELECT clause
    private boolean optimizeDistinct;
    private boolean sortSolutions;
    private boolean outputCorpusPath;
    private CorpusPathExtractor corpusPathExtractor;

    @Override
    public String selectClause(QueryData queryData, List<QueryNode> alternative, String indent) {
        int maxWidth = queryData.getMaxWidth();
        Validate.isTrue(alternative.size() <= maxWidth, "BUG: nodes.size() > maxWidth");

        boolean isDistinct = false || !optimizeDistinct;
        List<String> ids = new ArrayList<String>();
        int i = 0;

        for (QueryNode node : alternative) {
            ++i;

            TableAccessStrategy tblAccessStr = tables(node);
            ids.add(tblAccessStr.aliasedColumn(NODE_TABLE, "id") + " AS id" + i);
            ids.add(tblAccessStr.aliasedColumn(NODE_TABLE, "node_name") + " AS node_name" + i);
            if (outputCorpusPath) {
                ids.add(tblAccessStr.aliasedColumn(CORPUS_TABLE, "path_name") + " AS path_name" + i);
            }
            if (tblAccessStr.usesRankTable()) {
                isDistinct = true;
            }
        }

        for (i = alternative.size(); i < maxWidth; ++i) {
            ids.add("NULL");
        }

        ids.add(tables(alternative.get(0)).aliasedColumn(NODE_TABLE, "toplevel_corpus"));

        ids.add(tables(alternative.get(0)).aliasedColumn(NODE_TABLE, "corpus_ref"));

        return (isDistinct ? "DISTINCT" : "") + "\n" + indent + TABSTOP + StringUtils.join(ids, ", ");
    }

    @Override
    protected void appendOrderByClause(StringBuffer sb, QueryData queryData, List<QueryNode> alternative,
            String indent) {
        // only use ORDER BY clause if result has to be sorted
        if (!sortSolutions) {
            return;
        }
        // don't use ORDER BY clause if we are only counting saves a sort
        List<LimitOffsetQueryData> extensions = queryData.getExtensions(LimitOffsetQueryData.class);

        if (extensions.size() > 0) {
            super.appendOrderByClause(sb, queryData, alternative, indent);
        }
    }

    @Override
    public String orderByClause(QueryData queryData, List<QueryNode> alternative, String indent) {
        List<String> ids = new ArrayList<String>();
        for (int i = 1; i <= queryData.getMaxWidth(); ++i) {
            ids.add("id" + i);
        }
        return StringUtils.join(ids, ", ");
    }

    @Override
    public List<Match> extractData(ResultSet rs) throws SQLException, DataAccessException {
        List<Match> matches = new ArrayList<Match>();
        int rowNum = 0;
        while (rs.next()) {
            matches.add(mapRow(rs, ++rowNum));
        }
        return matches;
    }

    public Match mapRow(ResultSet rs, int rowNum) throws SQLException {
        Match match = new Match();

        // get size of solution
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();

        // the order of columns is not determined and I have to combined two
        // values, so save them here and combine later
        String node_name = null;
        List<String> corpus_path = null;

        //get path
        if (outputCorpusPath) {
            for (int column = 1; column <= columnCount; ++column) {
                if (corpusPathExtractor != null && metaData.getColumnName(column).startsWith("path_name")) {
                    corpus_path = corpusPathExtractor.extractCorpusPath(rs, metaData.getColumnName(column));
                }
            }
        }

        // one match per column
        for (int column = 1; column <= columnCount; ++column) {

            if (metaData.getColumnName(column).startsWith("node_name")) {
                node_name = rs.getString(column);
            } else // no more matches in this row if an id was NULL
            if (rs.wasNull()) {
                break;
            }

            if (outputCorpusPath && node_name != null) {
                match.setSaltId(buildSaltId(corpus_path, node_name));
                node_name = null;
            }
        }

        return match;
    }

    public boolean isOptimizeDistinct() {
        return optimizeDistinct;
    }

    public void setOptimizeDistinct(boolean optimizeDistinct) {
        this.optimizeDistinct = optimizeDistinct;
    }

    private String buildSaltId(List<String> path, String node_name) {
        StringBuilder sb = new StringBuilder("salt:/");

        for (String dir : path) {
            try {
                sb.append(URLEncoder.encode(dir, "UTF-8")).append("/");
            } catch (UnsupportedEncodingException ex) {
                log.error(null, ex);
                // fallback, cross fingers there are no invalid characters
                sb.append(dir).append("/");
            }
        }

        return sb.append("#").append(node_name).toString();
    }

    public CorpusPathExtractor getCorpusPathExtractor() {
        return corpusPathExtractor;
    }

    public void setCorpusPathExtractor(CorpusPathExtractor corpusPathExtractor) {
        this.corpusPathExtractor = corpusPathExtractor;
    }

    public boolean isSortSolutions() {
        return sortSolutions;
    }

    public void setSortSolutions(boolean sortSolutions) {
        this.sortSolutions = sortSolutions;
    }

    public boolean isOutputCorpusPath() {
        return outputCorpusPath;
    }

    public void setOutputCorpusPath(boolean outputCorpusPath) {
        this.outputCorpusPath = outputCorpusPath;
    }

}