设为首页 收藏本站
查看: 618|回复: 0

[经验分享] 让Linq To NHibernate支持Sql Server自带的全文检索

[复制链接]

尚未签到

发表于 2015-6-30 12:45:04 | 显示全部楼层 |阅读模式
  最近在使用NHibernate重构一个项目,好几处需要用到全文检索混搭其他一些条件进行的列表搜索。而大家都知道NHibernate本身是不支持全文检索的,网络上能找到的几处文章说来大概有以下几种方式
  1. 修改hibernate.cfg.xml来扩展contains, freetext谓词到HQL, 参见:http://nhforge.org/blogs/nhibernate/archive/2009/03/13/registering-freetext-or-contains-functions-into-a-nhibernate-dialect.aspx
  这种方法使得查询只能通过HQL进行,对于熟悉HQL的同学是个不错的选择,对于列表查询,获取列表总数的select count(*)和select *是两条HQL,需要写两次HQL的where条件或拼接HQL,因此个人不是很喜欢。
  
  2. 为ICriteria查询添加SQL表达式参见:http://archive.iyunv.com/a/1289840/
  这个方法很棒,也可以通过 criteria.setProjection(Projections.rowCount()).uniqueResult() 来直接获得记录总数,对CreateCriteria不熟悉的同学们要注意文中的Expression.Sql里的Expression不是System.Linq.Expressions.Expression, 而是 NHibernate.Criterion.Expression
  
  3. 基于Lucene的第三方扩展 NHibernate.Search : http://www.iyunv.com/lonely7345/archive/2009/03/17/1413836.html
这个对项目的改动太大,而且手上的项目Sql Server自身的搜索就够用了,还没有复杂到要引入Lucene,因此也没有去具体了解。
  
  4. 由于大量使用了Linq,因此自然希望全文搜索功能能够和Linq表达式无缝连接,因此这里介绍一个我自己折腾的Linq方法
先看下最终的调用方式:



var customers = Hibernate.Session.Query()
.Where
(
u => u.Company.Name.ContainsSearch("公司", u.Company.Phone)
);
  这里的含义是在Company.Name和Company.Phone中查找指定的关键词,ContainsSearch方法最后的参数是params的,因此可以在多个字段中搜索
  
  下面来看看实现过程:
  首先参见了园子里YJingLee的一篇文章:NHibernate3剖析:Query篇之NHibernate.Linq自定义扩展
  有了这个知识基础,我们现在只需要自定义一个Linq扩展方法,然后让NHibernate把这个扩展方法映射成contains
或者freetext就可以了,首先定义一个Linq扩展方法:



public static class MyHibernateLinqExtensions
{
///
/// 对字段进行模式搜索
///
///
/// 关键字
/// 其他用于搜索的字段
///
public static bool ContainsSearch(this string source, string keyword, params string[] otherProperties)
{
throw new NotSupportedException("仅用于数据库搜索");
}
}
  
  查看HqlTreeBuilder,其中有个BooleanMethodCall可以帮忙:(以下是NHibernate的官方源码片段)



public HqlBooleanMethodCall BooleanMethodCall(string methodName, IEnumerable parameters)
{
return new HqlBooleanMethodCall(_factory, methodName, parameters);
}
  我们看一下Sql Server中contains谓词的调用语法:



select * from customer
where contains((company, phone, address), '熊猫手机')
  因此contains谓词的调用正是一个返回boolean类型的方法调用,参数 IEnumerable parameters是这个函数的参数的表达式形式列表
  要注意的是contains和freetext其实只有两个参数,第一个参数的实现方式可以看成是一个方法调用的结果,只是这个方法的methodName = string.Empty
  下面搬上整个扩展类


DSC0000.gif DSC0001.gif View Code


