使用Lucene+Paoding构建SSH系统的站内搜索

使用Lucene+Paoding构建SSH系统的站内搜索

关键字: lucene paoding 搜索

目标:创建一个具有高度可移植的,定时创建索引的站内搜索。
途径:dic和index都放到程序中去。

准备:
1   Lucene
Lucene Java(以下简称Lucene)目前可用版本是2.4.0,关于Lucene的详细信息请查看 http://lucene.apache.org/java/docs/index.html


2 Paoding
Qieqie同学的伟大作品、优秀的Lucene中文分词组件,目前的版本为paoding-analysis-2.0.4-beta,对应的Lucene的版本为2.2。关于Paoding的具体信息请查看 http://code.google.com/p/paoding/


3 下载最新的paoding-analysis-2.0.4-beta版本(里面包含了lucene-core-2.2.0.jar, lucene-analyzers-2.2.0.jar,lucene-highlighter-2.2.0.jar, junit.jar, commons-logging.jar)。



开始工作:
   1 试运行
打开下载包中的examples文件夹,运行一下吧(注意一下编码)。


   2 集成到SSH2系统中去 (系统结构Action->service->dao)
1)  由于SSH2系统是web系统,因此在配置Paoding上就有可能和第一步有些不同。
直接把paoding文件夹下的src文件夹下的所有文件和dic文件夹复制到你的项目中去。打开paoding-dic-home.properties文件,修改paoding.dic.home.config-fisrt=this,使得程序知道该配置文件,修改paoding.dic.home=classpath:dic,使得字典在该项目中。保存就可以了。在这里我使用了classpath:dic是为了增加可移植性。如果使用绝对路径没有什么可说的了,但是如果你是制定为classpath:dic,则需要修改一下Paoding中的代码了。找到PaodingMaker.java的setDicHomeProperties方法,修改File dicHomeFile = getFile(dicHome);为

Java代码 复制代码
  1. File dicHomeFile2 = getFile(dicHome);   
  2.         String path="";   
  3.         try {   
  4.             path = URLDecoder.decode(dicHomeFile2.getPath(),"UTF-8");   
  5.         } catch (UnsupportedEncodingException e) {   
  6.             e.printStackTrace();   
  7.         }   
  8.     File dicHomeFile = new File(path);  
File dicHomeFile2 = getFile(dicHome);
		String path="";
		try {
			path = URLDecoder.decode(dicHomeFile2.getPath(),"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	File dicHomeFile = new File(path);

目的是解码,不然如果你的词典路径中有空格和汉字会出现找不到字典的异常。

2)表结构

Sql代码 复制代码
  1. CREATE TABLE `news` (   
  2.   `id` int(11) NOT NULL auto_increment,   
  3.   `title` varchar(255) default NULL,   
  4.   `details` mediumtext,   
  5.   `author` varchar(255) default NULL,   
  6.   `publisher` varchar(100) default NULL,   
  7.   `clicks` int(11) default NULL,   
  8.   `source` varchar(255) default NULL,   
  9.   `addtime` datetime default NULL,   
  10.   ` category ` varchar(100) default NULL,   
  11.   `keywords` varchar(255) default NULL,   
  12.   PRIMARY KEY  (`id`)   
  13. ) ENGINE=InnoDB DEFAULT CHARSET=gbk;  
CREATE TABLE `news` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(255) default NULL,
  `details` mediumtext,
  `author` varchar(255) default NULL,
  `publisher` varchar(100) default NULL,
  `clicks` int(11) default NULL,
  `source` varchar(255) default NULL,
  `addtime` datetime default NULL,
  ` category ` varchar(100) default NULL,
  `keywords` varchar(255) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk;



    3 正式实施编码
       编写站内搜索分为两步:创建索引和进行搜索,所需类:SearchAction.java和TaskAction.java(同一目录)
1) 创建索引
主要任务:从已有的txt文件中读取上一次进行索引的最后一条新闻的id号,然后从业务逻辑中查找大于这个id号的所有新闻进行索引,最后把这次最后的一条新闻id写入txt文件中。在这里要处理好路径的问题。在这里所有的记录id号的txt文件都放到了action目录下面。
新建TaskAction,增加如下方法

