使用SQLite进行网站搜索


简介

如果你的网站需要或者程序需要一个文字搜索引擎, 那么除非你自己写一个, 不然你就要把你想要搜索的数据交给某一个网络搜索引擎然后花钱来索引你的网站, 但是现在你用我的程序就可以索引你的 text/HTML/ASP 文件了,它会将关键词保存到数据库以备将来的搜索之用.

程序使用 SQLite 作为数据库. 它是一个开源的数据库可以免费使用. 更多的信息可以参考这个网站.

我使用 Finisar 的 .NET 封装来使用 SQLite. 这个封装实现了一个 SQLite的 ADO.NET 驱动, 而且用起来很顺手. 它的 DLL 可以从 SourceForge下载.

using Finisar.SQLite;

数据库设计

从下图中你可以看到数据库的 E/R 链图:


这张图说明了 m<-> n 的关系, 根据这个关系我们把它转化为Word_Page表(索引表).

Word_Page
Wid Pid QTY

创建数据库 (class DBWSE)

我认为一个程序应该能够自己创建数据库那才算是一个完整的程序,也就是说它的可执行文件不需要带着一个空的数据库. 我们用 CREATE SQL 语句来创建所需要的表.

Collapse
private void CreateDataBase(string path)
{
try
{
FileStream fi = File.Create(path);
fi.Close();
DBFile = path;
OpenDataBase();
string strSQL = "Create Table words (" +
"WID INTEGER PRIMARY KEY ," +
"Word NVarChar(50)" +
")";
SQLiteCommand sqd = new SQLiteCommand(strSQL, sqconn);
sqd.ExecuteNonQuery();
sqd.CommandText ="Create Table pages(" +
"PID INTEGER PRIMARY KEY ," +
"path NVarChar(100) NOT NULL," +
"nofw INTEGER NOT NULL," +
"Date_add NVARChar(10) NOT NULL" +
")";
sqd.ExecuteNonQuery();
sqd.CommandText ="Create Table word_page(" +
"WID INTEGER FORIGEN KEY REFERENCES words (wid)," +
"PID INTEGER FORIGEN KEY REFERENCES pages (pid)," +
"QTY INTEGER NOT NULL," +
"PRIMARY KEY (WID,PID)" +
")";
sqd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
}

上面的代码创建了一个空的数据库文件.

向Word, Page, 和 Word_Page表中添加数据

现在我们要把数据存到数据库当中去. 为此需要使用 SQL 语言中的 INSERT 命令.

public int AddPage(string path, int nofw)
{
int i = PID(path);
if (i < 0)
{
// the page must be add
string strSQL = "INSERT INTO pages (path,nofw,Date_add)" +
"VALUES ('" + path + "'," + nofw + ",'" +
DateTime.Now.ToShortDateString() + "')";
SQLiteCommand sqc = new SQLiteCommand(strSQL, sqconn);
sqc.CommandText = strSQL;
sqc.ExecuteNonQuery();
//return PID
i = PID(path);
}
return i;
}

"PID 函数" 用来返回新页面的ID(page ID). 接下来我们把页面的数据存到数据库当中来.

private int createWord(string word)
{
string strsql = "INSERT INTO words (word) VALUES ('" + word + "')";
SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);
sqc.ExecuteNonQuery();
return WID(word);
}

这个函数将向 "Word" 表中添加一个关键词.

public void AddWord(int page, string word, int QTY)
{
int i = WID(word);
if (i < 0)
i = createWord(word);
string strsql = "INSERT INTO word_page (WID,PID,QTY) VALUES " +
"(" + i.ToString() + "," + page.ToString() +
"," + QTY.ToString() + ")";
SQLiteCommand sqc = new SQLiteCommand(strsql, sqconn);
sqc.ExecuteNonQuery();
}

按照现在的设计关键词是不能够重复的. 当查询某个词的时候如果找到了则返回这个词所对应的 ID (WID)否则返回 -1. 当然首先我们必须要把词存入 Words 表中. 实际上这个函数在储存页面索引的时候并没有保存更多的冗余的数据.

搜索数据库

现在我们可以在数据库中搜索某个词了.

public DataTable SearchWord(string word)
{
string strSQL = "SELECT pages.path,pages.nofw,pages.Date_add,word_page.QTY " +
"FROM words INNER JOIN word_page ON words.wid=word_page.wid" +
" INNER JOIN pages ON word_page.pid=pages.pid" +
" WHERE words.word='"+word+"'";
SQLiteDataAdapter sqd = new SQLiteDataAdapter(strSQL, sqconn);
DataTable dt = new DataTable();
sqd.Fill(dt);
return dt;
}

函数返回 DataTable 对象用来在程序中使用.

为保存在数据库中的数据建立索引

现在你已经把数据都保存在数据库当中了, 为了使用这些数据我们需要为这些页面建立索引!

开始的时候这项工作并不难! 我们可以用一些分隔符来把一段文章切分成词. 例如: 空格 , ',' , ')' , '(' , '[' ,'\' 等等, 但是这么做不是很精确.

