package info.ephyra.search;
import info.ephyra.nlp.semantics.Predicate;
import info.ephyra.querygeneration.Query;
import info.ephyra.questionanalysis.Term;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* <p>A <code>Result</code> is a data structure representing a result returned
* by the QA engine.</p>
*
* <p>It comprises the following elements:
* <ul>
* <li>answer string</li>
* <li>score which is a confidence measure for the answer</li>
* <li>query used to obtain the answer (optional)</li>
* <li>ID (e.g. a URL) of a document containing the answer (optional)</li>
* <li>hit position at which the answer was returned by a search engine
* (optional)</li>
* <li>flag indicating whether the answer was judged correct (optional)</li>
* <li>other elements depending on the granularity of the answer string
* (e.g. sentence, factoid)</li>
* </ul></p>
*
* <p>This class implements the interfaces <code>Comparable</code> and
* <code>Serializable</code>. Note: it has a natural ordering that is
* inconsistent with <code>equals()</code>.</p>
*
* @author Nico Schlaefer
* @version 2008-01-29
*/
public class Result implements Comparable<Result>, Serializable {
/** Version number used during deserialization. */
private static final long serialVersionUID = 20070501;
/** The answer string. */
private String answer;
/** A confidence measure for the answer, initially 0. */
private float score = 0;
/** A normalized confidence measure for the answer (optional). */
private float normScore = 0;
/** The <code>Query</code> that was used to obtain the answer (optional). */
private Query query;
/** The ID (e.g. a URL) of a document containing the answer (optional). */
private String docID;
/** The ID of the document in the search engine cache (optional). */
private String cacheID;
/** The hit position of the answer, starting from 0 (optional). */
private int hitPos = -1;
/** A flag indicating whether the answer was judged correct (optional). */
private boolean correct;
/** Hashmap holding intermediate scores so they don't influence sorting*/
private HashMap<String, Float> extraScores = new HashMap<String, Float>();
/**
* If this is a sentence-level answer, named entities extracted from the
* sentence and their types (optional).
*/
private Map<String, String[]> nes;
/**
* If this is a sentence-level answer, terms extracted from the sentence
* (optional).
*/
private Term[] terms;
/**
* If this is a sentence-level answer, a predicate extracted from the
* sentence (optional).
*/
private Predicate predicate;
/**
* If this is a factoid answer, a sentence in the supporting document the
* answer was extracted from (optional).
*/
private String sentence;
/** If this is a factoid answer, the named entity types (optional). */
private String[] neTypes;
/**
* If this is a factoid answer, the techniques used to extract it
* (optional).
*/
private String[] extractionTechniques;
/**
* If this is an answer to an 'other' question, list to keep the IDs of
* covered nugget (optional).
*/
private ArrayList<String> coveredNuggets = new ArrayList<String>();
/**
* Creates a <code>Result</code> object and sets the answer string.
*
* @param answer answer string
*/
public Result(String answer) {
this.answer = answer;
}
/**
* Creates a <code>Result</code> object and sets the answer string and the
* <code>Query</code> that was used to obtain the answer.
*
* @param answer answer string
* @param query <code>Query</code> object
*/
public Result(String answer, Query query) {
this(answer);
this.query = query;
}
/**
* Creates a <code>Result</code> object and sets the answer string, the
* <code>Query</code> that was used to obtain the answer and the ID of a
* document that contains it.
*
* @param answer answer string
* @param query <code>Query</code> object
* @param docID document ID
*/
public Result(String answer, Query query, String docID) {
this(answer,query);
this.docID = docID;
}
/**
* Creates a <code>Result</code> object and sets the answer string, the
* <code>Query</code> that was used to obtain the answer, the ID of a
* document that contains it and the hit position.
*
* @param answer answer string
* @param query <code>Query</code> object
* @param docID document ID
* @param hitPos hit position, starting from 0
*/
public Result(String answer, Query query, String docID, int hitPos) {
this(answer, query, docID);
this.hitPos = hitPos;
}
/**
* Compares two results by comparing their scores.
*
* @param result the result to be compared
* @return a negative integer, zero or a positive integer as this result is
* less than, equal to or greater than the specified result
*/
public int compareTo(Result result) {
float diff = score - result.getScore();
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else
return 0;
// return answer.compareTo(result.getAnswer()); // tie-breaking
}
/**
* Indicates whether an other result is equal to this one.
* Two results are considered equal if the answer strings are equal.
*
* @param o the object to be compared
* @return <code>true</code> iff the objects are equal
*/
public boolean equals(Object o) {
// if objects incomparable, return false
if (!(o instanceof Result)) return false;
Result result = (Result) o;
return answer.equals(result.answer);
}
/**
* Returns the hash code of the answer string as a hash code for the result.
*
* @return hash code
*/
public int hashCode() {
return answer.hashCode();
}
/**
* Returns the answer string.
*
* @return answer string
*/
public String getAnswer() {
return answer;
}
/**
* Returns the confidence score of the result.
*
* @return confidence score
*/
public float getScore() {
return score;
}
/**
* Returns the normalized score of the result.
*
* @return normalized score
*/
public float getNormScore() {
return normScore;
}
/**
* Returns the <code>Query</code> that was used to obtain this result, or
* <code>null</code> if it is not set.
*
* @return <code>Query</code> used to obtain this result or
* <code>null</code>
*/
public Query getQuery() {
return query;
}
/**
* Returns the ID of a document that contains the answer or
* <code>null</code> if it is not set.
*
* @return document ID or <code>null</code>
*/
public String getDocID() {
return docID;
}
/**
* Returns the ID of the document in the search engine cache or
* <code>null</code> if it is not set.
*
* @return ID of the cached document or <code>null</code>
*/
public String getCacheID() {
return cacheID;
}
/**
* Returns the hit position of the result, starting from 0, or -1 if it is
* not set.
*
* @return hit position or -1
*/
public int getHitPos() {
return hitPos;
}
/**
* Checks if the answer was judged correct.
*
* @return <code>true</code> iff the answer was judged as correct
*/
public boolean isCorrect() {
return correct;
}
/**
* Returns named entities extracted from a sentence-level answer and their
* types.
*
* @return NEs or <code>null</code> if no NEs have been extracted
*/
public Map<String, String[]> getNes() {
return nes;
}
/**
* Returns terms extracted from a sentence-level answer.
*
* @return terms or <code>null</code> if no terms have been extracted
*/
public Term[] getTerms() {
return terms;
}
/**
* Returns a predicate extracted from a sentence-level answer.
*
* @return predicate or <code>null</code> if no predicate has been extracted
*/
public Predicate getPredicate() {
return predicate;
}
/**
* Returns the supporting sentence of a factoid answer.
*
* @return supporting sentence or <code>null</code> if it is not set
*/
public String getSentence() {
return sentence;
}
/**
* Checks if a factoid answer is a named entity.
*
* @return <code>true</code> iff the answer is a NE
*/
public boolean isNamedEntity() {
return neTypes != null;
}
/**
* Gets the named entity types if the answer is a named entity.
*
* @return NE types or <code>null</code> if the answer is not a NE
*/
public String[] getNeTypes() {
return neTypes;
}
/**
* Returns the techniques a factoid answer was extracted with.
*
* @return answer extraction techniques
*/
public String[] getExtractionTechniques() {
return extractionTechniques;
}
/**
* Checks if a factoid answer was extracted with the given technique.
*
* @param technique answer extraction technique
* @return <code>true</code> iff the answer was extracted with the technique
*/
public boolean extractedWith(String technique) {
if (extractionTechniques == null) return false;
for (String extractionTechnique : extractionTechniques)
if (technique.equals(extractionTechnique)) return true;
return false;
}
/**
* Returns the IDs of all nuggets covered by an answer to an 'other'
* question.
*
* @return IDs of all covered nuggets
*/
public String[] getCoveredNuggetIDs() {
return coveredNuggets.toArray(new String[coveredNuggets.size()]);
}
/**
* Sets the answer string.
*
* @param answer the answer string
*/
public void setAnswer(String answer) {
this.answer = answer;
}
/**
* Sets the confidence score of this result.
*
* @param score confidence score
*/
public void setScore(float score) {
this.score = score;
}
/**
* Increments the confidence score by the given value.
*
* @param value the value to be added to the score
*/
public void incScore(float value) {
score += value;
}
/**
* Sets the normalized score of this result.
*
* @param normScore normalized score
*/
public void setNormScore(float normScore) {
this.normScore = normScore;
}
/**
* Sets the ID of a document that contains the answer.
*
* @param docID document ID
*/
public void setDocID(String docID) {
this.docID = docID;
}
/**
* Sets the ID of the document in the search engine cache.
*
* @param cacheID ID of the cached document
*/
public void setCacheID(String cacheID) {
this.cacheID = cacheID;
}
/**
* Judges the answer as correct.
*/
public void setCorrect() {
correct = true;
}
/**
* Sets named entities extracted from a sentence-level answer and their
* types.
*
* @param nes NEs and their types
*/
public void setNes(Map<String, String[]> nes) {
this.nes = nes;
}
/**
* Sets terms extracted from a sentence-level answer.
*
* @param terms terms extracted from a sentence
*/
public void setTerms(Term[] terms) {
this.terms = terms;
}
/**
* Sets a predicate extracted from a sentence-level answer.
*
* @param predicate predicate extracted from a sentence
*/
public void setPredicate(Predicate predicate) {
this.predicate = predicate;
}
/**
* Sets the supporting sentence of a factoid answer.
*
* @param sentence supporting sentence
*/
public void setSentence(String sentence) {
this.sentence = sentence.trim();
}
/**
* Adds a named entity type for a factoid answer.
*
* @param neType NE type
* @return <code>true</code> if a new type was added
*/
public boolean addNeType(String neType) {
// check if NE type has already been added
if (neTypes != null)
for (String existing : neTypes)
if (neType.equals(existing)) return false;
// add new NE type
String[] newNeTypes;
if (neTypes == null) {
newNeTypes = new String[1];
} else {
newNeTypes = new String[neTypes.length + 1];
for (int i = 0; i < neTypes.length; i++)
newNeTypes[i] = neTypes[i];
}
newNeTypes[newNeTypes.length - 1] = neType;
neTypes = newNeTypes;
return true;
}
/**
* Sets the techniques a factoid answer was extracted with.
*
* @param techniques answer extraction techniques
*/
public void setExtractionTechniques(String[] techniques) {
extractionTechniques = techniques;
}
/**
* Adds an answer extraction technique for a factoid answer.
*
* @param technique answer extraction technique
* @return <code>true</code> iff a new technique was added
*/
public boolean addExtractionTechnique(String technique) {
// check if the extraction technique has already been added
if (extractionTechniques != null)
for (String existing : extractionTechniques)
if (technique.equals(existing)) return false;
// add new extraction technique
String[] newTechniques;
if (extractionTechniques == null) {
newTechniques = new String[1];
} else {
newTechniques = new String[extractionTechniques.length + 1];
for (int i = 0; i < extractionTechniques.length; i++)
newTechniques[i] = extractionTechniques[i];
}
newTechniques[newTechniques.length - 1] = technique;
extractionTechniques = newTechniques;
return true;
}
/**
* Adds the ID of a nugget covered by an answer to an 'other' question.
*
* @param nuggetID ID of the nugget to add
*/
public void addCoveredNuggetID(String nuggetID) {
this.coveredNuggets.add(nuggetID);
}
/**
* Returns a copy of this <code>Result</code> object.
*
* @return copy of this object
*/
public Result getCopy() {
Result result = new Result(answer, query, docID, hitPos);
result.score = score;
result.normScore = normScore;
result.cacheID = cacheID;
result.correct = correct;
result.nes = nes;
result.terms = terms;
result.predicate = predicate;
result.sentence = sentence;
result.neTypes = neTypes;
result.extractionTechniques = extractionTechniques;
result.coveredNuggets = coveredNuggets;
result.extraScores.putAll(this.extraScores);
return result;
}
/** add an extra score to this Result for storage, extra score will not influence sorting
* @param sourceName the name of the source of the score
* @param score the value of the score
*/
public void addExtraScore(String sourceName, float score) {
this.extraScores.put(sourceName, new Float(score));
}
/** retrieve the extra score set by some source
* @param sourceName the name of the source who set the required score
* @return the extra score set by the source with the specified name, or 0, if the is no suchh extra score
*/
public float getExtraScore(String sourceName) {
if (this.extraScores.containsKey(sourceName)) return this.extraScores.get(sourceName).floatValue();
else return 0f;
}
/** retrieve all extra scores set for this Result
* @return an array holding all the extra scores
*/
public float[] getExtraScores() {
float[] scores = new float[this.extraScores.size()];
int i = 0;
for (Iterator<Float> iter = this.extraScores.values().iterator(); iter.hasNext(); i++)
scores[i] = iter.next().floatValue();
return scores;
}
}