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)
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
public @interface FullTextIndex
|
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.
|
* 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.
|
* An array of the field names that should be indexed.
|
||||||
*/
|
*/
|
||||||
public String[] fields ();
|
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;
|
comma = true;
|
||||||
|
|
||||||
|
_builder.append("(");
|
||||||
field.left.accept(this);
|
field.left.accept(this);
|
||||||
|
_builder.append(")");
|
||||||
if (field.right == Order.DESC) {
|
if (field.right == Order.DESC) {
|
||||||
// ascending is default, print nothing unless explicitly descending
|
// ascending is default, print nothing unless explicitly descending
|
||||||
_builder.append(" desc");
|
_builder.append(" desc");
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import com.samskivert.util.StringUtil;
|
|||||||
import com.samskivert.depot.DatabaseException;
|
import com.samskivert.depot.DatabaseException;
|
||||||
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.annotation.FullTextIndex.Configuration;
|
||||||
import com.samskivert.depot.expression.EpochSeconds;
|
import com.samskivert.depot.expression.EpochSeconds;
|
||||||
import com.samskivert.depot.operator.Conditionals.FullText;
|
import com.samskivert.depot.operator.Conditionals.FullText;
|
||||||
|
|
||||||
@@ -48,17 +49,25 @@ import static com.samskivert.Log.log;
|
|||||||
public class PostgreSQLBuilder
|
public class PostgreSQLBuilder
|
||||||
extends SQLBuilder
|
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
|
public class PGBuildVisitor extends BuildVisitor
|
||||||
{
|
{
|
||||||
@Override public void visit (FullText.Match match) {
|
@Override public void visit (FullText.Match match) {
|
||||||
appendIdentifier("ftsCol_" + match.getDefinition().getName());
|
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) {
|
@Override public void visit (FullText.Rank rank) {
|
||||||
_builder.append("rank(");
|
_builder.append(PG83 ? "ts_rank" : "rank").append("(");
|
||||||
appendIdentifier("ftsCol_" + rank.getDefinition().getName());
|
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) {
|
@Override public void visit (EpochSeconds epochSeconds) {
|
||||||
@@ -73,13 +82,19 @@ public class PostgreSQLBuilder
|
|||||||
// _builder.append(" = any (?)");
|
// _builder.append(" = any (?)");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
protected PGBuildVisitor (DepotTypes types) {
|
protected FullTextIndex getFTIndex (FullText definition)
|
||||||
super(types);
|
{
|
||||||
|
DepotMarshaller<?> marsh = _types.getMarshaller(definition.getPersistentClass());
|
||||||
|
return marsh.getFullTextIndex(definition.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void appendIdentifier (String field) {
|
@Override protected void appendIdentifier (String field) {
|
||||||
_builder.append("\"").append(field).append("\"");
|
_builder.append("\"").append(field).append("\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected PGBuildVisitor (DepotTypes types) {
|
||||||
|
super(types);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PGBindVisitor extends BindVisitor
|
public class PGBindVisitor extends BindVisitor
|
||||||
@@ -181,7 +196,9 @@ public class PostgreSQLBuilder
|
|||||||
// build the UPDATE
|
// build the UPDATE
|
||||||
StringBuilder initColumn = new StringBuilder("UPDATE ").
|
StringBuilder initColumn = new StringBuilder("UPDATE ").
|
||||||
append(liaison.tableSQL(table)).append(" SET ").append(liaison.columnSQL(column)).
|
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 ++) {
|
for (int ii = 0; ii < fields.length; ii ++) {
|
||||||
if (ii > 0) {
|
if (ii > 0) {
|
||||||
@@ -193,12 +210,21 @@ public class PostgreSQLBuilder
|
|||||||
}
|
}
|
||||||
initColumn.append(")");
|
initColumn.append(")");
|
||||||
|
|
||||||
|
String triggerFun = PG83 ? "tsvector_update_trigger" : "tsearch2";
|
||||||
|
|
||||||
// build the CREATE TRIGGER
|
// build the CREATE TRIGGER
|
||||||
StringBuilder createTrigger = new StringBuilder("CREATE TRIGGER ").
|
StringBuilder createTrigger = new StringBuilder("CREATE TRIGGER ").
|
||||||
append(liaison.columnSQL(trigger)).append(" BEFORE UPDATE OR INSERT ON ").
|
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(", ");
|
append(liaison.columnSQL(column)).append(", ");
|
||||||
|
|
||||||
|
if (PG83) {
|
||||||
|
createTrigger.append("'").
|
||||||
|
append(translateFTConfig(fts.configuration())).
|
||||||
|
append("', ");
|
||||||
|
}
|
||||||
|
|
||||||
for (int ii = 0; ii < fields.length; ii ++) {
|
for (int ii = 0; ii < fields.length; ii ++) {
|
||||||
if (ii > 0) {
|
if (ii > 0) {
|
||||||
createTrigger.append(", ");
|
createTrigger.append(", ");
|
||||||
@@ -210,7 +236,8 @@ public class PostgreSQLBuilder
|
|||||||
// build the CREATE INDEX
|
// build the CREATE INDEX
|
||||||
StringBuilder createIndex = new StringBuilder("CREATE INDEX ").
|
StringBuilder createIndex = new StringBuilder("CREATE INDEX ").
|
||||||
append(liaison.columnSQL(index)).append(" ON " ).append(liaison.tableSQL(table)).
|
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();
|
Statement stmt = conn.createStatement();
|
||||||
try {
|
try {
|
||||||
@@ -307,4 +334,23 @@ public class PostgreSQLBuilder
|
|||||||
throw new IllegalArgumentException("Unknown field marshaller type: " + fm.getClass());
|
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;
|
protected SQLExpression _elseExp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An attempt at a dialect-agnostic full-text search condition, such as MySQL's MATCH() and
|
* An attempt at a dialect-agnostic full-text search condition, such as MySQL's MATCH() and
|
||||||
* PostgreSQL's @@ TO_TSQUERY(...) abilities.
|
* PostgreSQL's @@ TO_TSQUERY(...) abilities.
|
||||||
@@ -440,16 +438,16 @@ public abstract class Conditionals
|
|||||||
return _pClass;
|
return _pClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getQuery ()
|
|
||||||
{
|
|
||||||
return _query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName ()
|
public String getName ()
|
||||||
{
|
{
|
||||||
return _name;
|
return _name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getQuery ()
|
||||||
|
{
|
||||||
|
return _query;
|
||||||
|
}
|
||||||
|
|
||||||
protected String toString (String subType)
|
protected String toString (String subType)
|
||||||
{
|
{
|
||||||
return "FullText." + subType + "(" + _name + "=" + _query + ")";
|
return "FullText." + subType + "(" + _name + "=" + _query + ")";
|
||||||
|
|||||||
Reference in New Issue
Block a user