/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
* $Id: TextQueryResolver.java 577512 2007-09-20 02:27:54Z natalia $
*/
package org.apache.xindice.core.query;
import java.util.HashSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.data.Key;
import org.apache.xindice.core.data.NodeSet;
import org.apache.xindice.core.data.Entry;
import org.apache.xindice.core.DBException;
import org.apache.xindice.core.FaultCodes;
import org.apache.xindice.core.indexer.LuceneIndexer;
import org.apache.xindice.core.indexer.Indexer;
import org.apache.xindice.core.indexer.IndexMatch;
import org.apache.xindice.util.SimpleConfigurable;
import org.apache.xindice.util.XindiceRuntimeException;
import org.apache.xindice.xml.dom.DBDocument;
import org.apache.xindice.xml.NamespaceMap;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.analysis.Analyzer;
import org.w3c.dom.Node;
/**
* Query resolver for full text queries. Requires existing full text index
* to work.
*
* @see org.apache.xindice.core.indexer.LuceneIndexer
* @author Andy Armstrong
* @version $Revision: 577512 $, $Date: 2007-09-19 22:27:54 -0400 (Wed, 19 Sep 2007) $
*/
public class TextQueryResolver extends SimpleConfigurable implements QueryResolver {
public final static String STYLE_FT = "Text";
private static final Log log = LogFactory.getLog(TextQueryResolver.class);
private class TextQuery implements Query {
private Collection context;
private String query;
private Key keys[];
private LuceneIndexer idx;
private org.apache.lucene.search.Query compiledQuery;
private TextQuery(Collection context, String query, Key[] keys) throws QueryException {
this.context = context;
this.keys = keys;
this.query = query;
try {
idx = findIndex(context);
if (null == idx) {
throw new QueryException(FaultCodes.QRY_STYLE_NOT_FOUND, "Could not find text indexer in this collection");
}
Analyzer an = idx.getAnalyzer();
compiledQuery = new QueryParser("", an).parse(query);
} catch (QueryException e) {
throw e;
} catch (DBException e) {
throw new QueryException(FaultCodes.QRY_COMPILATION_ERROR, "Failed to compile the query due to database error", e);
} catch (ParseException e) {
throw new QueryException(FaultCodes.QRY_COMPILATION_ERROR, "Failed to compile the query", e);
}
}
public String getQueryStyle() {
return STYLE_FT;
}
public Collection getQueryContext() {
return context;
}
public String getQueryString() {
return query;
}
public NamespaceMap getNamespaceMap() {
return null;
}
public Key[] getKeySet() {
return keys;
}
/**
* Executes compiled Lucene query against existing index.
*
* @return NodeSet that contains document element of all matching
* documents
* @throws QueryException
*/
public NodeSet execute() throws QueryException {
try {
IndexMatch[] match = idx.queryMatches(compiledQuery);
Key[] uniqueKeys = QueryEngine.getUniqueKeys(match);
// convert keys filter to HashMap
HashSet filter = null;
if (keys != null) {
filter = new HashSet(keys.length);
for (int k = 0; k < keys.length; k++) {
filter.add(keys[k]);
}
}
Key rk[] = new Key[uniqueKeys.length];
int rkused = 0;
for (int i = 0; i < uniqueKeys.length; i++) {
if (filter == null || filter.contains(uniqueKeys[i])) {
rk[rkused++] = uniqueKeys[i];
}
}
return new ResultSet(rk, rkused);
} catch (DBException e) {
throw new ProcessingException("Error executing full text query: " + e.getMessage(), e);
}
}
/**
* ResultSet
*/
private class ResultSet implements NodeSet {
private Key[] keySet;
private int keyPos = 0;
private int keyLen;
private Node nextNode;
public ResultSet(Key[] keySet, int keyLen) {
this.keySet = keySet;
this.keyLen = keyLen;
try {
prepareNextNode();
} catch (Exception e) {
throw new XindiceRuntimeException(e);
}
}
private void prepareNextNode() throws DBException {
nextNode = null;
while (nextNode == null && keyPos < keyLen) {
Entry entry = context.getEntry(keySet[keyPos++]);
if (entry == null || entry.getEntryType() != Entry.DOCUMENT) {
continue;
}
DBDocument d = (DBDocument) entry.getValue();
if (d != null) {
nextNode = d.getDocumentElement();
}
}
}
public boolean hasMoreNodes() {
return nextNode != null;
}
public Object getNextNode() {
Node n = nextNode;
try {
prepareNextNode();
} catch (Exception e) {
throw new XindiceRuntimeException(e);
}
return n;
}
}
}
private LuceneIndexer findIndex(Collection c) throws DBException {
return (LuceneIndexer) c.getIndexManager().getBestIndexer(Indexer.STYLE_FULLTEXT, null);
}
public void setQueryEngine(QueryEngine engine) {
// do nothing
// FIXME: not used
}
public String getQueryStyle() {
return STYLE_FT;
}
public Query compileQuery(Collection context, String query, NamespaceMap nsMap, Key[] keys) throws QueryException {
if (log.isTraceEnabled()) {
log.trace("Compiling query for collection " + context.getCanonicalName() + ", query = " + query);
}
return new TextQuery(context, query, keys);
}
public NodeSet query(Collection context, String query, NamespaceMap nsMap, Key[] keys) throws QueryException {
if (log.isTraceEnabled()) {
log.trace("Querying collection " + context.getCanonicalName() + ", query = " + query);
}
try {
Query tq = new TextQuery(context, query, keys);
return tq.execute();
} catch (Exception e) {
if (e instanceof QueryException) {
throw (QueryException) e;
} else {
throw new ProcessingException("Failed to execute text query", e);
}
}
}
}