简体   繁体   中英

How would I alter the SQL that Linq-to-Nhibernate generates for specific columns?

To take advantage of Full text indexing on MariaDB 10, I need to use this new "MATCH AGAINST" syntax in the sql string.

http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html#function_match

I think it would be really cool if, for certain columns only, I could override linq-to-nhibernate to change the sql it generates when I use

.Where(x => FullTextIndexedStringProperty.Contains("Some word")).ToList().

Who can give me some general directions on how to get started?

This will get you a very simple MATCH ... AGAINST clause. If you want to get more complex (more arguments, specifying the search modifier), you'll have to make some bigger changes. Hopefully this will get you started though:

  1. Create a new dialect and register a simple MATCH (...) AGAINST (...) function:

     public class CustomMySQLDialect : MySQLDialect { public CustomMySQLDialect() { this.RegisterFunction( "matchagainst", new SQLFunctionTemplate( NHibernateUtil.Boolean, "match (?1) against (?2)")); } } 
  2. Create a static extension method on string that you'll use in LINQ statements:

     public static class LinqExtensions { public static bool MatchAgainst(this string source, string against) { throw new NotImplementedException(); } } 
  3. Create a new LINQ to HQL generator class that associates the method with the SQL function we registered in the custom dialect:

     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>()); } } 
  4. Create a custom LinqToHqlGeneratorsRegistry:

     public class MyLinqToHqlRegistry : DefaultLinqToHqlGeneratorsRegistry { public MyLinqToHqlRegistry() { var generator = new MatchAgainstGenerator(); RegisterGenerator(typeof(LinqExtensions).GetMethod("MatchAgainst"), generator); } } 
  5. Use your custom dialect, and Linq to HQL registry either in your cfg.xml file or in code:

     var cfg = new Configuration() .DataBaseIntegration(db => { db.Dialect<CustomMySQLDialect>(); }) .LinqToHqlGeneratorsRegistry<MyLinqToHqlRegistry>(); 
  6. Finally , use your extension method in a LINQ-to-NHibernate query:

     session.Query<Article>() .Where(a => a.Body.MatchAgainst("configured")) .ToList() .Dump(); 

    This will generate SQL that looks like this:

     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'); 

Again, this won't help if you have more complicated requirements. But hopefully this is at least a good starting point.

In case anyone is curious about how to make this support more complex scenarios, here are the problems I think you'd run into:

  • Separating the arguments to MATCH from those to AGAINST .
  • Registering a custom SQL function with NHibernate that can take an arbitrary number of arguments in different places
  • Creating the correct HQL even after solving the two issues above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM