使用HQL / Criteria API中的CONTAINS

时间:2010-09-16 10:01:23

标签: sql-server nhibernate full-text-search hql criteria

我正在使用NHibernate 2.1.2.4000GA。我正在尝试在HQL和标准API中使用SQL Server的CONTAINS函数。这在HQL中工作正常:

CONTAINS(:value)

但是,我需要对相关表格进行限定。这很好用:

CONTAINS(table.Column, :value)

但是,我需要搜索表格中的所有索引列。我试过这个:

CONTAINS(table.*, :value)

但我明白了:

NHibernate.Hql.Ast.ANTLR.QuerySyntaxException : Exception of type 'Antlr.Runtime.MissingTokenException' was thrown. near line ... [select table.Id from Entities.Table table where CONTAINS(table.*,:p0) order by table.Id asc]
    at NHibernate.Hql.Ast.ANTLR.ErrorCounter.ThrowQueryException()
    at NHibernate.Hql.Ast.ANTLR.HqlParseEngine.Parse()
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.Parse(Boolean isFilter)
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.DoCompile(IDictionary`2 replacements, Boolean shallow, String collectionRole)
    at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.Compile(IDictionary`2 replacements, Boolean shallow)
    at NHibernate.Engine.Query.HQLQueryPlan..ctor(String hql, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
    at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(String queryString, Boolean shallow, IDictionary`2 enabledFilters)
    at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(String query, Boolean shallow)
    at NHibernate.Impl.AbstractSessionImpl.CreateQuery(String queryString)

所以看起来HQL解析器会在星号上窒息。我想过这样做:

CONTAINS(table.Column1, :value) or CONTAINS(table.Column2, :value)

这不仅效率低下,而且还会产生错误的结果,具体取决于:value中的全文谓词。

我尝试按照these instructions自定义方言,但这没有用,因为您仍然遇到同样的问题:指定table.*会导致HQL解析器崩溃。

我想过指定查询替换:

<property name="query.substitutions">TABLECONTAINS(=CONTAINS(table.*,</property>

然后干脆做:

TABLECONTAINS(:value)

但这不起作用。我不确定为什么 - 根据结果错误判断,替换不会发生,因为“TABLECONTAINS”仍然存在于查询中。此外,这不适用于所有情况,因为我需要知道表的别名并动态地替换它。

因此,我推出了一种基于拦截的方法:

using System;
using NHibernate;
using NHibernate.SqlCommand;

public class ContainsInterceptor : EmptyInterceptor
{
    public override SqlString OnPrepareStatement(SqlString sql)
    {
        var indexOfTableContains = sql.IndexOfCaseInsensitive("TABLECONTAINS(");

        if (indexOfTableContains != -1)
        {
            var sqlPart = sql.ToString();
            var aliasIndex = sqlPart.LastIndexOf("Table ", indexOfTableContains, StringComparison.Ordinal);

            if (aliasIndex == -1)
            {
                return sql;
            }

            aliasIndex += "Table ".Length;
            var alias = sqlPart.Substring(aliasIndex, sqlPart.IndexOf(" ", aliasIndex, StringComparison.Ordinal) - aliasIndex);
            sql = sql.Replace("TABLECONTAINS(", "CONTAINS(" + alias + ".*,");
        }

        return base.OnPrepareStatement(sql);
    }
}

这有效,我现在可以在今晚睡觉,但我确实感到突然想要参加伦敦即将到来的教皇游行,并大声宣誓。

有人能建议更好的方法来实现这个目标吗?

2 个答案:

答案 0 :(得分:0)

我认为自定义方言是处理这个问题的合适方法。您可以在this article中找到一些指导。我已经使用这种方法注册SQLN特定的函数,如ISNULL,以便在我们的项目中使用。

答案 1 :(得分:0)

为什么不使用ISQLQuery呢?

您仍然可以检索实体,请参阅http://nhibernate.info/doc/nh/en/index.html#querysql-creating