package info.ephyra.search;
import info.ephyra.answerselection.filters.HitPositionSorterFilter;
import info.ephyra.querygeneration.Query;
import info.ephyra.search.searchers.KnowledgeAnnotator;
import info.ephyra.search.searchers.KnowledgeMiner;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* <p>The <code>Search</code> component queries several unstructured and
* (semi)structured knowledge sources in parallel and aggregate the results.</p>
*
* <p>Queries are instances of the class
* <code>info.ephyra.querygeneration.Query</code>, results are instances of the
* <code>Result</code> class in this package.</p>
*
* @author Nico Schlaefer
* @version 2007-05-29
*/
public class Search {
/** The maximum number of parallel queries. */
private static final int MAX_PENDING = 30;
/**
* <code>KnowledgeAnnotators</code> used to query (semi)structured knowledge
* sources.
*/
private static ArrayList<KnowledgeAnnotator> kas =
new ArrayList<KnowledgeAnnotator>();
/**
* <code>KnowledgeMiners</code> used to query unstructured knowledge
* sources.
*/
private static ArrayList<KnowledgeMiner> kms =
new ArrayList<KnowledgeMiner>();
/** Results from different searches are aggregated in this field. */
private static ArrayList<Result> results;
/** Number pending of queries. */
private static int pending;
/**
* Searches the (semi)structured knowledge sources.
*
* @param query query to be processed
*/
private static void queryKAs(Query query) {
for (int i = 0; i < kas.size(); i++)
kas.get(i).start(query);
}
/**
* Searches the unstructured knowledge sources.
*
* @param query query to be processed
*/
private static void queryKMs(Query query) {
for (int i = 0; i < kms.size(); i++)
kms.get(i).start(query);
}
/**
* Delays the main thread until all queries have been completed.
*/
private static void waitForResults() {
synchronized (results) {
while (pending > 0)
try {
results.wait();
} catch (InterruptedException e) {}
}
}
/**
* Drops duplicates among results from <code>KnowledgeMiners</code>.
*
* @param results results with duplicates
* @return results without duplicates
*/
private static ArrayList<Result> dropDuplicates(ArrayList<Result> results) {
// sort results by their hit positions in ascending order
Result[] sorted = results.toArray(new Result[results.size()]);
sorted = (new HitPositionSorterFilter()).apply(sorted);
Set<Result> noDups = new HashSet<Result>();
ArrayList<Result> remaining = new ArrayList<Result>();
for (Result result : sorted)
if (result.getScore() != Float.NEGATIVE_INFINITY ||
noDups.add(result))
remaining.add(result);
return remaining;
}
/**
* Registers a <code>KnowledgeAnnotator</code> for a (semi)structured
* knowledge source.
*
* @param ka <code>KnowledgeAnnotator</code> to add
*/
public static void addKnowledgeAnnotator(KnowledgeAnnotator ka) {
kas.add(ka);
}
/**
* Registers a <code>KnowledgeMiner</code> for an unstructured knowledge
* source.
*
* @param km <code>KnowledgeMiner</code> to add
*/
public static void addKnowledgeMiner(KnowledgeMiner km) {
kms.add(km);
}
/**
* Unregisters all <code>KnowledgeAnnotators</code>.
*/
public static void clearKnowledgeAnnotators() {
kas.clear();
}
/**
* Unregisters all <code>KnowledgeMiners</code>.
*/
public static void clearKnowledgeMiners() {
kms.clear();
}
/**
* Sends several alternative queries to all the searchers that have been
* registered and returns the aggregated results.
*
* @param queries queries to be processed
* @return results returned by the searchers
*/
public static Result[] doSearch(Query[] queries) {
results = new ArrayList<Result>();
pending = 0;
System.out.println("queries.length == " + queries.length);
// send only the first query to the KnowledgeAnnotators
if (queries.length > 0) queryKAs(queries[0]);
// send all queries to the KnowledgeMiners
for (Query query : queries) queryKMs(query);
// wait until all queries have been completed
waitForResults();
// drop duplicates among results from KnowledgeMiners
results = dropDuplicates(results);
return results.toArray(new Result[results.size()]);
}
/**
* Delays a thread until there are less than MAX_PENDING pending queries.
*/
public static void waitForPending() {
synchronized (results) {
while (pending >= MAX_PENDING)
try {
results.wait();
} catch (InterruptedException e) {}
}
}
/**
* Increments the number of pending queries by 1.
*/
public static void incPending() {
synchronized (results) {
pending++;
}
}
/**
* Used by <code>Searchers</code> to return the results found in the
* knowledge sources.
*
* @param results results found in the knowledge sources
*/
public static void addResults(Result[] results) {
synchronized (Search.results) {
// Error handling for case when searcher finds no results
if (results == null) {
System.out.println("Search returned no results " +
"for this query");
} else {
for (Result result : results) {
Search.results.add(result);
}
}
pending--;
Search.results.notify(); // signal that the query is completed
}
}
}