Java代码 复制代码
  1. public void createIndex() {   
  2.         String path;   
  3.         try {      
  4. //两个参数:创建索引的位置  和 上一次创建索引最后的新闻id所在文件   
  5.     createNewsIndex(getPath(TaskAction.class"date/index/news"),"newsid.txt");   
  6.         } catch (Exception e) {   
  7.             e.printStackTrace();   
  8.         }   
  9.     }   
  10.   
  11. public String getPath(Class clazz, String textName)   
  12.             throws IOException {   
  13.         String path = (URLDecoder.decode(   
  14.                 clazz.getResource(textName).toString(), "UTF-8")).substring(6);        
  15.         return path;   
  16.     }   
  17.   
  18. public void createNewsIndex(String path,String textName) throws Exception {   
  19.         String newsId = "0";   
  20.            
  21.         newsId = readText(TaskAction.class, textName);   
  22.         if (null ==newsId || "".equals(newsId))   
  23.             newsId = "0";   
  24.   
  25.         // 使用paoding中文分析器   
  26.         Analyzer analyzer = new PaodingAnalyzer();   
  27.         FSDirectory directory = FSDirectory.getDirectory(path);   
  28.         System.out.println(directory.toString());   
  29.         IndexWriter writer = new IndexWriter(directory, analyzer, isEmpty(TaskAction.class, textName));   
  30.         Document doc = new Document();   
  31.   
  32.         // 从业务逻辑层读取大于当前id的信息   
  33.         List list = newsManageService.getNewsBigId(Integer.parseInt(newsId));   
  34.         Iterator iterator = list.iterator();   
  35.         News news = new News();   
  36.         while (iterator.hasNext()) {   
  37.             doc = new Document();   
  38.             news = (News) iterator.next();   
  39.             doc.add(new Field("id""" + news.getId(), Field.Store.YES,   
  40.                     Field.Index.UN_TOKENIZED));   
  41.             doc.add(new Field("title""" + news.getTitle(), Field.Store.YES,   
  42.                     Field.Index.TOKENIZED));   
  43.             doc.add(new Field("author""" + news.getAuthor(), Field.Store.YES,   
  44.                     Field.Index.TOKENIZED));   
  45.             doc.add(new Field("details"""  
  46.                     + Constants.splitAndFilterString(news.getDetails()),   
  47.                     Field.Store.YES, Field.Index.TOKENIZED,   
  48.                     Field.TermVector.WITH_POSITIONS_OFFSETS));   
  49.             doc.add(new Field("addtime""" + news.getAddtime(),   
  50.                     Field.Store.YES, Field.Index.TOKENIZED));   
  51.             doc.add(new Field("keywords""" + news.getKeywords(),   
  52.                     Field.Store.YES, Field.Index.TOKENIZED));   
  53.             System.out.println("Indexing file " + news.getName() + "...");   
  54.             articleId = String.valueOf(news.getId());   
  55.             try {   
  56.                 writer.addDocument(doc);   
  57.             } catch (IOException e) {   
  58.                 e.printStackTrace();   
  59.             }   
  60.         }   
  61.         // 优化并关闭   
  62.         writer.optimize();   
  63.         writer.close();   
  64.   
  65.         // 将我索引的最后一篇文章的id写入文件   
  66.         String content = WriteText(TaskAction.class,   
  67.                 textName, newsId);   
  68.     }      
  69.   
  70. public boolean isEmpty(Class clazz, String textName) throws Exception {   
  71.         String articleId = "0";   
  72.         boolean isEmpty = true;   
  73.         articleId = ContentReader.readText(clazz, textName);   
  74.         if (null == articleId || "".equals(articleId))   
  75.             articleId = "0";   
  76.         if (!articleId.equals("0"))   
  77.             isEmpty = false;   
  78.         System.out.println(clazz.getName()+" "+isEmpty);   
  79.         return isEmpty;   
  80.     }   
  81.   
  82. //该方法参考了paoding中example中的一个方法。   
  83. public String readText(Class clazz, String textName)   
  84.             throws IOException {   
  85.         InputStream in = clazz.getResourceAsStream(textName);   
  86.         Reader re = new InputStreamReader(in, "UTF-8");   
  87.         char[] chs = new char[1024];   
  88.         int count;   
  89.         String content = "";   
  90.         while ((count = re.read(chs)) != -1) {   
  91.             content = content + new String(chs, 0, count);   
  92.         }   
  93.         return content;   
  94.     }   
  95.   
  96. public String WriteText(Class clazz, String textName, String text)   
  97.             throws IOException {   
  98.         String path = (URLDecoder.decode(   
  99.                 clazz.getResource(textName).toString(), "UTF-8")).substring(6);   
  100.         System.out.println(path);   
  101.         File file = new File(path);   
  102.         BufferedWriter bw = new BufferedWriter(new FileWriter(file));   
  103.         String temp = text;   
  104.         bw.write(temp);   
  105.         bw.close();   
  106.         return temp;   
  107.     }  
