package edu.stanford.nlp.sempre.tables.match;
import java.util.*;
import edu.stanford.nlp.sempre.*;
import edu.stanford.nlp.sempre.FuzzyMatchFn.FuzzyMatchFnMode;
import edu.stanford.nlp.sempre.tables.*;
import fig.basic.*;
/**
* Perform fuzzy matching on the table knowledge graph.
*
* @author ppasupat
*/
public abstract class FuzzyMatcher {
public static class Options {
// This would prevent "canada ?" from fuzzy matching: we already fuzzy match "canada"
@Option(gloss = "Ignore query strings where a boundary word is a punctuation (prevent overgeneration)")
public boolean ignorePunctuationBoundedQueries = true;
@Option(gloss = "Default fuzzy matcher to use")
public String fuzzyMatcher = "tables.match.EditDistanceFuzzyMatcher";
}
public static Options opts = new Options();
/**
* Get a fuzzy matcher of the default class.
*/
public static FuzzyMatcher getFuzzyMatcher(TableKnowledgeGraph graph) {
return getFuzzyMatcher(opts.fuzzyMatcher, graph);
}
public static FuzzyMatcher getFuzzyMatcher(String className, TableKnowledgeGraph graph) {
try {
Class<?> classObject = Class.forName(SempreUtils.resolveClassName(className));
return (FuzzyMatcher) classObject.getConstructor(TableKnowledgeGraph.class).newInstance(graph);
} catch (Exception e) {
e.printStackTrace();
e.getCause().printStackTrace();
throw new RuntimeException(e);
}
}
// ============================================================
// Precomputation
// ============================================================
public final TableKnowledgeGraph graph;
public FuzzyMatcher(TableKnowledgeGraph graph) {
this.graph = graph;
}
// ============================================================
// Fuzzy Matching Main Interface
// ============================================================
/**
* Check if the first or the last token is a punctuation (no alphanumeric character).
*/
public boolean checkPunctuationBoundaries(String term) {
String[] tokens = term.trim().split("\\s+");
if (tokens.length == 0) return false;
if (StringNormalizationUtils.collapseNormalize(tokens[0]).isEmpty()) return false;
if (tokens.length == 1) return true;
if (StringNormalizationUtils.collapseNormalize(tokens[tokens.length - 1]).isEmpty()) return false;
return true;
}
/**
* If needed, compute the fuzzy matched predicates for all substrings of sentence and cache the result.
* Then, return all formulas of the specified mode that match the phrase formed by sentence[startIndex:endIndex].
*/
public Collection<Formula> getFuzzyMatchedFormulas(
List<String> sentence, int startIndex, int endIndex, FuzzyMatchFnMode mode) {
FuzzyMatchCache cache = cacheSentence(sentence, mode);
Collection<Formula> formulas = cache.get(startIndex, endIndex);
return formulas == null ? Collections.emptySet() : formulas;
}
abstract protected FuzzyMatchCache cacheSentence(List<String> sentence, FuzzyMatchFnMode mode);
/**
* Return all formulas of the specified mode that match the phrase.
* Do not use any cached results.
*/
public Collection<Formula> getFuzzyMatchedFormulas(String term, FuzzyMatchFnMode mode) {
if (opts.ignorePunctuationBoundedQueries && !checkPunctuationBoundaries(term))
return Collections.emptySet();
Collection<Formula> formulas = getFuzzyMatchedFormulasInternal(term, mode);
return formulas == null ? Collections.emptySet() : formulas;
}
abstract protected Collection<Formula> getFuzzyMatchedFormulasInternal(String term, FuzzyMatchFnMode mode);
/** Return all formulas of the specified mode. */
public Collection<Formula> getAllFormulas(FuzzyMatchFnMode mode) {
Collection<Formula> formulas = getAllFormulasInternal(mode);
return formulas == null ? Collections.emptySet() : formulas;
}
abstract protected Collection<Formula> getAllFormulasInternal(FuzzyMatchFnMode mode);
// ============================================================
// Helper Functions: Construct Formulas
// ============================================================
/*
* ENTITIY --> fb:cell.___
* UNARY --> (!fb.row.row.___ (fb:type.object.type fb:type.row))
* BINARY --> fb:row.row.___
*/
static Formula getEntityFormula(NameValue nameValue) {
return new ValueFormula<>(nameValue);
}
static Formula getEntityFormula(TableCell cell) {
return new ValueFormula<>(cell.properties.nameValue);
}
static Formula getEntityFormula(TableCellProperties properties) {
return new ValueFormula<>(properties.nameValue);
}
static Formula getUnaryFormula(TableColumn column) {
return new JoinFormula(
new ValueFormula<>(CanonicalNames.reverseProperty(column.relationNameValue)),
new JoinFormula(new ValueFormula<>(new NameValue(CanonicalNames.TYPE)),
new ValueFormula<>(new NameValue(TableTypeSystem.ROW_TYPE))));
}
static Formula getBinaryFormula(TableColumn column) {
return new ValueFormula<>(column.relationNameValue);
}
static Formula getConsecutiveBinaryFormula(TableColumn column) {
return new ValueFormula<>(column.relationConsecutiveNameValue);
}
static List<Formula> getNormalizedBinaryFormulas(TableColumn column) {
List<Formula> formulas = new ArrayList<>();
for (Value normalization : column.getAllNormalization()) {
formulas.add(new LambdaFormula("x", new JoinFormula(
new ValueFormula<>(column.relationNameValue), new JoinFormula(
new ValueFormula<>(normalization), new VariableFormula("x")))));
}
return formulas;
}
}