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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user