diff --git a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java index a138c4b..7d7f6f9 100644 --- a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java @@ -10,6 +10,8 @@ import java.sql.SQLException; import java.util.Map; import java.util.Set; +import com.samskivert.util.StringUtil; + import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.annotation.FullTextIndex; import com.samskivert.depot.expression.ColumnExp; @@ -152,7 +154,7 @@ public class MySQLBuilder new ColumnExp(pClass, fields[ii]).accept(this); } _builder.append(") against ("); - bindValue(fullText.getQuery()); + bindValue(massageFTQuery(fullText)); _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) { super(types); diff --git a/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java b/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java index d5dc95d..0ea9b94 100644 --- a/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java @@ -15,7 +15,6 @@ import java.util.Set; import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.LiaisonRegistry; -import com.samskivert.util.ArrayUtil; import com.samskivert.util.StringUtil; import com.samskivert.depot.Exps; @@ -259,18 +258,17 @@ public class PostgreSQLBuilder 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 // (foo&bar)|goop - // so in this first simple implementation, we just take the user query, chop it into - // words by space/punctuation and 'or' those together like so: - // 'ho! who goes there?' -> 'ho|who|goes|there' - String[] searchTerms = match.getQuery().toLowerCase().split("\\W+"); - if (searchTerms.length > 0 && searchTerms[0].length() == 0) { - searchTerms = ArrayUtil.splice(searchTerms, 0, 1); - } - return StringUtil.join(searchTerms, "|"); + // so in this implementation, we take the user query, chop it into words by and combine + // those together depending on the value of isMatchAll(), like so: + // 'ho! who goes there?' -> 'ho|who|goes|there' OR 'ho&who&goes&there' + // + String[] searchTerms = fullText.getQuery().toLowerCase().trim().split("\\W+"); + String operator = fullText.isMatchAll() ? "&" : "|"; + return StringUtil.join(searchTerms, operator); } // Translate the mildly abstracted full-text parser/dictionary configuration support diff --git a/src/main/java/com/samskivert/depot/operator/FullText.java b/src/main/java/com/samskivert/depot/operator/FullText.java index 01547e9..d134b1c 100644 --- a/src/main/java/com/samskivert/depot/operator/FullText.java +++ b/src/main/java/com/samskivert/depot/operator/FullText.java @@ -7,6 +7,7 @@ package com.samskivert.depot.operator; import java.util.Collection; import com.samskivert.depot.PersistentRecord; +import com.samskivert.depot.annotation.FullTextIndex; import com.samskivert.depot.expression.FluentExp; import com.samskivert.depot.impl.FragmentVisitor; @@ -66,11 +67,29 @@ public class FullText } } - public FullText (Class 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 pClass, String name, String query, boolean matchAll) { _pClass = pClass; _name = name; _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 pClass, String name, String query) + { + this(pClass, name, query, false); } public Match match () @@ -98,12 +117,18 @@ public class FullText return _query; } + public boolean isMatchAll () + { + return _matchAll; + } + protected String toString (String subType) { - return "FullText." + subType + "(" + _name + "=" + _query + ")"; + return "FullText." + subType + "(" + _name + "=" + _query + ", " + _matchAll + ")"; } protected Class _pClass; protected String _name; protected String _query; + protected boolean _matchAll; }