Lucene概述
搜索引擎介绍
应用场景:
1. 单机软件的搜索
2. 站内搜索
3. 垂直领域的搜索
4. 专业搜索引擎公司
搜索数据的方式:
1. 顺序扫描法
所谓顺序扫描,例如要找内容包含一个字符串的文件,就是一个文档一个文档的看,对于每一个文档,从头看到尾,如果此文档包含此字符串,则此文档为我们要找的文件,接着看下一个文件,直到扫描完所有的文件。这种方法是顺序扫描方法,数据量大就搜索慢。
2. 倒排索引
倒排索引(也称为倒排文件)是一种存储了来自文本中的映射的索引数据结构。比如单词或者数字,对应到它们在数据库、一个文件或者一组文件中的位置。它是在文档检索系统中使用的最流行的数据结构,在搜索引擎中有大规模使用案例
术语
- 文档(Document):一般搜索引擎处理的对象是互联网网页,对于搜索引擎来讲,Word、PDF、html、XML等不同格式的文件都可以称为文档,一般以文档来表示文本信息。
- 文档集合(Document Collection):由若干文档构成的集合成为文档集合。比如海量的互联网网页等。
- 文档编号(Document ID):在搜索引擎内部,会为文档集合每个文档赋予一个唯一的内部编号,以作为文档的唯一标识,以便于处理。
- 单词编号(Word ID):与文档编号类似,搜索引擎内部以唯一的编号来表示某个单词,以作为某个单词的唯一表示。
- 倒排索引(Inverted Index):倒排索引是实现单词——文档矩阵的一种具体存储形式。通过倒排索引,可以根据单次快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:单词词典和倒排文件。
- 单词词典(Lexicon):搜索引擎通常的索引单位是单词,单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引记载单词本身的一些信息及指向倒排列表的指针。单词也就是我们在搜索时的一些关键字,也称为词条。
- 倒排列表(PostingList):倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文当中出现的位置信息,每条记录成为一个倒排向(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。
倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即为倒排文件,倒排文件是存储倒排索引的物理文件。
Lucune介绍
Lucene是apache下的一个开放源代码的全文检索引擎工具包。提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能。
- Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支 持和提供
- Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻, 在Java开发环境里Lucene是一个成熟的免费开放源代码工具
- Lucene并不是现成的搜索引擎产品,但可以用来制作搜索引擎产品
搜索数据库:
1. like不走mysql索引查询
2. 全表扫描,效率极低.
3. 数据库压力大
索引来源:
1. 搜索文档,读完文档,把文档数据变成文档对象,解析成单词,存储索引库,搜索索引库数据,搜索到文档,解析文档,设置索引
2. 查询数据库,把数据库数据变成文档对象,解析,把索引放入索引库
3. 爬虫爬取网页,解析网页,把网页数据变成文档对象,把索引希尔索引库
- 索引库索引写入流程
- tike工具读取文档,查询数据库数据,爬虫爬取网页数据
- Lucene API解析器,分词器,把数据文本内容解析,分词,变成一个一个索引单词
- 把索引单词写入索引库
一个文档有几个域,一个域有多个单词,搜索的时候,根据单词来进行搜索文档.
Lucene与搜索引擎的区别
全文检索系统是按照全文检索理论建立起来的用于提供全文检索服务的软件系统,包括建立索引、处理查询返回结果集、增加索引、优化索引结构等功能。
Lucene和搜索引擎不同,Lucene是一套用java或其它语言写的全文检索的工具包,为应用程序提供了很多个api接口去调用,可以简单理解为是一套实现全文检索的类库,搜索引擎是一个全文检索系统,它是一个单独运行的软件系统。
API核心
Document
Lucene创建索引时的原始文档。
IndexableField
- LongField:long类型,会切词
- StringField:string类型,通过StringField构建的字段不会被切词(分词)。
- TextField:Text类型,会切词。
- new XXXField(name:字段的名称,value:字段的值,store:是否存储到索引库中)
Directory
用来指定索引库存放的位置。
- Directory,指的是文件磁盘的索引路径
- RAMDirectory,指的是内存中的索引路径
Analyzer
在对Docuemnt中的内容进行索引之前,需要使用分词器进行分词 ,分词的目的是为了搜索。分词的主要过程就是先分词后过滤。
- 分词:采集到的数据会存储到document对象的Field域中,分词就是将Document中Field的value值切分成一个一个的词。
过滤:包括去除标点符号过滤、去除停用词过滤(的、是、a、an、the等)、大写转小写、词的形还原(复数形式转成单数形参、过去式转成现在式)等。
停用词是为节省存储空间和提高搜索效率,搜索引擎在索引页面或处理搜索请求时会自动忽略某些字或词,这些字或词即被称为Stop Words(停用词)。比如语气助词、副词、介词、连接词等,通常自身并无明确的意义,只有将其放入一个完整的句子中才有一定作用,如常见的“的”、“在”、“是”、“啊”等。
- 搜索使用的分析器要和索引使用的分析器一致
中文分词器
- StandardAnalyzer:
单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,
效果:“我”、“爱”、“中”、“国”。 - CJKAnalyzer
二分法分词:按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”“国人”。
QueryParser(查询解析)
使用场景:需要对查询关键词进行分词时候,使用查询解析器.
MultiFieldQueryParser
作用:指定多个字段检索并分词
TermQuery
作用:根据词条进行检索,不会切词了,因为词条已是最小单位。
WildcardQuery
作用:模糊检索
FuzzyQuery
作用:相似度检索
FuzzyQuery(Term term):默认支持模糊字数为2;
FuzzyQuery(Term term, int maxEdits):maxEdits:模糊字数,[0,2]之间,若为0,相当于TermQuery。
FuzzyQuery(Term term, int maxEdits, int prefixLength):prefixLength,指定要有多个前缀字母必须完全匹配。
NumericRangeQuery
作用:数字范围搜索(演示:略),最后两个参数的含义是:minInclusive,是否最小包含,maxInclusive,是否最大包含。
MatchAllDocsQuery
作用:查询所有
BooleanQuery
作用:组合查询
1、MUST和MUST表示“与”的关系,即“交集”,相当与AND。
2、MUST和MUST_NOT前者包含后者不包含。
3、MUST_NOT和MUST_NOT没意义
4、SHOULD与MUST表示MUST,SHOULD失去意义;
5、SHOULD与MUST_NOT相当于MUST与MUST_NOT。
6、SHOULD与SHOULD表示“或”的关系,相当与OR。
索引维护
添加索引
调用 indexWriter.addDocument(doc)添加索引。
@Test
public void testIndexDelete() throws Exception {
// 创建Directory流对象
Directory directory = FSDirectory.open(new File("f:/lucene/index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, null);
// 创建写入对象
IndexWriter indexWriter = new IndexWriter(directory, config);
// 根据Term删除索引库,name:java
indexWriter.deleteDocuments(new Term("name", "java"));
// 释放资源
indexWriter.close();
}
删除索引
@Test
public void testIndexDelete() throws Exception {
// 创建Directory流对象
Directory directory = FSDirectory.open(new File("f:/lucene/index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, null);
// 创建写入对象
IndexWriter indexWriter = new IndexWriter(directory, config);
// 根据Term删除索引库,name:java
indexWriter.deleteDocuments(new Term("name", "java"));
// 释放资源
indexWriter.close();
}
删除全部索引:
@Test
public void testIndexDelete() throws Exception {
// 创建Directory流对象
Directory directory = FSDirectory.open(new File("D:/lucene/index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, null);
// 创建写入对象
IndexWriter indexWriter = new IndexWriter(directory, config);
// 根据Term删除索引库,name:java
// indexWriter.deleteDocuments(new Term("name", "java"));
// 全部删除
indexWriter.deleteAll();
// 释放资源
indexWriter.close();
}
修改索引
@Test
public void testIndexUpdate() throws Exception {
// 创建分词器
Analyzer analyzer = new IKAnalyzer();
// 创建Directory流对象
Directory directory = FSDirectory.open(new File("f:/lucene/index"));
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 创建写入对象
IndexWriter indexWriter = new IndexWriter(directory, config);
// 创建Document
Document document = new Document();
document.add(new TextField("id", "1002", Store.YES));
document.add(new TextField("title", "lucene测试test 002", Store.YES));
// 执行更新,会把所有符合条件的Document删除,再新增。
indexWriter.updateDocument(new Term("title", "test"), document);
// 释放资源
indexWriter.close();
}
Lucene对结果排名
改变boost值来改变文档得分
boost,激励因子,默认值是1,可以手动更改。我们可以设置boost值来改变搜索结果排名。而且设置boost值后,该信息保存在Document文档的norm中。
/**
* 给30个文档其中一个文档设置得分
* @throws Exception
*/
@Test
public void addIndexByNumbericRangeQuery() throws Exception{
//指定索引库位置
String indexPath = "f:/index";
//Lucene管理索引库存储空间
FSDirectory directory = FSDirectory.open(new File(indexPath));
IKAnalyzer analyzer = new IKAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST,analyzer);
//创建写索引库核心对象
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
//创建30个文本,把30个文档写入索引库
for(int i=0;i<30;i++){
//创建文档对象
Document document = new Document();
//设置文档域字段
document.add(new LongField("id", i, Store.YES));
TextField textField = new TextField("title", "solr经典教程",Store.YES);
//设置得分
if (i == 23) {
textField.setBoost(100);
}
document.add(textField);
//内容域
document.add(new TextField("content", "solr底层封装lucene,solr使用非常简单",Store.NO));
//写入索引库
indexWriter.addDocument(document);
}
//提交
indexWriter.commit();
//关闭资源
indexWriter.close();
}
创建索引
/**
* 创建索引
*/
public class CreateIndex {
/**
* 创建索引
* @throws IOException
*/
@Test
public void testCreateIndex() throws IOException{
//1.创建document对象
Document document = new Document();
//2document文档中添加field
document.add(new StringField("id", "1", Store.NO));
document.add(new TextField("title","Lucene经典教程",Store.YES));
document.add(new TextField("content", "Lucene的入门程序,这个一个测试新建索引的程序",Store.YES));
//3.创建建立索引的对象
//指定创建索引的位置
Directory directory = FSDirectory.open(new File("f:/index"));
//分词器,将文档内容切词
Analyzer analyzer = new StandardAnalyzer();
//创建索引的配置信息
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//将文档添加到索引库
indexWriter.addDocument(document);
//关闭资源
indexWriter.close();
}
/**
* 创建索引库
*/
@Test
public void addIndex() throws Exception{
//指定索引库位置
String indexPath = "f:/index";
//Lucene管理索引库存储空间
FSDirectory directory = FSDirectory.open(new File(indexPath));
//创建索引库写入对象核心配置对象:
//创建基本分词器
//a.基本分词器
// Analyzer analyzer = new StandardAnalyzer();
//b.cjk分词器 二元分词
//缺点:两两组合,不管是否是词语
// CJKAnalyzer analyzer = new CJKAnalyzer();
//c.smartChineseAnalyzer 聪明的中国人分词器
//d.IK分词器:支持扩展词汇,支持提用词汇
IKAnalyzer analyzer = new IKAnalyzer();
//参数1:指定使用Lucene版本
//参数2:指定创建索引库使用分词器
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
//创建索引库,写入索引库核心对象
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//创建文档对象
//场景1:搜索word文档,读取word文档,把word文档数据变成文档对象,解析成单词,存储索引库,搜索索引库数据,搜索到文档
//场景2:查询数据库,把数据库数据变成文档对象,解析,把索引放入索引库
//场景3:爬虫爬取网页,解析网页,把网页数据变成文档对象,把索引写入索引库
Document document = new Document();
//添加模拟数据
//StringField:不分词,索引,Store.NO:不存储,Store.YES:存储
//参数1:域名称
//参数2:域对应值
//参数3:域值是否存储
document.add(new StringField("id", "101", Store.NO));
//TextField:分词,索引
document.add(new TextField("title", "测试lucene学习经典",Store.YES));
//TextField:分词,索引
document.add(new TextField("content", "Lucene是apache下的一个开放源代码的全文检索引擎工具包。"
+ "提供了完整的查询引擎和索引引擎,部分文本分析引擎",Store.YES));
//写入索引库
indexWriter.addDocument(document);
//提交
indexWriter.commit();
//关闭资源
indexWriter.close();
}
/**
* 给30个文档其中一个文档设置得分
* @throws Exception
*/
@Test
public void addIndexByNumbericRangeQuery() throws Exception{
//指定索引库位置
String indexPath = "f:/index";
//Lucene管理索引库存储空间
FSDirectory directory = FSDirectory.open(new File(indexPath));
IKAnalyzer analyzer = new IKAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST,analyzer);
//创建写索引库核心对象
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
//创建30个文本,把30个文档写入索引库
for(int i=0;i<30;i++){
//创建文档对象
Document document = new Document();
//设置文档域字段
document.add(new LongField("id", i, Store.YES));
TextField textField = new TextField("title", "solr经典教程",Store.YES);
//设置得分
if (i == 23) {
textField.setBoost(100);
}
document.add(textField);
//内容域
document.add(new TextField("content", "solr底层封装lucene,solr使用非常简单",Store.NO));
//写入索引库
indexWriter.addDocument(document);
}
//提交
indexWriter.commit();
//关闭资源
indexWriter.close();
}
/**
* 索引库的修改
* 如果id存在,更新
* 如果id不存在,添加
* 根据域字段,进行更新,先删除,再更新
* @throws Exception
*/
@Test
public void testUpdateDoc() throws Exception{
//指定索引库位置
String indexPath = "f:/index";
//Lucene管理索引库存储空间
FSDirectory directory = FSDirectory.open(new File(indexPath));
IKAnalyzer analyzer = new IKAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST,analyzer);
//创建写索引库核心对象
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
//创建文档对象
Document doc = new Document();
doc.add(new LongField("id", 1, Store.YES));
doc.add(new TextField("title", "张三在学习solr", Store.YES));
doc.add(new TextField("content", "solr底层封装lucene,solr使用非常简单",Store.NO));
//修改索引
indexWriter.updateDocument(new Term("title","solr"), doc);
//提交
indexWriter.commit();
//关闭资源
indexWriter.close();
}
/**
* 删除索引库索引
*/
@Test
public void testDeleteDoc() throws Exception{
//指定索引库位置
String indexPath = "f:/index";
//Lucene管理索引库存储空间
FSDirectory directory = FSDirectory.open(new File(indexPath));
IKAnalyzer analyzer = new IKAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST,analyzer);
//创建写索引库核心对象
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
//第一种方式
// indexWriter.deleteDocuments(new TermQuery(new Term("title","solr")));
//第二种方式
indexWriter.deleteDocuments(new Term("title","solr"));
//提交
indexWriter.commit();
//关闭资源
indexWriter.close();
}
}
查询索引
/**
* 搜索索引
*/
public class SearchIndex {
/**
* 查询索引库
* 流程:
* 1.根据查询关键字查询索引库
* 2.根据定位索引,找到文档对象
* 3.返回文档对象
* @throws ParseException
* @throws IOException
*/
@Test
public void testSearchIndex() throws ParseException, IOException {
//1.将检索内容转成query对象
String test = "入门";
Analyzer analyzer = new StandardAnalyzer();
// 指定对哪个字段检索并且指定使用哪个分词器
QueryParser queryParser = new QueryParser("content",analyzer);
Query query = queryParser.parse(test);
//2.创建检索的对象,需要指定从哪里检索
FSDirectory directory = FSDirectory.open(new File("f:/index"));
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//3.通过search方法检索
TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE);
System.out.println("总条数:"+topDocs.totalHits);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//4.遍历检索结果
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println("得分:"+scoreDoc.score);
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.println("id:"+doc.get("id"));
System.out.println("title:"+doc.get("title"));
System.out.println("content:"+doc.get("content"));
}
}
/**
* 查询索引库
* 流程:
* 1.根据查询关键字查询索引库
* 2.根据定位索引,找到文档对象
* 3.返回文档对象
* @throws ParseException
* @throws IOException
*/
@Test
public void queryIndex() throws Exception{
//指定索引库存储位置
String indexPath = "f:/index";
//读取索引库索引
DirectoryReader directoryReader = DirectoryReader.open(FSDirectory.open(new File(indexPath)));
//创建查询索引库核心对象
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
//指定查询关键词
String qName = "solr";
//创建查询解析器,解析查询关键字
//参数1:指定查询域字段
//参数2:使用何种分词器
//搜索时候,两边应该使用相同分词算法,创建索引库使用IK分词器,搜索时候必须使用IK分词器切分词语
QueryParser qp = new QueryParser("title",new IKAnalyzer());
// QueryParser queryParser = new QueryParser("title", new StandardAnalyzer());
//解析关键词进行分词
Query query = qp.parse(qName);
//使用indexSearcher查询
//返回文档概要信息,TopDocs:文档总记录数,文档id,文档得分
//返回得分最高的10条记录:匹配度越高,得分越高
TopDocs topDocs = indexSearcher.search(query, 10);
//获取查询文档总记录数
int totalHits = topDocs.totalHits;
System.out.println("总记录数:"+totalHits);
//获取文档id,文档得分数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//循环文档id,文档得分数组,获取单个文档id,根据文档id,获取文档对象
for (ScoreDoc scoreDoc : scoreDocs) {
//获取文档id
int docId = scoreDoc.doc;
System.out.println("文档id:"+docId);
//获取文档得分
float score = scoreDoc.score;
System.out.println("得分:"+score);
//使用搜索核心对象,搜索文档:根据文档id唯一定义一个文档
Document doc = indexSearcher.doc(docId);
//获取文档数据
//获取文档域id
String id = doc.get("id");
System.out.println("文档域id"+id);
//获取文档标题
String title = doc.get("title");
System.out.println("文档标题:"+title);
//获取文档内容
String content = doc.get("content");
System.out.println("文档内容:"+content);
}
}
/**
* 两种queryParser(查询解析)
* 使用场景:需要对关键词进行分词
* 查询关键词不是最小分词单位
* MultiFieldQueryParse:同时查询多个域
* @throws Exception
*/
@Test
public void queryIndexMultiField() throws Exception{
//指定索引库存储位置
String indexPath = "f:/index";
//读取索引库索引
DirectoryReader directoryReader = DirectoryReader.open(FSDirectory.open(new File(indexPath)));
//创建查询索引库核心对象
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
//指定查询关键词
String qName = "一个开放源代码";
//创建查询解析器,使用IK分词器进行分词
MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[]{"title","content"}, new IKAnalyzer());
//解析查询关键词
Query query = parser.parse(qName);
//返回得分最高的10条记录:匹配度越高,得分越高
TopDocs topDocs = indexSearcher.search(query, 10);
//获取查询文档总记录数
int totalHits = topDocs.totalHits;
System.out.println("总记录数:"+totalHits);
//获取文档id,文档得分数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//循环文档id,文档得分数组,获取单个文档id,根据文档id,获取文档对象
for (ScoreDoc scoreDoc : scoreDocs) {
//获取文档id
int docId = scoreDoc.doc;
System.out.println("文档id:"+docId);
//获取文档得分
float score = scoreDoc.score;
System.out.println(score);
//使用搜索核心对象,搜索文档:根据文档id唯一定义一个文档
Document doc = indexSearcher.doc(docId);
//获取文档数据
//获取文档域id
String id = doc.get("id");
System.out.println("文档域id"+id);
//获取文档标题
String title = doc.get("title");
System.out.println("文档标题:"+title);
//获取文档内容
String content = doc.get("content");
System.out.println("文档内容:"+content);
}
}
/**
* 6种termQuery 查询关键词已是最小分词单元
* @throws Exception
*/
@Test
public void queryIndexByTermQuery() throws Exception{
//指定索引库存储位置
String indexPath = "f:/index";
//读取索引库索引
DirectoryReader directoryReader = DirectoryReader.open(FSDirectory.open(new File(indexPath)));
//创建查询索引库核心对象
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
//指定查询关键词
String qName = "solr";
//a.使用termQuery查询
// TermQuery query = new TermQuery(new Term("title",qName));
//b.wildcardQuery 模糊查询 ? * *qName*
//c.fuzzyQuery相似度查询
//相似度查询规则:最多经过2次变化,能变回原单词,长得像
//FuzzyQuery query = new FuzzyQuery(new Term("content", qName));
//d.范围查询
//参数1:指定查询域字段名称,newLongRange只能根据long类型字段进行查询
//参数2:指定范围查询起始位置角标
//参数3:指定范围查询结束位置角标
//参数4:左边true:闭区间,false:开区间
//参数5:右边true:闭区间,false:开区间
// NumericRangeQuery query = NumericRangeQuery.newLongRange("id",5L, 10L, true, true);
//e.组合查询 BooleanQuery,求query1和query2的交集
BooleanQuery query = new BooleanQuery();
//创建查询所有的查询对象
MatchAllDocsQuery query1 = new MatchAllDocsQuery();
//创建范围查询
NumericRangeQuery query2 = NumericRangeQuery.newLongRange("id",5L, 10L, true, true);
//设置组合查询
query.add(query1, Occur.MUST);
query.add(query2,Occur.MUST_NOT);//补集
//返回得分最高的10条记录:匹配度越高,得分越高
TopDocs topDocs = indexSearcher.search(query, 10);
//获取查询文档总记录数
int totalHits = topDocs.totalHits;
System.out.println("总记录数:"+totalHits);
//获取文档id,文档得分数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//循环文档id,文档得分数组,获取单个文档id,根据文档id,获取文档对象
for (ScoreDoc scoreDoc : scoreDocs) {
//获取文档id
int docId = scoreDoc.doc;
System.out.println("文档id:"+docId);
//获取文档得分
float score = scoreDoc.score;
System.out.println(score);
//使用搜索核心对象,搜索文档:根据文档id唯一定义一个文档
Document doc = indexSearcher.doc(docId);
//获取文档数据
//获取文档域id
String id = doc.get("id");
System.out.println("文档域id"+id);
//获取文档标题
String title = doc.get("title");
System.out.println("文档标题:"+title);
//获取文档内容
String content = doc.get("content");
System.out.println("文档内容:"+content);
}
}
}