一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

擴(kuò)展Lucene的索引文件存儲(chǔ)

 軟件團(tuán)隊(duì)頭目 2007-03-08

擴(kuò)展Lucene的索引文件存儲(chǔ)

2007/02/01  作者:劉冬   心情: sunny  星期四 (12:01)

本文主要敘述如何通過引入Commons-VFS項(xiàng)目來擴(kuò)展Lucene的索引文件存儲(chǔ)方式。在閱讀本文之前,您必須對(duì)Lucene有一定的了解,最好是有編寫過Lucene代碼。另外文章中所提到的Lucene如果不做特殊說明指的是LuceneJava版本。

使用過Lucene來做為搜索引擎的朋友知道,Lucene默認(rèn)的使用文件系統(tǒng)來存儲(chǔ)索引文件。一般我們需要指定一個(gè)路徑做為參數(shù)來初始化索引的讀寫類。例如下面語句準(zhǔn)備在D盤的lucene_idx目錄下寫索引信息。

IndexWriter idx_writer = new IndexWriter(“D:/lucene_idx”,new StandardAnalyzer(),false);

因此整個(gè)系統(tǒng)的索引一般就會(huì)集中在某臺(tái)服務(wù)器的某個(gè)目錄。為了保證寫索引不會(huì)產(chǎn)生沖突,經(jīng)常我們只運(yùn)行一個(gè)負(fù)責(zé)寫索引的進(jìn)程,其他的應(yīng)用可以訪問索引目錄進(jìn)行查詢。當(dāng)應(yīng)用程序是集群環(huán)境下的不同機(jī)器時(shí),其他的機(jī)器如何訪問到索引服務(wù)器上的索引數(shù)據(jù)目錄呢?你肯定已經(jīng)想到很多種方法來訪問其他機(jī)器的目錄:例如Windows機(jī)器可以通過網(wǎng)上鄰居;Linux服務(wù)器之間可以通過NFS訪問;或者LinuxWindows服務(wù)器之間可以通過SAMBA來相互訪問各自的文件系統(tǒng)。還有沒有其他更加靈活的解決方案嗎?

當(dāng)然有,通過擴(kuò)展Lucene的索引存儲(chǔ)接口,你可以通過FTP、HTTP來讀寫索引文件,你甚至可以將Lucene的索引數(shù)據(jù)保存在數(shù)據(jù)庫中,雖然我不知道這到底是不是一個(gè)好主意,但起碼技術(shù)上是可行的。

為了使我們對(duì)Lucene的擴(kuò)展有著實(shí)際的意義,我們將引入另外一個(gè)Apache組織新的開源項(xiàng)目—— Commons-VFS,我們簡(jiǎn)稱為VFSVFS項(xiàng)目把對(duì)各種各樣的文件系統(tǒng)的訪問封裝成統(tǒng)一的應(yīng)用程序接口,這大大的簡(jiǎn)化了應(yīng)用程序本身代碼的復(fù)雜度。目前VFS支持下面一些文件系統(tǒng),而這個(gè)列表會(huì)越來越多,當(dāng)然你也可以自行進(jìn)行擴(kuò)展。

FTP
Local Files
HTTP and HTTPS
SFTP
Temporary Files
Zip, Jar and Tar (uncompressed, tgz or tbz2)
gzip and bzip2
res
ram

或許你已經(jīng)猜出來了引入VFS項(xiàng)目的目的:我們要利用VFS 項(xiàng)目和Lucene結(jié)合,使Lucene的索引文件可以存放在VFS所支持的各種可讀寫的文件系統(tǒng)中。

因此接下來我們的任務(wù)就是讓Lucene使用VFS來存取索引數(shù)據(jù),至于這些數(shù)據(jù)放在哪里,是放在數(shù)據(jù)庫、文件系統(tǒng)還是其他地方,這個(gè)問題就交給VFS的配置去處理了。

