徐州企业建站,iis一个文件夹配置多个网站,淘客做网站多少钱,营销型网站建设网络推广lucene结构 索引#xff1a;概念上的一个表#xff0c;现实体现就是一个文件目录#xff0c;一个目录代表一个索引#xff0c;也视作documents文档集合 文档#xff1a;document#xff0c;为索引中的一条数据#xff0c;一个document可以拥有多个filed#xff08;域概念上的一个表现实体现就是一个文件目录一个目录代表一个索引也视作documents文档集合 文档document为索引中的一条数据一个document可以拥有多个filed域甚至可以完全相同。 域field一个field即为一个字段 是否分词tokenized 是否索引indexed 是否存储stored 倒排索引 所谓倒排索引 就是从文档内容通过分词器分出不重复关键字以关键字作为主键查询到具有关键字的文档id列表查询到出具有关键词的文档。因此倒排索引是用于模糊查询大量数据得到列表结果的而非精确查询。
常用Field类型 类型 是否分词 是否索引 是否存储 StringField N Y Y或N TextField Y Y Y或N FloatPoint Y Y N DoublePoint LongPoint IntPoint StoredField N N Y NumericDocValuesField 配合其他域使用
分词器 分词 采集到的数据会存储道document对象的Field域中分词就是将Document中的Field的value值切分成一个一个的词。分词就是为了查询不作为查询条件的不要分词。
过滤标点符号去除停用词过滤大写转小写词的形还原 Analyzer使用时机索引时使用/搜索时使用两者所用分词器必须一致
扩展词典 放专有名词或者我们认为需要强制将某一些词拆成一个关键字 凡是出现在扩展词典的词就会强制分成一个关键字
停用词典 凡是出现在停用词点的词都会被过滤掉节省存储空间提高搜索效率
存储结构 一个目录一个索引一个逻辑索引由多个段segment组成多个段可以合并减少读取内存时候的IO。Lucene数据先写入buffer缓冲区到达一定数量后写入segment段segment内部的数据不可以被修改但可以被删除删除为逻辑删除。新增家的document单独在一个新生的段中随着段的合并写入到一个段中。
词典数据结构 跳跃表 占用内存小且可调但是对模糊查询支持不好。3.0之前使用
排序列表Array/List 使用二分法查找不平衡字典树 查询效率跟字符串长度有关但只适合英文词典 哈希表 性能高内存消耗大几乎是原始数据的三倍 双数组字典树 适合做中文词典内存占用小很多分词工具均采用此种算法 Finite State Transducers (FST) 一种有限状态转移机Lucene 4有开源实现并大量使用 B树 磁盘索引更新方便但检索速度慢多用于数据库
lucene优化 解决大量磁盘IO
indexWriterConfig.setMaxBufferedDocs(10000); // 配置写入segment前缓冲区doc最大数目
indexWriter.forceMerge(1000); // 多少个文档合并一个段 越大索引越快 搜索越慢
TermQuery更快 QueryParser更强大
相关度排序 计算出词的权重 Termtf – 此文档中词出现次数越高越大 df — 越多文档包含得分越低 根据词的权重值计算文档相关度得分 boost加权默认加权值为1.0f 在索引时对某个文档中的filed设置加权重搜索匹配到时这个文档就可能排在前面 在搜索时对某个域进行加权在进行组合域查询时匹配到加权值高的域最后计算的相关度得分就高。 设置boost是给域field或者Document设置的。
注意事项 关键词区分大小写 OR AND TO等关键词是区分大小写的lucene只认大写的小写的当做普通单词。 读写互斥性 同一时刻只能有一个对索引的写操作在写的同时可以进行搜索 文件锁在写索引的过程中强行退出将在tmp目录留下一个lock文件使以后的写操作无法进行可以将其手工删除时间格式 lucene只支持一种时间格式yyMMddHHmmss所以你传一个yy-MM-dd HH:mm:ss的时间给lucene它是不会当作时间来处理的 设置boost 有些时候在搜索时某个字段的权重需要大一些例如你可能认为标题中出现关键词的文章比正文中出现关键词的文章更有价值你可以把标题的boost设置的更大那么搜索结果会优先显示标题中出现关键词的文章
代码演示
/*** 新增文档/索引* throws IOException*/
Test
public void createIndexTest() throws IOException {// 1.采集数据// 2.创建文档对象Document document new Document();document.add(new TextField(id, 12345, Field.Store.YES));document.add(new TextField(name, 第二份文档数据, Field.Store.YES));document.add(new TextField(time, 2023-01-27, Field.Store.YES));// 3.创建分词器 标准分词器对英文分词效果好 对中文是单字分词StandardAnalyzer standardAnalyzer new StandardAnalyzer();IKAnalyzer smartChineseAnalyzer new IKAnalyzer();// 4.创建Directory目录对象 目录对象表示索引库的位置Directory fsDirectory FSDirectory.open(Paths.get(/opt/lucene_dir));// 5.创建IndexWriterConfig对象 这个对象中指定且分次使用的分词器IndexWriterConfig indexWriterConfig new IndexWriterConfig(smartChineseAnalyzer);indexWriterConfig.setMaxBufferedDocs(10000); // 配置写入segment前缓冲区doc最大数目// 6.创建IndexWriter输出流对象 指定输出的位置和使用的config初始化对象IndexWriter indexWriter new IndexWriter(fsDirectory, indexWriterConfig);indexWriter.forceMerge(1000); // 多少个文档合并一个段 越大索引越快 搜索越慢// 7.写入文档到索引库indexWriter.addDocument(document);// 8.释放资源indexWriter.close();
}/*** 搜索*/
Test
public void testSearch() throws ParseException, IOException {// 1. 创建分词器(对搜索的关键词进行分词使用)// 注意: 分词器需要跟创建索引时的分词器一模一样IKAnalyzer analyzer new IKAnalyzer();// 2. 创建查询对象, 第一个参数: *默认*查询域, 第二个参数, 使用的分词器QueryParser parser new QueryParser(id, analyzer);// 3. 设置搜索关键词 可指定查询域 如id:123 不指定时用默认查询域Query query parser.parse(123456);// 4. 创建Directory目录, 指定索引库的位置Directory directory FSDirectory.open(Paths.get(D:\\dir));// 5. 创建输入流对象IndexReader indexReader DirectoryReader.open(directory);// 6. 创建搜索对象IndexSearcher indexSearcher new IndexSearcher(indexReader);// 7. 搜索, 并返回结果TopDocs docs indexSearcher.search(query, 10);System.out.println(查询到数据: docs.totalHits);// 8. 获取结果集ScoreDoc[] scoreDocs docs.scoreDocs;// 9. 遍历结果集if (scoreDocs ! null) {for (ScoreDoc scoreDoc : scoreDocs) {// 获取查询到的文档id 该id为lucene创建文档时自动分配的idint docId scoreDoc.doc;// 通过文档id读取文档Document doc indexSearcher.doc(docId);System.out.println(doc.get(name));System.out.println(doc.get(time));System.out.println(doc.get(id));}}
}/*** 索引库修改*/
Test
public void updateIndexTest() throws IOException {// 需要变更成的内容Document document new Document();document.add(new TextField(id, 123456,Field.Store.YES));document.add(new TextField(name, 修改后的名称, Field.Store.YES));// 创建分词器IKAnalyzer analyzer new IKAnalyzer();// 创建目录对象FSDirectory directory FSDirectory.open(Paths.get(D:\\dir));// 创建配置对象IndexWriterConfig config new IndexWriterConfig(analyzer);// 创建输出流对象IndexWriter indexWriter new IndexWriter(directory, config);// 修改, 第一个参数: 修改条件,第二个参数: 修改成的内容indexWriter.updateDocument(new Term(id, 123456), document);// 关闭资源indexWriter.close();
}/*** 索引库删除*/
Test
public void deleteTest() throws IOException {// 创建分词器IKAnalyzer analyzer new IKAnalyzer();// 创建目录对象FSDirectory directory FSDirectory.open(Paths.get(D:\\dir));// 创建配置对象IndexWriterConfig config new IndexWriterConfig(analyzer);// 创建输出流对象IndexWriter indexWriter new IndexWriter(directory, config);// 修改, 第一个参数: 修改条件,第二个参数: 修改成的内容indexWriter.deleteDocuments(new Term(id, 123456));// 关闭资源indexWriter.close();
}/*** 使用第三方分词器IK分词*/
Test
public void testIkAnalyzer() throws IOException {// 创建分词器Analyzer analyzer new IKAnalyzer();// 创建Directory对象 声明索引库的位置Directory directory FSDirectory.open(Paths.get(/opt/lucene_dir));// 创建indexWriterConfigIndexWriterConfig indexWriterConfig new IndexWriterConfig(analyzer);// 6.创建IndexWriter输出流对象 指定输出的位置和使用的config初始化对象IndexWriter indexWriter new IndexWriter(directory, indexWriterConfig);Document document new Document();document.add(new TextField(id, 12346, Field.Store.YES));document.add(new TextField(name, 华为手机4G至尊P30, Field.Store.YES));document.add(new IntPoint(price, 9999));document.add(new StoredField(price, 9999));// 7.写入文档到索引库indexWriter.addDocument(document);// 8.释放资源indexWriter.close();
}/*** 高级查询文本搜索*/
Test
public void testTextSearch() throws IOException, ParseException {// 1. 创建分词器(对搜索的关键词进行分词使用)// 注意: 分词器需要跟创建索引时的分词器一模一样IKAnalyzer analyzer new IKAnalyzer();// 2. 创建查询对象, 第一个参数: *默认*查询域, 第二个参数, 使用的分词器// QueryParser parser new QueryParser(name, analyzer);// 3. 设置搜索关键词 可指定查询域 如id:123 不指定时用默认查询域// AND 取交集 OR 取并集// QueryParser只能对文本进行搜索String[] fields {id, name, brand};MapString, Float boost new HashMap();// 设置权重boost.put(brand, 10000f);MultiFieldQueryParser parser new MultiFieldQueryParser(fields, analyzer, boost);Query query parser.parse(最新 or 华为);// 4. 创建Directory目录, 指定索引库的位置Directory directory FSDirectory.open(Paths.get(/opt/lucene_dir));// 5. 创建输入流对象IndexReader indexReader DirectoryReader.open(directory);// 6. 创建搜索对象IndexSearcher indexSearcher new IndexSearcher(indexReader);// 7. 搜索, 并返回结果TopDocs docs indexSearcher.search(query, 10);System.out.println(查询到数据: docs.totalHits);// 8. 获取结果集ScoreDoc[] scoreDocs docs.scoreDocs;// 9. 遍历结果集if (scoreDocs ! null) {for (ScoreDoc scoreDoc : scoreDocs) {// 获取查询到的文档id 该id为lucene创建文档时自动分配的idint docId scoreDoc.doc;// 通过文档id读取文档Document doc indexSearcher.doc(docId);System.out.println(doc.get(name));System.out.println(doc.get(time));System.out.println(doc.get(id));}}
}/*** 高级查询范围搜索*/
Test
public void testRangeSearch() throws IOException, ParseException {// 1. 创建分词器(对搜索的关键词进行分词使用)// 注意: 分词器需要跟创建索引时的分词器一模一样IKAnalyzer analyzer new IKAnalyzer();// 2. 创建查询对象, 第一个参数: *默认*查询域, 第二个参数, 使用的分词器Query query IntPoint.newRangeQuery(price, 100, 9998);// 4. 创建Directory目录, 指定索引库的位置Directory directory FSDirectory.open(Paths.get(/opt/lucene_dir));// 5. 创建输入流对象IndexReader indexReader DirectoryReader.open(directory);// 6. 创建搜索对象IndexSearcher indexSearcher new IndexSearcher(indexReader);// 7. 搜索, 并返回结果TopDocs docs indexSearcher.search(query, 10);System.out.println(查询到数据: docs.totalHits);// 8. 获取结果集ScoreDoc[] scoreDocs docs.scoreDocs;// 9. 遍历结果集if (scoreDocs ! null) {for (ScoreDoc scoreDoc : scoreDocs) {// 获取查询到的文档id 该id为lucene创建文档时自动分配的idint docId scoreDoc.doc;// 通过文档id读取文档Document doc indexSearcher.doc(docId);System.out.println(doc.get(name));System.out.println(doc.get(time));System.out.println(doc.get(id));System.out.println(doc.get(price));}}
}/*** 高级查询组合搜索*/
Test
public void testComplexSearch() throws IOException, ParseException {// 1. 创建分词器(对搜索的关键词进行分词使用)// 注意: 分词器需要跟创建索引时的分词器一模一样IKAnalyzer analyzer new IKAnalyzer();// 2. 创建查询对象, 第一个参数: *默认*查询域, 第二个参数, 使用的分词器Query query1 IntPoint.newRangeQuery(price, 100, 9998);QueryParser parse new QueryParser(name, analyzer);Query query2 parse.parse(华为);// 3. 组合查询条件 MUST 必须 SHOULD 或 MUST_NOT 必须不// 注意如果查询条件都是must not 则查询不到结果BooleanQuery.Builder builder new BooleanQuery.Builder();builder.add(query1, BooleanClause.Occur.SHOULD);builder.add(query2, BooleanClause.Occur.SHOULD);BooleanQuery query builder.build();// 4. 创建Directory目录, 指定索引库的位置Directory directory FSDirectory.open(Paths.get(/opt/lucene_dir));// 5. 创建输入流对象IndexReader indexReader DirectoryReader.open(directory);// 6. 创建搜索对象IndexSearcher indexSearcher new IndexSearcher(indexReader);// 7. 搜索, 并返回结果TopDocs docs indexSearcher.search(query, 10);System.out.println(查询到数据: docs.totalHits);// 8. 获取结果集ScoreDoc[] scoreDocs docs.scoreDocs;// 9. 遍历结果集if (scoreDocs ! null) {for (ScoreDoc scoreDoc : scoreDocs) {// 获取查询到的文档id 该id为lucene创建文档时自动分配的idint docId scoreDoc.doc;// 通过文档id读取文档Document doc indexSearcher.doc(docId);System.out.println(doc.get(name));System.out.println(doc.get(time));System.out.println(doc.get(id));System.out.println(doc.get(price));}}
}