你必须先去掉那些不重要的或者是重复的词, 然后再开始建立索引.

拆分文章段落

string[] split = words.Split (new Char[] { ' ', ',', '.', ':',';','{','}','[',
']','(',')','\'','"','\\','<','>','=','+','-','/','*','#','&','%','^',
'`','~','0','1','2','3','4','5','6','7','8','9' });

创建关键词列表(class ScanFile)

折叠
private void StartMonitoring(string p)
{
//first step must read file
StreamReader stR = File.OpenText(p);
string words = stR.ReadToEnd();
stR.Close();
string[] split = words.Split(new Char[] { ' ', ',', '.', ':',';','{','}','[',
']','(',')','\'','"','\\','<','>','=','+','-','/','*','#','&','%','^',
'`','~','0','1','2','3','4','5','6','7','8','9' });
max = split.Length;
int index;
int k = 0;
list1.Clear();
for (int i = 0; i < split.Length; i++)
{
WordInfo word = new WordInfo();
word.Word = split[i].Trim();
if (word.Word.Length > 2)
{
SearchableWord = word.Word;
index = list1.FindIndex(HaveWord);
if (index < 0)//not found
{
word++;
list1.Add(word);
k++;
}
else
{
// increment count of word
list1[index]++;
k++;
}
}
// Progress( this is a event )
OnProgress(System.EventArgs.Empty);
}
total = k;
}
private static bool HaveWord(WordInfo str)
{
if (str.Word.ToUpper() == SearchableWord.ToUpper())
return true;
else
return false;
}

在这个函数中我使用了 WordInfoList 类, 并且为检索文件创建了一个新的事件 "OnProgress".

SCF.Progress += new EventHandler(SCF_Progress);

为了找到 列表(List)中的词, 我们需要定义一个委托:

private static bool HaveWord(WordInfo str)

然而用这个参数我们还不能找到词,为了解决这个问题, 我添加了一个用于字符串比较的私有变量:

private static string SearchableWord;

垃圾词处理 (class RepluseTrivial)

为了排除那些没有价值的词, 你就必须有一个这些词的列表. 我是考虑把这些词存入数据库, 这样就可以使用 SQL 语句很方便的使用它们. 所以我创建了一个垃圾词表, 使用 insert/delete 来添加删除这些词.

当你建立一个 RepluseTrivial 对象的时候, 你需要给它传一个词的列表然后调用 Repulse 函数 来去除那些没有价值的词.

public void Repluse()
{
List temp = new List();
for (int i = 0; i < list1.Count; i++)
if (!IsTrivial(list1[i].Word))
temp.Add(list1[i]);
list1 = temp;
}

函数中调用 IsTrivial 函数来检查是否为垃圾词.

如何使用我的代码

现在你就有了一个剔除垃圾词的列表和一个用来存储这些词的数据库, 使用前面提到的算法调用"search 函数"去搜索吧. 当然这个函数还可以被进一步开发成像 Google 和 Microsoft 的引擎一样去隐藏的索引. (效率上可没法相比).

我们可以用一个进度条来显示进度:

public Form1()
{
InitializeComponent();
SCF = new ScanFile();
SCF.Progress += new EventHandler(SCF_Progress);
}
void SCF_Progress(object sender, EventArgs e)
{
progressBar1.Maximum = SCF.MaxProgress;
progressBar1.Value++;
}

创建一个剔除垃圾词的列表:

private void button2_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
SCF.Scan(textBox1.Text);
label2.Text = "Total Word:"+SCF.Total.ToString();
List list1=new List();
list1 = SCF.WordList;
string str =
Path.GetDirectoryName(Application.ExecutablePath)+
"\\Trivial.db3";
RepluseTrivial rt = new RepluseTrivial(str);
list1 = rt.Repluse(list1);
listView1.Items.Clear();
for (int i = 0; i < list1.Count; i++)
{
ListViewItem li = new ListViewItem(new string[] { i.ToString(),
list1[i].Count.ToString(), list1[i].Word });
listView1.Items.Add(li);
}
}

保存列表到数据库:

private void button3_Click(object sender, EventArgs e)
{
string str = Path.GetDirectoryName(Application.ExecutablePath);
DBWSE db = new DBWSE(str+""\\WSEDB.db3");
int i=db.AddPage(textBox1.Text,SCF.Total);
progressBar1.Maximum = SCF.WordList.Count;
progressBar1.Value = 0;
for (int j=0;j < SCF.WordList.Count;j++)
{
db.AddWord(i, SCF.WordList[j].Word, SCF.WordList[j].Count);
progressBar1.Value++;
}
}

你可以很容易的将查询的结果显示在一个 DataGridView 控件中:

private void button7_Click(object sender, EventArgs e)
{
string str = Path.GetDirectoryName(Application.ExecutablePath);
DBWSE db = new DBWSE(str + "\\WSEDB.db3");
dataGridView2.DataSource = db.SearchWord(textBox3.Text);
}

转自:http://www.sqlitechina.org/html/1/20071221/147.html
posted @ 2008-09-20 16:51  古道轻风  阅读(507)  评论(0编辑  收藏  举报