zoukankan      html  css  js  c++  java
  • Lucene.Net, SQL Server 2008全文检索, Like模糊查询的一点心得

    1.Like 模糊查询

    例如: select * from table where productdes like '%cad%'

    它不能利用索引查询, 只能是全表扫描, 效率较低, 而且不会分词, 只能按关键字老老实实的查, 但查询实时数据, 结果准确.

    2.SQL Server 2008全文检索

    为了提高效率, 换用sql server的全文检索, 怎么建全文检索就此略去, 不提, 只看查询方法.

    例如: select * from table where contains(ProductDesc, '*cad*')

    它查询的效率很高, 支持中文分词(但好不好就另说了), 但缺点竟然是在英文查询上, 如果想模糊查询带cad三个字母的数据, 它只能搜索出以cad为完整单词的数据, 例如: 它能查出abc cad , cad def, 或者cad, 它不能查出autocad这种字母连在一起的数据, 也就是说, sql server的全文检索的英文分词是空格, 要想查连在一起的英文词, 它办不到, 得另寻第三方的全文索引了, ms如果在这里开个可扩展的口子多好, 可惜了.

    索引的更新填充问题: 创建好全文检索就自动来一次完全填充, 如果在跟踪更改处选择自动, 全文检索就会采用基于更改跟踪的填充, 原表数据一旦有改动, 就会从后台悄悄地传播过来, 自动的更新全文索引. 具体可参考http://msdn.microsoft.com/zh-cn/library/ms142575.aspx

    在更新全文索引之前这段时间, 搜不到新录入但未收入全文索引的数据, 但like可查到. 虽然不是实时的, 但是经过测试, 我发现这个更新速度非常之快, 几乎一改原表, 全文索引就更新了, 所以我觉得可以认为实时的, 估计sqlserver内部应该是用观察者模式实现这个功能的.

    另外, contains包含的列都必须来自同一个表, 不能跨表, 例如where contains(a.ProductDesc, b.ProductName, '*cad*'), 这样是不行的.

    3.Lucene全文检索

    SQL server全文检索不灵了, 只能找第三方的方案了, 首当其冲的就是Lucene了, 但在.net下, Lucene却很不顺当. 

    NLucene是将 Lucene 从 Java 移植到 .NET 的一个 SourceForge 项目,它从 Lucene 1.2 版本转化而来, 但2002年就停止更新了.

    因为 NLucene 项目到2002年就没有再推出新的版本,可Lucene 却一直在发展,于是有人把Lucene 1.3版移植到.NET就成了Lucene .NET,但是Lucene .Net发展到2.0版的时候变成了商业化的产品,脱离了开源项目, 听说现在进了孵化器已停止开发了, 但上官网http://incubator.apache.org/lucene.net/download.html上看, 还仍然在更新中似乎没有停止, 最新开源的版本是2.9.2, 发布日期是2011年5月6日, 他们还在准备2.9.4版. 

    受到Lucene.Net脱离开源项目的影响,有人为了继续发展开源.Net搜索引擎,于是在Lucene.Net的原有基础上继续发展该项目,但是名字改成了DotLucene以区别于Lucene.Net。但现在打开官网一看, 得, 又停止了. 看来, 只能用Lucene.Net2.0这最后一个开源版本了.

    索引的更新填充问题: 也是要隔一段时间更新一次索引, 也是不可实时更新的, 需要定期更新填充才可以, 如果需要频繁更新推荐删除旧的然后重建索引.

    建索引, 查索引的代码如下:

            private void btnCreateIndex_Click(object sender, EventArgs e)
            {
                StringBuilder sbSQL = new StringBuilder();
                sbSQL.AppendLine(" Select ProductName, ProductDesc from Tbl_Product  ");
                dt = DataHelper.RetrieveData(sbSQL.ToString());
               
                //建立索引
                IndexWriter writer = CreateIndex(dt);
            }

            public IndexWriter CreateIndex(DataTable dt)
            {
                IndexWriter writer = new IndexWriter("c:/index/", new StandardAnalyzer(), true);
                try
                {
                    //建立索引字段
                    foreach(DataRow dr in dt.Rows)
                    {
                        Document doc=new Document();
                        doc.Add(new Field("ProductName", dr[0].ToString(), Field.Store.YES, Field.Index.TOKENIZED));
                        doc.Add(new Field("ProductDesc", dr[1].ToString(), Field.Store.YES, Field.Index.TOKENIZED));
                        writer.AddDocument(doc);                                   
                    }
                    writer.Optimize();
                    writer.Close();
                }
                catch(Exception e)
                {
                    MessageBox.Show(e.Message);              
                }
                return writer;
            }

            private void Search_Click(object sender, EventArgs e)
            {
                string key = this.textBox1.Text;

                IndexSearcher search = new IndexSearcher(@"c:/index/"); //把刚才建立的索引取出来

                Term t = new Term("ProductDesc", "*" + key + "*");
                WildcardQuery query = new WildcardQuery(t);

                Hits hit = search.Search(query);

                DataTable dt = new DataTable();
                dt.Columns.Add("ProductDesc");

                for (int i = 0; i <= hit.Length() - 1; i++)
                {
                    DataRow dr = dt.NewRow();
                    dr[0] = hit.Doc(i).GetField("ProductDesc").StringValue();
                    dt.Rows.Add(dr);
                }
                this.dataGridView1.DataSource = dt;
            }

    关键是下面这两句:

    Term t = new Term("ProductDesc", "*" + key + "*");
    WildcardQuery query = new WildcardQuery(t);

    它通过WildcardQuery(通配符查询, 包括?和*) 实现了类似like的模糊查询, 这样, 就解决了上面like查得出但慢, sqlserver full-text search虽快但查不出来的问题.

    但据说lucene建(更新)索引时不能查, 查时则不能建(更新)索引, 还没细看,如果是这样, 还是有些不爽.

    4.全文索引和like语句比较

    当然是全文索引的执行效率高.

    一般全文索引使用的是倒排索引,能够支持多关键字的索引,而LIKE只有前缀匹配时才能使用索引,否则就是全表扫描,效率当然很低

    但全文索引存在填充问题,需要在增加内容后进行增量填充,否则检索不到新增的内容的。Sql Server 2008里可采用基于更改跟踪的填充, 速度飞快, 几乎可以认为就是实时增量填充了.

    5.倒排索引 inverted index

    为什么Sql server全文检索和Lucune全文检索速度快呢, 因为它和大多数搜索引擎一样, 都使用了倒排索引 inverted index

    从别人那里抄了例子, 如下:

    假设有3篇文章:

    文章1的内容为:I was a student, i came from chengdu.

    文章2的内容为:My father is a teacher, and i am a student.

    文章3的内容为:Chengdu is my hometown. my father’s hometown is wuhan.

    Lucene是基于关键字索引和查询的。首先取得这3篇文章的关键字,通常需要如下处理措施(在Lucene中以下措施由Analyzer类完成):

    1)需要把3篇文章的内容切分成一个个单词,作为索引关键字的候选集。英文中单词与单词之间有空格,分词比较好处理,但中文字与字之间没有空隔,是连在一起的,所以中文分词处理要麻烦。

    2)需要将文章中分离出来的没有实际意义的单词过滤掉。比如a、from、and,中文中的“的”、“也”、“啊”等通常无具体含义的词可以去掉。还要过滤掉标点符号。

    3)单词需要统一大小写。

    4)查找与当前关键字相关联的其他关键字,即统一单词不同的时态。如“came”、“coming”统一成“come”,再进行查找。

    经过上面处理后,3篇文章的所有关键字为:

    文章1:[i] [am] [student] [i] [come] [chengdu]

    文章2:[my] [father] [is] [teacher] [i] [am] [student]

    文章3:[chengdu] [is] [my] [hometown] [my] [father] [hometown] [is] [wuhan]

    有了关键字后,就可以建立倒排索引了。上面的对应关系是顺排的,即“文章号→关键字”,倒排处理为“关键字→文章号”。文章1、2、3经过倒排后变成如表5-1所示。

    表5-1  倒排文件

       

       

    i

    12

    am

    12

    student

    12

    come

    1

    chengdu

    1,3

    my

    2,3

    father

    2,3

    is

    2,3

    teacher

    2

    hometown

    3

    wuhan

    3

    最后推荐几个学习资料:

    1.全文检索与Lucene学习
    http://www.cnblogs.com/keith2011/archive/2011/09/12/2173984.html

    2.Lucene:WildcardQuery
    http://hi.baidu.com/lewutian/blog/item/2f5adafb89cac468034f56ed.html

    3.lucene.net 查询与添加索引的同步问题
    http://blog.csdn.net/poson/article/details/2201880

    4.倒排文件索引(Inverted File Index)的建立(2)
    http://book.51cto.com/art/200906/128320.htm

    作者:BobLiu
    邮箱:lzd_ren@hotmail.com
    出处:http://www.cnblogs.com/liuzhendong
    本文版权归作者所有,欢迎转载,未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    mysqli使用记录
    D3力布图绘制--基本方法
    使用SVG绘制流程图
    关于echarts绘制树图形的注意事项(文字倾斜、数据更新、缓存重绘问题等)
    如何在iview组件中使用jsx
    素描学习记录2
    关于react-router-dom的一些记录
    素描学习记录1
    Typescript中一些不理解的概念解释(泛型、断言、解构、枚举)
    关于this的全面解析(call,apply,new)
  • 原文地址:https://www.cnblogs.com/liuzhendong/p/2194675.html
Copyright © 2011-2022 走看看