Adapt Depot's full-text search capabilities for PostgreSQL 8.3. This is a fairly minimal change, excepting support for associating FullTextIndex configurations with PostgreSQL search configurations. The legacy stuff to support 8.2 is unlikely to last long.

This commit is contained in:
Par Winzell
2009-03-26 20:14:45 +00:00
parent 51e43b6545
commit 34599bc760
4 changed files with 73 additions and 15 deletions
@@ -32,6 +32,13 @@ import java.lang.annotation.Target;
@Retention(value=RetentionPolicy.RUNTIME)
public @interface FullTextIndex
{
public enum Configuration {
/** For searching generic strings; makes no linguistic/semantic assumptions. */
Simple,
/** For English text search; does basic stemming. */
English
}
/**
* An identifier for this index, unique with the scope of the record.
*/
@@ -41,4 +48,9 @@ public @interface FullTextIndex
* An array of the field names that should be indexed.
*/
public String[] fields ();
/**
* What parser/dictionary to use for this index.
*/
public Configuration configuration () default Configuration.English;
}
@@ -525,7 +525,9 @@ public abstract class BuildVisitor implements ExpressionVisitor
}
comma = true;
_builder.append("(");
field.left.accept(this);
_builder.append(")");
if (field.right == Order.DESC) {
// ascending is default, print nothing unless explicitly descending
_builder.append(" desc");
@@ -40,6 +40,7 @@ import com.samskivert.util.StringUtil;
import com.samskivert.depot.DatabaseException;
import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.annotation.FullTextIndex;
import com.samskivert.depot.annotation.FullTextIndex.Configuration;
import com.samskivert.depot.expression.EpochSeconds;
import com.samskivert.depot.operator.Conditionals.FullText;
@@ -48,17 +49,25 @@ import static com.samskivert.Log.log;
public class PostgreSQLBuilder
extends SQLBuilder
{
// Are we running with PostgreSQL 8.3's new FTS engine, or the old one?
// TODO: Rip out when no longer needed.
public final static boolean PG83 = Boolean.getBoolean("com.samskivert.depot.pg83");
public class PGBuildVisitor extends BuildVisitor
{
@Override public void visit (FullText.Match match) {
appendIdentifier("ftsCol_" + match.getDefinition().getName());
_builder.append(" @@ to_tsquery('default', ?)");
_builder.append(" @@ to_tsquery('").
append(translateFTConfig(getFTIndex(match.getDefinition()).configuration())).
append("', ?)");
}
@Override public void visit (FullText.Rank rank) {
_builder.append("rank(");
_builder.append(PG83 ? "ts_rank" : "rank").append("(");
appendIdentifier("ftsCol_" + rank.getDefinition().getName());
_builder.append(", to_tsquery('default', ?), 32)");
_builder.append(", to_tsquery('").
append(translateFTConfig(getFTIndex(rank.getDefinition()).configuration())).
append("', ?), 32)");
}
@Override public void visit (EpochSeconds epochSeconds) {
@@ -73,13 +82,19 @@ public class PostgreSQLBuilder
// _builder.append(" = any (?)");
// }
protected PGBuildVisitor (DepotTypes types) {
super(types);
protected FullTextIndex getFTIndex (FullText definition)
{
DepotMarshaller<?> marsh = _types.getMarshaller(definition.getPersistentClass());
return marsh.getFullTextIndex(definition.getName());
}
@Override protected void appendIdentifier (String field) {
_builder.append("\"").append(field).append("\"");
}
protected PGBuildVisitor (DepotTypes types) {
super(types);
}
}
public class PGBindVisitor extends BindVisitor
@@ -181,7 +196,9 @@ public class PostgreSQLBuilder
// build the UPDATE
StringBuilder initColumn = new StringBuilder("UPDATE ").
append(liaison.tableSQL(table)).append(" SET ").append(liaison.columnSQL(column)).
append(" = TO_TSVECTOR('default', ");
append(" = TO_TSVECTOR('").
append(translateFTConfig(fts.configuration())).
append("', ");
for (int ii = 0; ii < fields.length; ii ++) {
if (ii > 0) {
@@ -193,11 +210,20 @@ public class PostgreSQLBuilder
}
initColumn.append(")");
String triggerFun = PG83 ? "tsvector_update_trigger" : "tsearch2";
// build the CREATE TRIGGER
StringBuilder createTrigger = new StringBuilder("CREATE TRIGGER ").
append(liaison.columnSQL(trigger)).append(" BEFORE UPDATE OR INSERT ON ").
append(liaison.tableSQL(table)).append(" FOR EACH ROW EXECUTE PROCEDURE tsearch2(").
append(liaison.tableSQL(table)).
append(" FOR EACH ROW EXECUTE PROCEDURE ").append(triggerFun).append("(").
append(liaison.columnSQL(column)).append(", ");
if (PG83) {
createTrigger.append("'").
append(translateFTConfig(fts.configuration())).
append("', ");
}
for (int ii = 0; ii < fields.length; ii ++) {
if (ii > 0) {
@@ -210,7 +236,8 @@ public class PostgreSQLBuilder
// build the CREATE INDEX
StringBuilder createIndex = new StringBuilder("CREATE INDEX ").
append(liaison.columnSQL(index)).append(" ON " ).append(liaison.tableSQL(table)).
append(" USING GIST(").append(liaison.columnSQL(column)).append(")");
append(" USING ").append(PG83 ? "GIN" : "GIST").append("(").
append(liaison.columnSQL(column)).append(")");
Statement stmt = conn.createStatement();
try {
@@ -307,4 +334,23 @@ public class PostgreSQLBuilder
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
}
}
// Translate the mildly abstracted full-text parser/dictionary configuration support
// in FullText to actual PostgreSQL configuration identifiers.
protected static String translateFTConfig (Configuration configuration)
{
// legacy support
if (!PG83) {
return "default";
}
switch(configuration) {
case Simple:
return "pg_catalog.simple";
case English:
return "pg_catalog.english";
default:
throw new IllegalArgumentException(
"Unknown full text configuration: " + configuration);
}
}
}
@@ -358,8 +358,6 @@ public abstract class Conditionals
protected SQLExpression _elseExp;
}
/**
* An attempt at a dialect-agnostic full-text search condition, such as MySQL's MATCH() and
* PostgreSQL's @@ TO_TSQUERY(...) abilities.
@@ -440,15 +438,15 @@ public abstract class Conditionals
return _pClass;
}
public String getQuery ()
{
return _query;
}
public String getName ()
{
return _name;
}
public String getQuery ()
{
return _query;
}
protected String toString (String subType)
{