public void createIndex() {
		String path;
		try {	
//两个参数:创建索引的位置  和 上一次创建索引最后的新闻id所在文件
	createNewsIndex(getPath(TaskAction.class, "date/index/news"),"newsid.txt");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

public String getPath(Class clazz, String textName)
			throws IOException {
		String path = (URLDecoder.decode(
				clazz.getResource(textName).toString(), "UTF-8")).substring(6);		
		return path;
	}

public void createNewsIndex(String path,String textName) throws Exception {
		String newsId = "0";
		
		newsId = readText(TaskAction.class, textName);
		if (null ==newsId || "".equals(newsId))
			newsId = "0";

		// 使用paoding中文分析器
		Analyzer analyzer = new PaodingAnalyzer();
		FSDirectory directory = FSDirectory.getDirectory(path);
		System.out.println(directory.toString());
		IndexWriter writer = new IndexWriter(directory, analyzer, isEmpty(TaskAction.class, textName));
		Document doc = new Document();

		// 从业务逻辑层读取大于当前id的信息
		List list = newsManageService.getNewsBigId(Integer.parseInt(newsId));
		Iterator iterator = list.iterator();
		News news = new News();
		while (iterator.hasNext()) {
			doc = new Document();
			news = (News) iterator.next();
			doc.add(new Field("id", "" + news.getId(), Field.Store.YES,
					Field.Index.UN_TOKENIZED));
			doc.add(new Field("title", "" + news.getTitle(), Field.Store.YES,
					Field.Index.TOKENIZED));
			doc.add(new Field("author", "" + news.getAuthor(), Field.Store.YES,
					Field.Index.TOKENIZED));
			doc.add(new Field("details", ""
					+ Constants.splitAndFilterString(news.getDetails()),
					Field.Store.YES, Field.Index.TOKENIZED,
					Field.TermVector.WITH_POSITIONS_OFFSETS));
			doc.add(new Field("addtime", "" + news.getAddtime(),
					Field.Store.YES, Field.Index.TOKENIZED));
			doc.add(new Field("keywords", "" + news.getKeywords(),
					Field.Store.YES, Field.Index.TOKENIZED));
			System.out.println("Indexing file " + news.getName() + "...");
			articleId = String.valueOf(news.getId());
			try {
				writer.addDocument(doc);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		// 优化并关闭
		writer.optimize();
		writer.close();

		// 将我索引的最后一篇文章的id写入文件
		String content = WriteText(TaskAction.class,
				textName, newsId);
	}	

public boolean isEmpty(Class clazz, String textName) throws Exception {
		String articleId = "0";
		boolean isEmpty = true;
		articleId = ContentReader.readText(clazz, textName);
		if (null == articleId || "".equals(articleId))
			articleId = "0";
		if (!articleId.equals("0"))
			isEmpty = false;
		System.out.println(clazz.getName()+" "+isEmpty);
		return isEmpty;
	}

//该方法参考了paoding中example中的一个方法。
public String readText(Class clazz, String textName)
			throws IOException {
		InputStream in = clazz.getResourceAsStream(textName);
		Reader re = new InputStreamReader(in, "UTF-8");
		char[] chs = new char[1024];
		int count;
		String content = "";
		while ((count = re.read(chs)) != -1) {
			content = content + new String(chs, 0, count);
		}
		return content;
	}

public String WriteText(Class clazz, String textName, String text)
			throws IOException {
		String path = (URLDecoder.decode(
				clazz.getResource(textName).toString(), "UTF-8")).substring(6);
		System.out.println(path);
		File file = new File(path);
		BufferedWriter bw = new BufferedWriter(new FileWriter(file));
		String temp = text;
		bw.write(temp);
		bw.close();
		return temp;
	}


2)进行搜索

Java代码 复制代码
  1. public void searchIndex(String path, String keywords) throws Exception {   
  2.         String[] FIELD = { "title""details" };   
  3.         String QUERY = keywords;   
  4.   
  5.         Analyzer analyzer = new PaodingAnalyzer();   
  6.         FSDirectory directory = FSDirectory.getDirectory(path);   
  7.         IndexReader reader = IndexReader.open(directory);   
  8.         String queryString = QUERY;   
  9.         BooleanClause.Occur[] flags = new BooleanClause.Occur[] {   
  10.                 BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };   
  11.         Query query = MultiFieldQueryParser.parse(queryString, FIELD, flags,   
  12.                 analyzer);   
  13.   
  14.         Searcher searcher = new IndexSearcher(directory);   
  15.         query = query.rewrite(reader);   
  16.         System.out.println("Searching for: " + query.toString());   
  17.         Hits hits = searcher.search(query);   
  18.   
  19.         NewsDTO news = new NewsDTO();   
  20.         String highLightText = "";   
  21.   
  22.         for (int i = 0; i < hits.length(); i++) {   
  23.   
  24.             Document doc = hits.doc(i);   
  25.             String title1 = doc.get("title");   
  26.             String contents1 = doc.get("details");   
  27.   
  28.             SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter(   
  29.                     """");   
  30.   
  31.             Highlighter highlighter = new Highlighter(simpleHTMLFormatter,   
  32.                     new QueryScorer(query));   
  33.             highlighter.setTextFragmenter(new SimpleFragmenter(200));   
  34.   
  35.             if (contents1 != null) {   
  36.                 TokenStream tokenStream = analyzer.tokenStream("details",   
  37.                         new StringReader(contents1));   
  38.                 highLightText = highlighter.getBestFragment(tokenStream,   
  39.                         contents1);   
  40.             }   
  41.             news = new NewsDTO();   
  42.             news.setId(Integer.parseInt(doc.get("id")));   
  43.             news.setName(doc.get("title"));   
  44.             news.setDetails(highLightText);   
  45.             news.setAddtime(doc.get("addtime"));   
  46.             news.setAuthor(doc.get("author"));   
  47.             searchResultItem.add(news);   
  48.         }   
  49.         reader.close();   
  50.   
  51.     }  
public void searchIndex(String path, String keywords) throws Exception {
		String[] FIELD = { "title", "details" };
		String QUERY = keywords;

		Analyzer analyzer = new PaodingAnalyzer();
		FSDirectory directory = FSDirectory.getDirectory(path);
		IndexReader reader = IndexReader.open(directory);
		String queryString = QUERY;
		BooleanClause.Occur[] flags = new BooleanClause.Occur[] {
				BooleanClause.Occur.SHOULD, BooleanClause.Occur.SHOULD };
		Query query = MultiFieldQueryParser.parse(queryString, FIELD, flags,
				analyzer);

		Searcher searcher = new IndexSearcher(directory);
		query = query.rewrite(reader);
		System.out.println("Searching for: " + query.toString());
		Hits hits = searcher.search(query);

		NewsDTO news = new NewsDTO();
		String highLightText = "";

		for (int i = 0; i < hits.length(); i++) {

			Document doc = hits.doc(i);
			String title1 = doc.get("title");
			String contents1 = doc.get("details");

			SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter(
					"", "");

			Highlighter highlighter = new Highlighter(simpleHTMLFormatter,
					new QueryScorer(query));
			highlighter.setTextFragmenter(new SimpleFragmenter(200));

			if (contents1 != null) {
				TokenStream tokenStream = analyzer.tokenStream("details",
						new StringReader(contents1));
				highLightText = highlighter.getBestFragment(tokenStream,
						contents1);
			}
			news = new NewsDTO();
			news.setId(Integer.parseInt(doc.get("id")));
			news.setName(doc.get("title"));
			news.setDetails(highLightText);
			news.setAddtime(doc.get("addtime"));
			news.setAuthor(doc.get("author"));
			searchResultItem.add(news);
		}
		reader.close();

	}

   核心代码已经基本完成了,还有一个加亮显示,非常不错的哦。

3)再来一个定时创建索引:
   定义一下bean

Java代码 复制代码
  1.            
  2. <bean id="myTask" class="edu.cumt.jnotnull.action.TaskAction">   
  3.         <property name="newsManageService">   
  4.             <ref bean="newsManageService" />   
  5.         </property>   
  6.     </bean>   
  7.   
  8.     <bean id="entity"  
  9.         class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">   
  10.         <property name="targetObject">   
  11.             <ref local="myTask" />   
  12.         </property>   
  13.         <property name="targetMethod">   
  14.             <value>createIndex</value>   
  15.         </property>   
  16.     </bean>   
  17.   
  18.     <bean id="cron"  
  19.         class="org.springframework.scheduling.quartz.CronTriggerBean">   
  20.         <property name="jobDetail">   
  21.             <ref bean="entity" />   
  22.         </property>   
  23.         <property name="cronExpression">   
  24.             <value>0 0-5 2 * * ?</value>   
  25.         </property>   
  26.     </bean>   
  27.   
  28.     <bean autowire="no"  
  29.         class="org.springframework.scheduling.quartz.SchedulerFactoryBean">   
  30.         <property name="triggers">   
  31.             <list>   
  32.                 <ref local="cron" />   
  33.             </list>   
  34.         </property>   
  35.     </bean>      

   引用原帖:http://jnotnull.javaeye.com/blog/275327

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值