1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Linq.Expressions;
5 using System.Text;
6 using NHibernate;
7 using NHibernate.Linq;
8 using NHibernate.Hql;
9 using NHibernate.Hql.Ast;
10 using NHibernate.Hql.Ast.ANTLR;
11
12 namespace Ares.Service.Linq
13 {
14     public static class MyHibernateLinqExtensions
15     {
16         ///
17         /// 对字段进行模式搜索
18         ///
19         ///
20         /// 关键字
21         /// 其他用于搜索的字段
22         ///
23         public static bool ContainsSearch(this string source, string keyword, params string[] otherProperties)
24         {
25             throw new NotSupportedException("仅用于数据库搜索");
26         }
27
28         ///
29         /// 对字段进行开放式搜索
30         ///
31         ///
32         /// 关键字
33         /// 其他用于搜索的字段
34         ///
35         public static bool FreeSearch(this string source, string keyword, params string[] otherFields)
36         {
37             throw new NotSupportedException("仅用于数据库搜索");
38         }
39     }
40
41     class ContainsSearchGenerator : NHibernate.Linq.Functions.BaseHqlGeneratorForMethod
42     {
43         public ContainsSearchGenerator()
44         {
45             SupportedMethods = new[]
46             {
47                 ReflectionHelper.GetMethodDefinition
48                 (
49                     () => MyHibernateLinqExtensions.ContainsSearch(null, null, null)
50                 )
51             };
52         }
53
54         public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(System.Reflection.MethodInfo method, Expression targetObject, System.Collections.ObjectModel.ReadOnlyCollection arguments, NHibernate.Hql.Ast.HqlTreeBuilder treeBuilder, NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor)
55         {
56             var pms = arguments.Select(p => visitor.Visit(p).AsExpression()).ToArray();
57
58             var fields = treeBuilder.MethodCall("", pms.Where(p => p != pms[1]).ToArray());
59
60             return treeBuilder.BooleanMethodCall("contains", new HqlExpression[] { fields, pms[1] });
61         }
62     }
63
64     class FreeSearchGenerator : NHibernate.Linq.Functions.BaseHqlGeneratorForMethod
65     {
66         public FreeSearchGenerator()
67         {
68             SupportedMethods = new[]
69             {
70                 ReflectionHelper.GetMethodDefinition
71                 (
72                     () => MyHibernateLinqExtensions.FreeSearch(null, null, null)
73                 )
74             };
75         }
76
77         public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(System.Reflection.MethodInfo method, Expression targetObject, System.Collections.ObjectModel.ReadOnlyCollection arguments, NHibernate.Hql.Ast.HqlTreeBuilder treeBuilder, NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor)
78         {
79             var pms = arguments.Select(p => visitor.Visit(p).AsExpression()).ToArray();
80
81             var fields = treeBuilder.MethodCall("", pms.Where(p => p != pms[1]).ToArray());
82
83             return treeBuilder.BooleanMethodCall("freetext", new HqlExpression[] { fields, pms[1] });
84         }
85     }
86
87     class MyLinqToHqlGeneratorRegistry : NHibernate.Linq.Functions.DefaultLinqToHqlGeneratorsRegistry
88     {
89         public MyLinqToHqlGeneratorRegistry()
90         {
91             RegisterGenerator(ReflectionHelper.GetMethodDefinition(() => MyHibernateLinqExtensions.ContainsSearch(null, null, null)), new ContainsSearchGenerator());
92             RegisterGenerator(ReflectionHelper.GetMethodDefinition(() => MyHibernateLinqExtensions.FreeSearch(null, null, null)), new FreeSearchGenerator());
93         }
94     }
95 }
  注册这个扩展的方法(在实现Factory方法的地方):



private static ISessionFactory BuildFactory()
{
var cfg = new Configuration();
//大家实现SessionFactory的方法可能会略有区别,重要的是在BuildSessionFactory()之前,调用下面的方法将扩展类注册到你的Configuration对象即可
cfg.LinqToHqlGeneratorsRegistry();
cfg = cfg.Configure();
return cfg.BuildSessionFactory();
}
  
  现在我们就可以将全文搜索和其他各种Linq方法一起混合调用了。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-81931-1-1.html 上篇帖子: SQL server 2000 标准版、企业版、开发版、个人版各版本之间的区别 下篇帖子: Sql server 2005中output用法解析
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表