Как бы я изменил SQL, который Linq-to-Nhibernate генерирует для определенных столбцов?
Чтобы воспользоваться преимуществами полнотекстового индексирования на MariaDB 10, мне нужно использовать этот новый синтаксис "MATCH AGAINST" в строке sql.
http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html
Я думаю, было бы очень здорово, если бы только для определенных столбцов я мог переопределить linq-to-nhibernate, чтобы изменить sql, который он генерирует при использовании
.Where(x => FullTextIndexedStringProperty.Contains("Some word")).ToList().
Кто может дать мне несколько общих указаний о том, как начать?
1 ответ
Это поможет вам очень просто MATCH ... AGAINST
пункт. Если вы хотите сделать более сложным (больше аргументов, указав модификатор поиска), вам придется внести некоторые большие изменения. Надеюсь, это поможет вам начать:
Создайте новый диалект и зарегистрируйте простой
MATCH (...) AGAINST (...)
функция:public class CustomMySQLDialect : MySQLDialect { public CustomMySQLDialect() { this.RegisterFunction( "matchagainst", new SQLFunctionTemplate( NHibernateUtil.Boolean, "match (?1) against (?2)")); } }
Создать метод статического расширения на
string
что вы будете использовать в операторах LINQ:public static class LinqExtensions { public static bool MatchAgainst(this string source, string against) { throw new NotImplementedException(); } }
Создайте новый класс генератора LINQ to HQL, который связывает метод с функцией SQL, которую мы зарегистрировали в пользовательском диалекте:
public class MatchAgainstGenerator : BaseHqlGeneratorForMethod { public MatchAgainstGenerator() { this.SupportedMethods = new[] { ReflectionHelper.GetMethod(() => LinqExtensions.MatchAgainst(null, null)) }; } public override HqlTreeNode BuildHql( MethodInfo method, System.Linq.Expressions.Expression targetObject, ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor) { return treeBuilder.BooleanMethodCall( "matchagainst", arguments.Select(visitor.Visit).Cast<HqlExpression>()); } }
Создайте пользовательскую LinqToHqlGeneratorsRegistry:
public class MyLinqToHqlRegistry : DefaultLinqToHqlGeneratorsRegistry { public MyLinqToHqlRegistry() { var generator = new MatchAgainstGenerator(); RegisterGenerator(typeof(LinqExtensions).GetMethod("MatchAgainst"), generator); } }
Используйте свой собственный диалект и реестр Linq to HQL либо в файле cfg.xml, либо в коде:
var cfg = new Configuration() .DataBaseIntegration(db => { db.Dialect<CustomMySQLDialect>(); }) .LinqToHqlGeneratorsRegistry<MyLinqToHqlRegistry>();
Наконец, используйте ваш метод расширения в запросе LINQ-to-NHibernate:
session.Query<Article>() .Where(a => a.Body.MatchAgainst("configured")) .ToList() .Dump();
Это сгенерирует SQL, который выглядит так:
select userquery_0_.Id as Id51_, userquery_0_.Title as Title51_, userquery_0_.Body as Body51_ from articles userquery_0_ where match (userquery_0_.Body) against ('configured');
Опять же, это не поможет, если у вас есть более сложные требования. Но, надеюсь, это как минимум хорошая отправная точка.
Если кому-то интересно, как сделать эту поддержку более сложной, вот проблемы, с которыми, я думаю, вы столкнетесь:
- Разделение аргументов на
MATCH
от тех кAGAINST
, - Регистрация пользовательской функции SQL в NHibernate, которая может принимать произвольное количество аргументов в разных местах
- Создание правильного HQL даже после решения двух вышеуказанных проблем.