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