From Bruno Garcia: Add a mode for FullText to search for rows that match all the words in a query rather than any of them.

This commit is contained in:
Par Winzell
2011-08-24 15:04:43 +00:00
parent 3c1e6f7538
commit 710d2ffaf2
3 changed files with 51 additions and 13 deletions
@@ -10,6 +10,8 @@ import java.sql.SQLException;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex; import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.ColumnExp;
@@ -152,7 +154,7 @@ public class MySQLBuilder
new ColumnExp<Object>(pClass, fields[ii]).accept(this); new ColumnExp<Object>(pClass, fields[ii]).accept(this);
} }
_builder.append(") against ("); _builder.append(") against (");
bindValue(fullText.getQuery()); bindValue(massageFTQuery(fullText));
_builder.append(" in boolean mode)"); _builder.append(" in boolean mode)");
} }
@@ -162,6 +164,19 @@ public class MySQLBuilder
} }
} }
protected static String massageFTQuery (FullText fullText)
{
// Split the query into words and remove punctuation
String[] searchTerms = fullText.getQuery().toLowerCase().trim().split("\\W+");
if (fullText.isMatchAll()) {
// Prepend every search term with a plus for ANDing
return '+' + StringUtil.join(searchTerms, " +");
} else {
// Use the default OR matching
return StringUtil.join(searchTerms, " ");
}
}
public MySQLBuilder (DepotTypes types) public MySQLBuilder (DepotTypes types)
{ {
super(types); super(types);
@@ -15,7 +15,6 @@ import java.util.Set;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
import com.samskivert.jdbc.LiaisonRegistry; import com.samskivert.jdbc.LiaisonRegistry;
import com.samskivert.util.ArrayUtil;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.samskivert.depot.Exps; import com.samskivert.depot.Exps;
@@ -259,18 +258,17 @@ public class PostgreSQLBuilder
return field.getType().equals(Long.TYPE) ? "BIGSERIAL" : "SERIAL"; return field.getType().equals(Long.TYPE) ? "BIGSERIAL" : "SERIAL";
} }
protected static String massageFTQuery (FullText match) protected static String massageFTQuery (FullText fullText)
{ {
// The tsearch2 engine takes queries on the form // The tsearch2 engine takes queries on the form
// (foo&bar)|goop // (foo&bar)|goop
// so in this first simple implementation, we just take the user query, chop it into // so in this implementation, we take the user query, chop it into words by and combine
// words by space/punctuation and 'or' those together like so: // those together depending on the value of isMatchAll(), like so:
// 'ho! who goes there?' -> 'ho|who|goes|there' // 'ho! who goes there?' -> 'ho|who|goes|there' OR 'ho&who&goes&there'
String[] searchTerms = match.getQuery().toLowerCase().split("\\W+"); //
if (searchTerms.length > 0 && searchTerms[0].length() == 0) { String[] searchTerms = fullText.getQuery().toLowerCase().trim().split("\\W+");
searchTerms = ArrayUtil.splice(searchTerms, 0, 1); String operator = fullText.isMatchAll() ? "&" : "|";
} return StringUtil.join(searchTerms, operator);
return StringUtil.join(searchTerms, "|");
} }
// Translate the mildly abstracted full-text parser/dictionary configuration support // Translate the mildly abstracted full-text parser/dictionary configuration support
@@ -7,6 +7,7 @@ package com.samskivert.depot.operator;
import java.util.Collection; import java.util.Collection;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.expression.FluentExp; import com.samskivert.depot.expression.FluentExp;
import com.samskivert.depot.impl.FragmentVisitor; import com.samskivert.depot.impl.FragmentVisitor;
@@ -66,11 +67,29 @@ public class FullText
} }
} }
public FullText (Class<? extends PersistentRecord> pClass, String name, String query) /**
* @param pClass The persistent record the full-text query is defined on.
* @param name The name of the corresponding {@link FullTextIndex}.
* @param query What to actually search for; this can be unprocessed natural language
* @param matchAll If true, all words in the query must be present in matches.
*/
public FullText (
Class<? extends PersistentRecord> pClass, String name, String query, boolean matchAll)
{ {
_pClass = pClass; _pClass = pClass;
_name = name; _name = name;
_query = query; _query = query;
_matchAll = matchAll;
}
/**
* @param pClass The persistent record the full-text query is defined on.
* @param name The name of the corresponding {@link FullTextIndex}.
* @param query What to actually search for; this can be unprocessed natural language
*/
public FullText (Class<? extends PersistentRecord> pClass, String name, String query)
{
this(pClass, name, query, false);
} }
public Match match () public Match match ()
@@ -98,12 +117,18 @@ public class FullText
return _query; return _query;
} }
public boolean isMatchAll ()
{
return _matchAll;
}
protected String toString (String subType) protected String toString (String subType)
{ {
return "FullText." + subType + "(" + _name + "=" + _query + ")"; return "FullText." + subType + "(" + _name + "=" + _query + ", " + _matchAll + ")";
} }
protected Class<? extends PersistentRecord> _pClass; protected Class<? extends PersistentRecord> _pClass;
protected String _name; protected String _name;
protected String _query; protected String _query;
protected boolean _matchAll;
} }