用Lucene.Net轻松创建最简单的搜索引擎
Lucene是当下最受欢迎的Java开源搜索引擎开发包. 其实从本质上说, 它就是一个索引和检索的软件系统.
在Lucene中有一种特定的类型: Document, 它是一个Key-Value类型的集合. 我们只要将需要被索引的内容填入进去, Lucene就能够自动为其建立索引, 并且提供搜索. 因此, 通过Document, Lucene可以索引任何类型.
前几天一个做职位信息搜索的朋友让我给他的搜索引擎提提意见, 我试用后发现他其实需要一个很好的索引/检索系统来改善搜索效率. 于是就推荐Lucene.Net(Lucene.Net是Lucene的.Net版本)给他用.
用Lucene.Net创建搜索引擎其实十分简单, 为了让他更加容易上手, 我写了一个文件搜索引擎作为Demo:
Demo包含两个部分:
DirIndexer 和 DirSearcher
顾名思义, DirIndexer是用来创建索引的程序, 而DirSearcher是用来进行搜索的程序.
先说DirIndexer. 其中, 我们要用到Lucene.Net的5个核心类:
IndexWriter
Directory
Analyzer
Document
Field
它们之间的关系如下:
实现代码如下:
在Lucene中有一种特定的类型: Document, 它是一个Key-Value类型的集合. 我们只要将需要被索引的内容填入进去, Lucene就能够自动为其建立索引, 并且提供搜索. 因此, 通过Document, Lucene可以索引任何类型.
前几天一个做职位信息搜索的朋友让我给他的搜索引擎提提意见, 我试用后发现他其实需要一个很好的索引/检索系统来改善搜索效率. 于是就推荐Lucene.Net(Lucene.Net是Lucene的.Net版本)给他用.
用Lucene.Net创建搜索引擎其实十分简单, 为了让他更加容易上手, 我写了一个文件搜索引擎作为Demo:
Demo包含两个部分:
DirIndexer 和 DirSearcher
顾名思义, DirIndexer是用来创建索引的程序, 而DirSearcher是用来进行搜索的程序.
先说DirIndexer. 其中, 我们要用到Lucene.Net的5个核心类:
IndexWriter
Directory
Analyzer
Document
Field
它们之间的关系如下:
实现代码如下:
using System;
using System.IO;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using Lucene.Net.Analysis.Standard;
namespace DirIndexer
{
class Indexer
{
[STAThread]
static void Main(string[] args)
{
/* *
* args[0]: Index Directory;
* args[1]: Data Directory;
* args[2]: Pattern, like "*.txt", "*.cs", etc.;
* */
new Indexer(args[0], args[1], args[2]);
}
public Indexer(string indexDir, string dataDir, string pattern)
{
Lucene Code
DateTime start = DateTime.Now;
DoIndex(writer, new DirectoryInfo(dataDir), pattern);
DateTime end = DateTime.Now;
int docNum = writer.DocCount();
Console.WriteLine("Index Finished. {0} Documents takes {1} second.", docNum, ((TimeSpan)(end - start)).TotalSeconds);
writer.Optimize();
writer.Close();
}
private static void DoIndex(IndexWriter writer, DirectoryInfo dir, string pattern)
{
FileInfo[] files = dir.GetFiles(pattern);
foreach(FileInfo fi in files)
{
Lucene Code
}
DirectoryInfo[] dirs = dir.GetDirectories();
foreach(DirectoryInfo di in dirs)
{
DoIndex(writer, di, pattern);
}
}
}
}
using System.IO;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using Lucene.Net.Analysis.Standard;
namespace DirIndexer
{
class Indexer
{
[STAThread]
static void Main(string[] args)
{
/* *
* args[0]: Index Directory;
* args[1]: Data Directory;
* args[2]: Pattern, like "*.txt", "*.cs", etc.;
* */
new Indexer(args[0], args[1], args[2]);
}
public Indexer(string indexDir, string dataDir, string pattern)
{
Lucene Code
DateTime start = DateTime.Now;
DoIndex(writer, new DirectoryInfo(dataDir), pattern);
DateTime end = DateTime.Now;
int docNum = writer.DocCount();
Console.WriteLine("Index Finished. {0} Documents takes {1} second.", docNum, ((TimeSpan)(end - start)).TotalSeconds);
writer.Optimize();
writer.Close();
}
private static void DoIndex(IndexWriter writer, DirectoryInfo dir, string pattern)
{
FileInfo[] files = dir.GetFiles(pattern);
foreach(FileInfo fi in files)
{
Lucene Code
}
DirectoryInfo[] dirs = dir.GetDirectories();
foreach(DirectoryInfo di in dirs)
{
DoIndex(writer, di, pattern);
}
}
}
}
其中Lucene Code标签中包含的就是所有和Lucene.Net相关的代码, 一共6行有效代码, 其他的都是用来遍历文件夹等逻辑的代码.
OK, 运行一下, 命令行参数如下:
args[0] = "f:\index" //创建索引的位置
args[1] = "f:\工作区\myprojects" //要索引的文件位置
args[2] = "*.cs" //要索引的文件类型
1594个文档, 一共用时91秒, 平均每秒17个文档. 我用的是一台CPU 1G, 内存512M的机器, 呵呵, 比较古董, 大家的应该比我的快才对.
下面看看DirSearcher.在搜索的时候, 我们也要用到6个核心类:
IndexSearcher
Term
Query
TermQuery
QueryParser
Hits
其关系如下:
其中实线和虚线分别代表了两种不同的搜索实现方式.
代码如下:
using System;
using Lucene.Net.Documents;
using Lucene.Net.Search;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.QueryParsers;
namespace DirSearcher
{
class Searcher
{
[STAThread]
static void Main(string[] args)
{
/* *
* args[0]: Index Directory
* */
Searcher s = new Searcher(args[0]);
Console.WriteLine("Please input query string:");
string query = "";
while((query = Console.ReadLine()) != "")
{
Hits hits = s.Search(query);
Console.WriteLine("Press ENTER to see details:");
Console.ReadLine();
for(int i = 0; i < hits.Length(); i++)
{
Document doc = hits.Doc(i);
Console.WriteLine(doc.Get("filename"));
}
Console.WriteLine("----------------------------------------------------");
}
}
private string dir = "";
public Searcher(string indexDir)
{
dir = indexDir;
}
public Hits Search(string q)
{
Lucene Code
Console.WriteLine("Search Finished. {0} Results takes {1} second:", hits.Length(), ((TimeSpan)(end - start)).TotalSeconds);
return hits;
}
}
}
using Lucene.Net.Documents;
using Lucene.Net.Search;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.QueryParsers;
namespace DirSearcher
{
class Searcher
{
[STAThread]
static void Main(string[] args)
{
/* *
* args[0]: Index Directory
* */
Searcher s = new Searcher(args[0]);
Console.WriteLine("Please input query string:");
string query = "";
while((query = Console.ReadLine()) != "")
{
Hits hits = s.Search(query);
Console.WriteLine("Press ENTER to see details:");
Console.ReadLine();
for(int i = 0; i < hits.Length(); i++)
{
Document doc = hits.Doc(i);
Console.WriteLine(doc.Get("filename"));
}
Console.WriteLine("----------------------------------------------------");
}
}
private string dir = "";
public Searcher(string indexDir)
{
dir = indexDir;
}
public Hits Search(string q)
{
Lucene Code
Console.WriteLine("Search Finished. {0} Results takes {1} second:", hits.Length(), ((TimeSpan)(end - start)).TotalSeconds);
return hits;
}
}
}
和DirIndexer一样, Lucene Code标记中就是Lucenen.Net的代码, 一共3行有效代码.
OK, 运行一下:
args[0] = "f:\index" //创建索引的位置
怎么样? 十分简单吧. 这就是Lucene的力量. Demo的完整代码在下面, 希望了解Lucene的朋友赶快试试吧. 感谢关注!
DirIndex & DirSearcher