数据分类
结构化数据和非结构化数据
结构化数据搜索
sql
非结构化数据查询方法
顺序扫描法
全文检索
lucene实现全文检索的流程

创建索引
对文档索引的过程,将用户要搜索的文档内容进行索引,索引存在索引库中,
获取原始文档
创建文档对象,文档中包括一个一个的域(Field),域中存储内容,可以将磁盘上的一个文件当成一个document, Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容)
每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和 域值都相同)
每个文档都有一个唯一的编号,就是文档id。
分析文档
将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文 档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个 一个的单词
创建索引
对所有文档分析得出的语汇单元进行索引,索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到 Document(文档)
创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
举例:通过关键字搜索文件
jar文件
commons-io-2.6.jar
IK-Analyzer-1.0-SNAPSHOT.jar
lucene-analyzers-common-7.4.0.jar
lucene-core-7.4.0.jar
lucene-queryparser-7.4.0.jar
1 public class LuenceFirsrt {
2
3
4 /**
5 * 1创建一个Director对象,指定索引库保存的位置
6 * 2.基于Director对象创建一个IndexWrite对象
7 * 3.读取磁盘上文件,对应每个文件创建一个文档对象
8 * 4.向文档对象中添加域
9 * 5.向文档对象中写入索引库
10 * 6.关闭indexwrite对象
11 */
12 //创建索引库
13 @Test
14 public void createIndex() throws Exception{
15 Directory directory = FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath());
16
17 IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
18
19 File dir = new File("D:\\lucene\\demo\\searchsource");
20 File[] files = dir.listFiles();
21
22 for(File f :files) {
23 String fileName = f.getName();
24 String filePath = f.getPath();
25
26 String fileContent = FileUtils.readFileToString(f, "utf-8");
27 long fileSize = FileUtils.sizeOf(f);
28
29 Field fieldName = new TextField("name",fileName,Field.Store.YES);
30
31 Field fieldPath = new TextField("path",filePath,Field.Store.YES);
32
33 Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
34
35 Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES);
36
37 Document document = new Document();
38 document.add(fieldName);
39 document.add(fieldPath);
40 document.add(fieldContent);
41 document.add(fieldSize);
42
43
44 indexWriter.addDocument(document);
45
46
47 }
48 indexWriter.close();
49 }
50 }
查询索引
1 //查询索引库
2 /**
3 * /**
4 * 1.创建一个Director对象,指定索引库位置
5 * 2.创建一个IndexReader对象
6 * 3.创建一个IndexSearch 对象,构造方法中的参数indexReader对象
7 * 4.创建一个Query对象,TermQuery
8 * 5.执行查询,得到一个TopDocs对象
9 * 6.取查询结果的总记录数
10 * 7.取文档列表
11 * 8.打印文档内容
12 * 9.关闭indexReader对象
13 */
14 @Test
15 public void searchIndex() throws Exception{
16
17
18 Directory directory = FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath());
19
20 IndexReader indexReader = DirectoryReader.open(directory);
21
22 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
23
24 Query query = new TermQuery(new Term("content","spring"));
25
26 TopDocs topDocs = indexSearcher.search(query, 10);
27
28 System.out.println("查询记录总数为"+ topDocs.totalHits);
29
30 ScoreDoc[] scoreDocs = topDocs.scoreDocs;
31
32 for(ScoreDoc doc : scoreDocs) {
33 int docId = doc.doc;
34 Document document = indexSearcher.doc(docId);
35 System.out.println(document.get("name"));
36 System.out.println(document.get("path"));
37 System.out.println(document.get("size"));
38 System.out.println(document.get("content"));
39 System.out.println("----------------------");
40 }
41
42 indexReader.close();
43 }
44
查看分词效果
1 @Test
2 public void testTokenStream() throws Exception {
3 /**
4 * 1.创建一个Analyzer对象,StandardAnalyzer
5 * 2.使用分析器对象的tokenStream方法获得tokenStream对象
6 * 3.向TokenStream对象中设置一个引用,相当于数一个指针
7 * 4.调用TokenStream对象的rest方法
8 * 5.使用while循环遍历ToeknStream对象
9 * 6.关闭TokenStream对象
10 */
11
12 Analyzer analyzer = new StandardAnalyzer();
13 TokenStream tokenStream = analyzer.tokenStream("", "The Spring Framework provides a comprehensive programming and configuration model.");
14 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
15 tokenStream.reset();
16
17 while(tokenStream.incrementToken()) {
18 System.out.println(charTermAttribute.toString());
19 }
20
21 tokenStream.close();
22 }
23
查看中文分词器效果
第一步:把jar包添加到工程中
第二步:把配置文件和扩展词典和停用词词典添加到classpath下
注意:hotword.dic和ext_stopword.dic文件的格式为UTF-8
1 public class Ikanalyzer {
2 @Test
3 public void testCHTokenStream() throws Exception {
4 /**
5 * 1.创建一个Analyzer对象,StandardAnalyzer
6 * 2.使用分析器对象的tokenStream方法获得tokenStream对象
7 * 3.向TokenStream对象中设置一个引用,相当于数一个指针
8 * 4.调用TokenStream对象的rest方法
9 * 5.使用while循环遍历ToeknStream对象
10 * 6.关闭TokenStream对象
11 */
12
13 Analyzer analyzer = new IKAnalyzer();
14 TokenStream tokenStream = analyzer.tokenStream("", "由于日前美方宣称拟对3000亿美元中国输美商品加征10%关税,严重违背中美两国元首大阪会晤共识");
15 CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
16 tokenStream.reset();
17
18 while(tokenStream.incrementToken()) {
19 System.out.println(charTermAttribute.toString());
20 }
21
22 tokenStream.close();
23 }
24
25 }
索引库的维护
是否分析,是否索引,是否存储
|
Field类 |
数据类型 |
Analyzed 是否分析 |
Indexed 是否索引 |
Stored 是否存储 |
说明 |
|
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等) 是否存储在文档中用Store.YES或Store.NO决定 |
|
LongPoint(String name, long... point) |
Long型 |
Y |
Y |
N |
可以使用LongPoint、IntPoint等类型存储数值类型的数据。让数值类型可以进行索引。但是不能存储数据,如果想存储数据还需要使用StoredField。 |
|
StoredField(FieldName, FieldValue) |
重载方法,支持多种类型 |
N |
N |
Y |
这个Field用来构建不同类型Field 不分析,不索引,但要Field存储在文档中 |
|
TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader)
|
字符串 或 流 |
Y |
Y |
Y或N |
如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略. |
添加文档
1 public class IndexManager {
2
3
4 /**
5 * 添加文档
6 *创建一个indexWriter对象,需要使用ik作为分词器
7 *创建一个Document对象
8 *向document对象添加域
9 *把文档写入索引库
10 *关闭索引库
11 */
12 @Test
13 public void addDocument() throws Exception{
14 IndexWriter indexWriter =
15 new IndexWriter(FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath()),
16 new IndexWriterConfig(new IKAnalyzer()));
17
18 Document document = new Document();
19 document.add(new TextField("name","新添加的文件",Field.Store.YES));
20 document.add(new TextField("content","新添加的文件内容",Field.Store.NO));
21 document.add(new StoredField("path","D:\\lucene\\demo\\path"));
22
23
24 indexWriter.addDocument(document);
25 indexWriter.close();
26 }
删除索引
1 public class Delete {
2
3 private IndexWriter indexWriter;
4
5 @Before
6 public void init() throws Exception {
7 indexWriter =
8 new IndexWriter(FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath()),
9 new IndexWriterConfig(new IKAnalyzer()));
10 }
11
12 @Test
13 public void deleteAllDocument() throws Exception {
14 indexWriter.deleteAll();
15 indexWriter.close();
16
17 }
18
19 //根据查询条件删除索引
20 @Test
21 public void deleteDocumentByQuery() throws Exception {
22 indexWriter.deleteDocuments(new Term("name","apache"));
23 indexWriter.close();
24 }
25
26 }
修改索引
先删除后添加
1 /**
2 *1.创建Document对象
3 *2.向Document中添加域
4 *3.不同的Document中有不同的域,同一个document中有相同的域
5 *4.关闭IndexWriter
6 */
7 public class Update {
8
9 private IndexWriter indexWriter;
10
11 @Before
12 public void init() throws Exception {
13 indexWriter =
14 new IndexWriter(FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath()),
15 new IndexWriterConfig(new IKAnalyzer()));
16 }
17
18 @Test
19 public void updateDocument() throws Exception {
20 Document document = new Document();
21
22 document.add(new TextField("name","更新后的文档",Field.Store.YES));;
23
24 indexWriter.updateDocument(new Term("name","spring"),document);
25
26 indexWriter.close();
27
28
29 }
30
31 }
索引库查询
1)使用Lucene提供Query子类
2)使用QueryParse解析查询表达式
Query
public class testRangeQuery{
private IndexReader indexReader;
private IndexSearcher indexSearcher;
@Before
public void init() throws Exception {
indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath()));
indexSearcher = new IndexSearcher(indexReader);
}
@Test
public void testRangleQuery() throws Exception {
Query query = LongPoint.newRangeQuery("size", 0l, 10000l);
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("总记录数:"+topDocs.totalHits);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for(ScoreDoc doc:scoreDocs) {
int docId = doc.doc;
Document document = indexSearcher.doc(docId);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
System.out.println(document.get("content"));
System.out.println("----------------------");
}
indexReader.close();
}
}
QueryParser提供一个Parse方法,此方法可以直接根据查询语法来查询
1 /**
2 *1.创建QueryParser对象,两个参数
3 * 参数一,默认搜索域 。 第二 ,分析器对象
4 *2.使用QueryPaser对象创建一个Query对象
5 *3.执行查询
6 *
7 */
8 public class Paser {
9
10 private IndexReader indexReader;
11
12 private IndexSearcher indexSearcher;
13
14 @Before
15 public void init() throws Exception {
16 indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\lucene\\demo\\index").toPath()));
17 indexSearcher = new IndexSearcher(indexReader);
18 }
19
20 @Test
21 public void testQueryParser() throws Exception {
22
23 //创建queryparser对象
24 //第一个参数默认搜索的域
25 //第二个参数就是分析器对象
26 QueryParser queryParser = new QueryParser("content", new IKAnalyzer());
27 Query query = queryParser.parse("Lucene是java开发的");
28 //执行查询
29 printResult(query, indexSearcher);
30 }
31
32 private void printResult(Query query, IndexSearcher indexSearcher) throws Exception {
33 //执行查询
34 TopDocs topDocs = indexSearcher.search(query, 10);
35 //共查询到的document个数
36 System.out.println("查询结果总数量:" + topDocs.totalHits);
37 //遍历查询结果
38 for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
39 Document document = indexSearcher.doc(scoreDoc.doc);
40 System.out.println(document.get("filename"));
41 System.out.println(document.get("content"));
42 System.out.println(document.get("path"));
43 System.out.println(document.get("size"));
44 }
45 //关闭indexreader
46 indexSearcher.getIndexReader().close();
47 }
48 }
完