你準(zhǔn)備好了嘛?下面我們要深入到Lucene的代碼中去探個(gè)究竟了。首先我們注意到了IndexReader、IndexWriter、IndexSearcher等這幾個(gè)索引數(shù)據(jù)的讀寫類的構(gòu)造函數(shù)都可以接受名為org.apache.lucene.store.Directory 的類做為參數(shù)。再查看api文檔發(fā)現(xiàn)Directory有兩個(gè)子類分別是:FSDirectoryRAMDirectory,從類名上可以猜出FSDirectory是基于文件系統(tǒng)的,而RAMDirectory是基于內(nèi)存的。再讀一下IndexWriter類的源碼后證實(shí)了這個(gè)猜想,當(dāng)我們傳遞路徑字符串而不是Directory對(duì)象給IndexXxxxx類時(shí),Lucene會(huì)初始化一個(gè)FSDirectory對(duì)象來處理索引數(shù)據(jù)的讀寫操作。

而另外一個(gè)RAMDirectory類是基于內(nèi)存的索引數(shù)據(jù)存取,它是Lucene所提供的第二種索引數(shù)據(jù)存取方式。它可以接受一個(gè)FSDirectory做為構(gòu)造函數(shù)的參數(shù),相當(dāng)于把存放在磁盤中的索引數(shù)據(jù)全部加載到內(nèi)存中,以提高索引搜索的性能,我們暫不理會(huì)這種方式。但我們已經(jīng)了解到org.apache.lucene.store包中的類便是Lucene存儲(chǔ)的核心,我們可以通過擴(kuò)展Directory類來實(shí)現(xiàn)存儲(chǔ)自定義。姑且把所要擴(kuò)展的這個(gè)類命名為VFSDirectory。

先來看看Directory都有什么方法:

Method Summary

abstract void

close() 關(guān)閉存儲(chǔ).

abstract IndexOutput

createOutput(String name) 使用給定的名字創(chuàng)建一個(gè)新的空文件.

abstract void

deleteFile(String name) 刪除指定名字的文件.

abstract boolean

fileExists(String name) 判斷文件是否存在.

abstract long

fileLength(String name) 獲取文件長(zhǎng)度.

abstract long

fileModified(String name) 返回文件最近修改的時(shí)間.

abstract String[]

list() 列出所有的文件.

abstract Lock

makeLock(String name) 構(gòu)造一個(gè)鎖對(duì)象.

abstract IndexInput

openInput(String name) 獲取文件輸入流.

abstract void

renameFile(String from, String to) 文件重命名.

abstract void

touchFile(String name) 設(shè)置文件最近修改時(shí)間為當(dāng)前時(shí)間.

上面這些都是跟文件操作密切相關(guān)的方法,應(yīng)該也是大家天天要打交道的文件操作方法。為了不至于誤解這些方法的意思,我們不妨看看FSDirectory的源碼。以下VFSDirectory類的完整代碼,該類已經(jīng)在實(shí)際的應(yīng)用中測(cè)試無誤。此類的構(gòu)造函數(shù)接受兩個(gè)uri做為參數(shù),分別是存放索引文件的URI以及鎖文件的URI,關(guān)于URI的格式請(qǐng)參考VFS項(xiàng)目的文檔。性能方面由于采用了跟FSDirectory一致的BufferedIndexInputBufferedIndexOuput,因此二者并沒有太大區(qū)別。

package dlog.common.search;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.RandomAccessContent;
import org.apache.commons.vfs.VFS;
import org.apache.commons.vfs.util.RandomAccessMode;
import org.apache.lucene.store.BufferedIndexInput;
import org.apache.lucene.store.BufferedIndexOutput;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.Lock;

/**
*
擴(kuò)展Lucene索引的存儲(chǔ),使用commons-vfs做為lucene的存儲(chǔ)引擎
* @author Winter Lau
* @email javayou@gmail.com
* @home http://www./javayou
*/

public class VFSDirectory extends Directory {
private FileSystemManager fileManager;
private FileObject directory;
private FileObject locker;

public VFSDirectory(String repository_uri, String lock_uri) throws IOException {
this.fileManager = VFS.getManager();
this.directory = fileManager.resolveFile(repository_uri);
if(!directory.exists())
directory.createFolder();
this.locker = fileManager.resolveFile(lock_uri);
if(!locker.exists())
locker.createFolder();
}

@Override
public void close() throws IOException {
directory.close();
}

protected void finalize() throws IOException {
close(); // close the directory
}

@Override
public IndexOutput createOutput(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
return new VFSIndexOutput(file);
}

@Override
public IndexInput openInput(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
return new VFSIndexInput(file);
}

@Override
public void deleteFile(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
//System.out.println(file.getName().getFriendlyURI());
if(!file.delete())
throw new IOException("Cannot delete " + file);
}

@Override
public boolean fileExists(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
return file.exists();
}

@Override
public long fileLength(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
return file.getContent().getSize();
}

@Override
public long fileModified(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
return file.getContent().getLastModifiedTime();
}

@Override
public String[] list()throws IOException {
List names = new ArrayList();
FileObject[] files = directory.getChildren();

for(int i=0;i

@Override
public Lock makeLock(String name) {

final StringBuffer buf = getLockPrefix();
buf.append("-");
buf.append(name);

try{

final FileObject lockFile = this.fileManager.resolveFile(locker, buf.toString());

return new Lock() {
public boolean obtain() throws IOException {
lockFile.createFile();
return lockFile.exists();
}

public void release() {
try {
lockFile.delete();
} catch (FileSystemException e) {
throw new RuntimeException("Cannot release lock : " + buf.toString(), e);
}
}

public boolean isLocked() {
try {
return lockFile.exists();
} catch (FileSystemException e) {
throw new RuntimeException("Cannot check locking status : " + buf.toString(), e);
}
}

public String toString() {
return "Lock@" + lockFile;
}
};
} catch (IOException e){
throw new RuntimeException("Request lock failed : " + buf.toString(), e);
}
}

private static MessageDigest DIGESTER;

/**
* So we can do some byte-to-hexchar conversion below
*/
static char[] HEX_DIGITS = { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘a(chǎn)‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘ };

static {
try {
DIGESTER = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e.toString(), e);
}
}

private StringBuffer getLockPrefix() {

String dirName; // name to be hashed

try {
dirName = directory.getURL().toString();
} catch (IOException e) {
throw new RuntimeException(e.toString(), e);
}

byte digest[];

synchronized (DIGESTER) {
digest = DIGESTER.digest(dirName.getBytes());
}

StringBuffer buf = new StringBuffer();
buf.append("lucene-");
for (int i = 0; i < digest.length; i++) {
int b = digest[i];
buf.append(HEX_DIGITS[(b >> 4) & 0xf]);
buf.append(HEX_DIGITS[b & 0xf]);
}
return buf;
}

@Override
public void renameFile(String name1, String name2) throws IOException {
FileObject file1 = this.fileManager.resolveFile(directory, name1);
FileObject file2 = this.fileManager.resolveFile(directory, name2);
try{
IOUtils.copy(file1.getContent().getInputStream(), file2.getContent().getOutputStream());
}finally{
close(file2);
close(file1);
}
}

@Override
public void touchFile(String name) throws IOException {
FileObject file = this.fileManager.resolveFile(directory, name);
file.getContent().setLastModifiedTime(System.currentTimeMillis());
}

private void close(FileObject file){
if(file!=null){
try{
file.close();
}catch (IOException e) {
throw new RuntimeException("Cannot close input stream: " + e.toString(), e);
}
}
}

private static class VFSIndexInput extends BufferedIndexInput {

private long length;
private RandomAccessContent content;

public VFSIndexInput(FileObject file) throws IOException {
this.content = file.getContent().getRandomAccessContent(RandomAccessMode.READ);
this.length = content.length();
}

@Override
public void close() throws IOException {
this.content.close();
}

@Override
public long length() {
return length;
}

@Override
protected void readInternal(byte[] b, int off, int len) throws IOException {
this.content.readFully(b, off, len);
}

@Override
protected void seekInternal(long position) throws IOException {
this.content.seek(position);
}
}

private static class VFSIndexOutput throws BufferedIndexOutput {

private RandomAccessContent content;

public VFSIndexOutput(FileObject file) throws IOException{
if(!file.exists())
file.createFile();
this.content = file.getContent().getRandomAccessContent(RandomAccessMode.READWRITE);
}

@Override
protected void flushBuffer(byte[] b, int size) throws IOException {
this.content.write(b, 0, size);
}

@Override
public void seek(long pos) throws IOException {
super.seek(pos);
//此行代碼非常重要,否則會(huì)導(dǎo)致寫文件錯(cuò)誤
this.content.seek(pos);
}

@Override
public long length() throws IOException {
return this.content.length();
}

@Override
public void close() throws IOException {
super.close();
this.content.close();
}

protected void finalize() throws IOException {
close(); // close the file
}
}
}

有了VFSDirectory這個(gè)類后,使用就非常方便了。我們只需要構(gòu)造一個(gè)VFSDirectory對(duì)象,并傳遞給IndexReaderIndexWriter、IndexSearcher等即可。為了方便各位讀者進(jìn)行測(cè)試,本文中的所有代碼請(qǐng)看參考資料中的源碼下載。

完成了VFSDirectory后,就相當(dāng)于給Lucene注入更多的功力。任何VFSDirectory所支持的可讀寫文件系統(tǒng)都可以被Lucene用來存放索引數(shù)據(jù)。如果是VFS本身不支持的文件系統(tǒng)我們也可以通過擴(kuò)展VFS來實(shí)現(xiàn)對(duì)Lucene的擴(kuò)展。

最后補(bǔ)充一點(diǎn)關(guān)于本文所涉及的源碼的使用說明,編譯源碼需要lucene以及vfs包的支持,編譯成功后直接運(yùn)行TestLuceneVFS類即可進(jìn)行測(cè)試。

參考資料:

源碼下載地址:http://www./uploads/files/lucene_and_vfs.zip
Lucene
官方網(wǎng)站:http://lucene.
Commons-VFS
項(xiàng)目網(wǎng)站:http://jakarta./commons/vfs

關(guān)于作者:

劉冬,一直使用J2EE/J2ME從事移動(dòng)互聯(lián)網(wǎng)方面的開發(fā)。您可以通過Java自由人網(wǎng)站來跟他聯(lián)系,網(wǎng)址是:http://www./javayou ,另外他的郵件地址是javayou@gmail.com。
關(guān)鍵字: Lucene  VFS 
引用地址: http://www./html/trackback.do?id=5903&type=1 (復(fù)制地址)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国内外免费在线激情视频| 99久久无色码中文字幕免费| 成人精品欧美一级乱黄| 精品国产亚洲av成人一区| 亚洲一区二区三区福利视频| 国产在线视频好看不卡| 欧美一区二区口爆吞精| 亚洲一区二区三区精选| 日韩精品中文字幕亚洲| 国产福利一区二区三区四区| 中文字幕一区二区三区大片| 国产传媒高清视频在线| 中国一区二区三区人妻| 在线一区二区免费的视频| 午夜成年人黄片免费观看| 欧美大黄片在线免费观看| 成人精品国产亚洲av久久| 日本高清一区免费不卡| 久久精品免费视看国产成人| 亚洲精品黄色片中文字幕| 国产午夜精品亚洲精品国产| 日韩欧美黄色一级视频| 欧美日韩精品综合一区| 日韩精品一区二区三区四区| 日韩精品毛片视频免费看| 久久综合亚洲精品蜜桃| 中日韩免费一区二区三区| 国产精品视频一级香蕉| 国产偷拍精品在线视频| 大尺度剧情国产在线视频| 东京热电东京热一区二区三区| 粉嫩国产一区二区三区在线| 日韩欧美中文字幕av| 国产欧美亚洲精品自拍| 久热99中文字幕视频在线| 激情国产白嫩美女在线观看| 色综合久久六月婷婷中文字幕| 91亚洲国产成人久久| 精品久久综合日本欧美| 日韩一级免费中文字幕视频| 亚洲高清一区二区高清|