- 浏览: 2638784 次
- 来自: 杭州
文章分类
- 全部博客 (1188)
- webwork (4)
- 网摘 (18)
- java (103)
- hibernate (1)
- Linux (85)
- 职业发展 (1)
- activeMQ (2)
- netty (14)
- svn (1)
- webx3 (12)
- mysql (81)
- css (1)
- HTML (6)
- apache (3)
- 测试 (2)
- javascript (1)
- 储存 (1)
- jvm (5)
- code (13)
- 多线程 (12)
- Spring (18)
- webxs (2)
- python (119)
- duitang (0)
- mongo (3)
- nosql (4)
- tomcat (4)
- memcached (20)
- 算法 (28)
- django (28)
- shell (1)
- 工作总结 (5)
- solr (42)
- beansdb (6)
- nginx (3)
- 性能 (30)
- 数据推荐 (1)
- maven (8)
- tonado (1)
- uwsgi (5)
- hessian (4)
- ibatis (3)
- Security (2)
- HTPP (1)
- gevent (6)
- 读书笔记 (1)
- Maxent (2)
- mogo (0)
- thread (3)
- 架构 (5)
- NIO (5)
- 正则 (1)
- lucene (5)
- feed (4)
- redis (17)
- TCP (6)
- test (0)
- python,code (1)
- PIL (3)
- guava (2)
- jython (4)
- httpclient (2)
- cache (3)
- signal (1)
- dubbo (7)
- HTTP (4)
- json (3)
- java socket (1)
- io (2)
- socket (22)
- hash (2)
- Cassandra (1)
- 分布式文件系统 (5)
- Dynamo (2)
- gc (8)
- scp (1)
- rsync (1)
- mecached (0)
- mongoDB (29)
- Thrift (1)
- scribe (2)
- 服务化 (3)
- 问题 (83)
- mat (1)
- classloader (2)
- javaBean (1)
- 文档集合 (27)
- 消息队列 (3)
- nginx,文档集合 (1)
- dboss (12)
- libevent (1)
- 读书 (0)
- 数学 (3)
- 流程 (0)
- HBase (34)
- 自动化测试 (1)
- ubuntu (2)
- 并发 (1)
- sping (1)
- 图形 (1)
- freemarker (1)
- jdbc (3)
- dbcp (0)
- sharding (1)
- 性能测试 (1)
- 设计模式 (2)
- unicode (1)
- OceanBase (3)
- jmagick (1)
- gunicorn (1)
- url (1)
- form (1)
- 安全 (2)
- nlp (8)
- libmemcached (1)
- 规则引擎 (1)
- awk (2)
- 服务器 (1)
- snmpd (1)
- btrace (1)
- 代码 (1)
- cygwin (1)
- mahout (3)
- 电子书 (1)
- 机器学习 (5)
- 数据挖掘 (1)
- nltk (6)
- pool (1)
- log4j (2)
- 总结 (11)
- c++ (1)
- java源代码 (1)
- ocr (1)
- 基础算法 (3)
- SA (1)
- 笔记 (1)
- ml (4)
- zokeeper (0)
- jms (1)
- zookeeper (5)
- zkclient (1)
- hadoop (13)
- mq (2)
- git (9)
- 问题,io (1)
- storm (11)
- zk (1)
- 性能优化 (2)
- example (1)
- tmux (1)
- 环境 (2)
- kyro (1)
- 日志系统 (3)
- hdfs (2)
- python_socket (2)
- date (2)
- elasticsearch (1)
- jetty (1)
- 树 (1)
- 汽车 (1)
- mdrill (1)
- 车 (1)
- 日志 (1)
- web (1)
- 编译原理 (1)
- 信息检索 (1)
- 性能,linux (1)
- spam (1)
- 序列化 (1)
- fabric (2)
- guice (1)
- disruptor (1)
- executor (1)
- logback (2)
- 开源 (1)
- 设计 (1)
- 监控 (3)
- english (1)
- 问题记录 (1)
- Bitmap (1)
- 云计算 (1)
- 问题排查 (1)
- highchat (1)
- mac (3)
- docker (1)
- jdk (1)
- 表达式 (1)
- 网络 (1)
- 时间管理 (1)
- 时间序列 (1)
- OLAP (1)
- Big Table (0)
- sql (1)
- kafka (1)
- md5 (1)
- springboot (1)
- spring security (1)
- Spring Boot (3)
- mybatis (1)
- java8 (1)
- 分布式事务 (1)
- 限流 (1)
- Shadowsocks (0)
- 2018 (1)
- 服务治理 (1)
- 设计原则 (1)
- log (0)
- perftools (1)
最新评论
-
siphlina:
课程——基于Python数据分析与机器学习案例实战教程分享网盘 ...
Python机器学习库 -
san_yun:
leibnitz 写道hi,我想知道,无论在92还是94版本, ...
hbase的行锁与多版本并发控制(MVCC) -
leibnitz:
hi,我想知道,无论在92还是94版本,更新时(如Puts)都 ...
hbase的行锁与多版本并发控制(MVCC) -
107x:
不错,谢谢!
Latent Semantic Analysis(LSA/ LSI)算法简介 -
107x:
不错,谢谢!
Python机器学习库
不管怎么说,搜索都是非常重要的技术,不仅仅是操作系统集成了,很多应用软件都有搜索的功能,论坛有专门用于在论坛搜索的,互联网就有诸如像百度、谷歌之类的搜索引擎,总而言之,只要数据量稍微大一点的应用程序,都会提供搜索这个功能.
我们为什么需要Lucene?
任何的的查询功能都类似,都是对文本内容的搜索,说白了,就是找出含有指定字符串的的资源,只是查找的范围不同而已.
目 前的主流搜索都是全文搜索,即根据程序扫描文章中的每一个词,为每一个词建立相应的索引,并且指明该词在文章中出现的次数和位置.当用户查询时,根据建立 的索引进行查找,类似于通过字典的检索方式来查字的过程.我们做搜索,要保证几点,第一点就是要快,如果百度、谷歌搜个东西要10几秒,恐怕都没人用了 吧?第二点,光快有什么?搜出来的东西完全不是自己想要的,大家通过搜索引擎找东西的时候,肯定注意到了一点,就是大家很少在2页以后还点下一页,这搜索 的结果是被处理过的,把最有可能是你需要的东西放在了前面,这就是准确性,而且搜索只针对文本,不管你的关键字的语义.所以,总结一下,就是:
- 只处理文本
- 不处理语义
- 英文不区分大小写
- 结果列表有相关度排序
和数据库的搜索语句有何不同?
我们为什么需要专门对全文搜索进行描述呢?因为它可以做到select语句做不到的事情.如果我们需要在数据库里面搜索一个关键字,比如ant,就会有类似的语句:
SELECT * FROM table_name WHERE content like ' %ant% ' |
会把planting之类的单词也搜索出来,显然就是没有意义的,没有人会喜欢这样结果.
另外数据库的搜索也并不能为结果做相关度得分,也就做不了相关度排名.搜索结果也更多的是无意的,或者是无用的.
最后一点,也很致命,数据库中的like,找得非常慢,一条记录一条记录地找,有时候简直难以忍受,而用全文检索的方式则是先在目录里面查,找到记录所在的位置,再直接定位过去.
所以select语句的弱点就是Lucene的优点,它可以解决上述的问题.
在程序中需要引入哪些包?
需要如何准备开发环境呢,哪些包是必须要有的,心里要大致有个数才行:
lucene-core-3.0.1.jar(最为核心的包,必不可少)
contrib/analyzers/common/lucene-analyzers-3.0.1.jar(用来将一段字符串以什么规则分解开来的分词器)
contrib/highlighter/lucene-highlighter-3.0.1.jar(将找到的结果高亮显示)
contrib/memory/lucene-memory-3.0.1.jar(高亮显示jar包所依赖的包)
Lucene的工作流程
我 们每次使用搜索引擎的时候,右上角往往会显示用了多久,这个时间让人老是感觉不太准确,好像没那么快.这不是说搜索引擎的速度不够,有可能查询真的只用了 那么点时间,但是返回的页面也是需要时间来生成的,还要在页面上加入广告之类的,这个时间可能就没有计算在内.总而言之,真正用来搜索的时间是非常短的, 那么它为什么能够这么快呢?实际上,搜索的时候并不是在数据库里面进行搜索,而是在Lucene维护的索引库里面进行的,索引库包含两部分内容,一个称之 为目录,这个目录里面就存有各种关键词对应数据的位置,搜索的时候上,Lucene就以某种指定的规则将你提供的关键字进行分词,然后在目录里面找,找得 到的话,就返回一个编号,这个编号是唯一的,通过这个编号可以找到数据,但是数据也不是存在数据库中的.Lucene的索引库中更多的部分是用来存储数据 的,这个数据是从互联网或者文件系统或者数据库中找到的,就像百度的快照一样,它只是一个缓存,给你看大致的内容,当你确定这就是你要找的内容,并且点进 去的时候,才真正地去访问那个页面.搜索都是在索引库里面完成的.那么就出现了一个问题,有时候搜索到的东西,点进去发现不存在,或是已经删除了,或是已 经更新了.那么就需要经常更新,或是时时更新.大致流程如下:
爬虫用来不间断的获取数据,通常刚刚发布到互联网上的数据不能立即在搜索引擎中找到,就是因为这个原因.可以指定让爬虫爬哪些类型的页面,来做垂直搜索.
索引库的CURD
一切前提都是建立在有索引的基础上,所以要先创建索引.对索引进行写的对象是IndexWriter.需要指定索引的位置,可以在文件系统中,也可以在内存中,除非保证计算机的内存每时每刻都是存在的,否则将丢失:
/* * |
*FSDirectory:Directory是抽象类,FSDirectory是继承它的子类.FS前缀代表文件系统(File System),指定在当前路径下建立索引的文件夹名为indexDir. *Analyzer:分词器.以某种规则对关键字进行分词,分词的结果存进目录,用编号与数据对应,需要指定Lucene的版本号. *IndexWriter:能够建立索引库,要给定上面两个类.这里的第三个参数表示一个索引里面最多存多少个Field,超出部分将忽略. *MaxFieldLength.LIMITED:10000. |
*/ |
Directory directory = FSDirectory. open(new File(" ./indexDir/ " )); |
Analyzer analyzer = new StandardAnalyzer(Version. LUCENE_30); |
IndexWriter indexWriter = new IndexWriter(directory,analyzer,MaxFieldLength. LIMITED); |
这 样就能创建索引库,但是不能把对象直接存进去,需要转成Lucene需求的对 象:org.apache.lucene.document.Document,Document的每一个Field都代表对象需要存储在索引库中的属 性,这样在搜索的时候,可以看到数据的摘要:
/* * |
*用Document的add()方法增加一个属性进索引库,接收一个Field对象. *Field对象的第一个参数:用指定的字符串创建一个Field对象. *Field对象的第二个参数:存储的值. *Field对象的第三个参数:是否在索引库的数据里面存储Field的值. *Field对象的第四个参数:以何种方式对第二个参数的值进行操作(分词、不分词、不建立索引). |
*/ |
Document doc = new Document(); |
Article article = (Article) obj; |
doc. add(new Field(" id " ,article. getId(). toString(),Store. YES,Index. ANALYZED)); |
把你的对象转为Document对象就可以被IndexWriter添加到索引库中了:
indexWriter. addDocument(Document); |
这样,一个完整的创建索引就完成了.
添加索引的目的就是为了有效、快捷的查询,与IndexWriter对应,Lucene为查询提供了相应的API,org.apache.lucene.search.IndexSearcher,需要给它指定索引库的目录:
Directory directory = FSDirectory. open(new File(" ./indexDir/ " )); |
IndexSearcher indexSearcher = new IndexSearcher(directory); |
Lucene支持多种查询方式,最常用的就是Query对象了:
/* * |
*提供分词器 |
*QueryParser:用于解析查询字符串的处理器类 *第二个参数:在哪个Field里面查找 *Query.parse:需要解析的查询字符串 |
*/ |
Analyzer analyzer = new StandardAnalyzer(Version. LUCENE_30); |
QueryParser queryParser = new QueryParser(Version. LUCENE_30," title " ,analyzer); |
Query query = queryParser. parse(" panpan " ); |
提供分词器一定要注意:创建索引库时的分词器要和解析时用的分词器一样 . 不然规则不一样,处理的关键词也不一样.很的可能找不到结果.得到Query对象之后,就可以进行查询了.要用到IndexSearcher的 search()方法,这个方法需要两个参数,第一个参数就是Query对象,第二个参数是需要指定返回前多少条结果.然后返回一个TopDocs对象, 返回一个对象而不是一个集合也是很好理解的,因为如果我们指定了返回前100条结果,如果结果总数大于100,我们就无法知道总共有多少条记录,也就无法 完成分页.所以返回一个对象,这个对象封装了记录的总数和符合搜索条件的List集合:
/* * |
*得到TopDocs对象之后,可以获取它的成员变量totalHits和scoreDocs.这两个成员变量的访问权限是public的,所以可以直接访问 |
*/ |
TopDocs topDocs = indexSearcher. search(query, 100 ); |
Integer count = topDocs. totalHits; |
ScoreDoc[] scoreDocs = topDocs. scoreDocs; |
然后通过循环的方式打印出来,以验证效果是否正确:
/* * |
*从ScoreDoc对象里面可以获取两个东西,同样是public的访问权限: *score:相关度得分,跟在内容中出现的次数有关 *doc:从上面那个流程图中可以得知,索引库从应用程序那里接收的是Document对象,所以返回的也是Document对象. *DocumentUtils.docConvert():自己写的工具方法,因为很多地方都要用到双边的转换 |
*/ |
List< Article> list = new ArrayList(); |
for (int i = 0 ;i< scoreDocs. length ;i+ + ){ |
ScoreDoc scoreDoc = scoreDocs[i]; |
//浮点类型的得分 |
float score = scoreDoc. score; |
int docID = scoreDoc. doc; |
Document document = indexSearcher. doc(docID); |
list. add(DocumentUtils. docConvert(document, Article. class )); |
} |
System. out. println(" 总共获取了 " + count + " 条记录 " ); |
for (Article a: list){ |
System. out. print(a. getId() + " " ); |
System. out. print(a. getContent() + " " ); |
System. out. println(a. getTitle()); |
} |
把工具方法也放上来吧:
/* * |
*有些东西还是写死了,其实完全可以通过反射来完成,当时没有花太多时间的结果 |
*/ |
public static < M,T> T docConvert(M obj,Class< T> clazz){ |
try { |
T t = clazz. newInstance(); |
if (t instanceof Document){ |
Document doc = (Document) t; |
Article article = (Article) obj; |
doc. add(new Field(" id " ,article. getId(). toString(),Store. YES,Index. ANALYZED)); |
doc. add(new Field(" title " ,article. getTitle(),Store. YES,Index. ANALYZED)); |
doc. add(new Field(" content " ,article. getContent(),Store. YES,Index. ANALYZED)); |
return (T) doc; |
}else if (t instanceof Article){ |
Article article = (Article) t; |
Document doc = (Document) obj; |
article. setId(Integer. parseInt(doc. get(" id " ))); |
article. setContent(doc. get(" content " )); |
article. setTitle(doc. get(" title " )); |
return (T) article; |
} |
return null ; |
} catch (Exception e) { |
throw new RuntimeException(e); |
} |
} |
基本的创建和查询就完成了.有几点需要注意:
- IndexWriter和IndexSearcher使用完后,要记得调用它们的close()方法
- 版本号一定要选择当前使用的版本
- 如果在创建索引时选择Store.NO,将不会在索引库的数据中添加内容;选择Index.NO,将不会在索引库中增加目录
- 目录和分词器都要匹配,不然找不到结果.更好的做好是在一个工具类中声明为static,初始化一次就行了
接下来考虑删除索引,为什么不是先更新呢?因为删除也是更新的一部分.还是需要用到IndexWriter类,有两种方式可以删除,第一种就是使用Term类,第二种就是将满足搜索条件的删除:
/* * |
*Term类也是用来搜索的,构造函数的意思是:在哪个Field里面查哪个关键词 |
*然后调用IndexWriter的deleteDocument()方法删除包含指定Term的Document |
*/ |
IndexWriter indexWriter = null ; |
Term term = new Term(" title " ," panpan " ); |
indexWriter =
new
IndexWriter(Configuration.
directory,Configuration. analyzer,MaxFieldLength. LIMITED); |
indexWriter. deleteDocuments(term); |
再来就是更新,为什么把更新放在最后?因为更新操作需要较高的代价,因为文档修改后,即使是很小的修改,就可能会造成文档中的很多关键词的位置都发生变化,这就需要频繁的读取和修改记录,这种代价是相当高的.因此,一般不进行真正的更新操作,而是使用"先删除,再创建
"的策略代替更新操作:
/* * |
*最后一句话,相当于: indexWriter.deleteDocuments(term); indexWriter.addDocument(doc); 先删除,再创建! |
*/ |
IndexWriter indexWriter = null ; |
Article article = new Article(); |
article. setContent(" This is the updated content! " ); |
article. setId(1 ); |
article. setTitle(" panpan " ); |
Term term = new Term(" id " ,article. getId(). toString()); |
indexWriter =
new
IndexWriter(Configuration.
directory,Configuration. analyzer,MaxFieldLength. LIMITED); |
indexWriter. updateDocument(term, DocumentUtils. docConvert(article, Document. class )); |
基 本的CURD就到此结束了.但是有些问题没有解决,比如搜索时以上代码只能在一个Field里面进行搜索,如果我要在title和content里面同时 进行搜索就不行,搜索两次?太无聊了.lucene当然有相应的解决办法,比如使用QueryParser的子 类:MultiFieldQueryParser.从这个名字就猜到是专用来做多Field搜索的:
/* * |
*接收一个String数组也是可以理解的. |
*/ |
String[] fields = {" title " ," content " }; |
MultiFieldQueryParser queryParser =
new
MultiFieldQueryParser(Version. LUCENE_30,fields,Configuration. analyzer); |
Query query = queryParser. parse(" love " ); |
如果稍微留心就可以注意到,索引库的文件不是一层不变的,cfs类型的文件在不停的有规律地增加,这个文件多了以后,会影响到搜索的效率,因为它要打开多个文件,所以我们又要想办法让它合并成一个文件:
indexWriter. addDocument(DocumentUtils. docConvert(article, Document. class )); |
indexWriter. optimize(); |
这是手动优化的方法,IndexWriter在关闭时候会自动调用commit()方法,这个方法会把索引真正的写到硬盘上去,也就是说每一次对索引库进行操作,都会生成一个索引文件.手动优化毕竟不太方便,那是否有自动优化的操作呢?答案是肯定的:
indexWriter. addDocument(DocumentUtils. docConvert(article, Document. class )); |
// 手动优化 合并文件 |
// indexWriter.optimize(); |
// 自动优化 合并文件 |
indexWriter. setMergeFactor(5 ); |
设置合并因子即可,这里又有两点需要注意:
- 默认合并因子为10,也就是说cfs文件达到10个,Lucene就会自动合并
- 设置合并因子的代码一定要在操作IndexWriter的时候进行,并且是每一次操作的时候都要进行,它需要不停的判断
接 着下一个问题,更新索引库实际上就是更新硬盘上的目录,每次更新或者创建新的索引都对硬盘进行操作,大家肯定都知道效率不高,但是索引库一定是要放在硬盘 上的,不能随着程序的结束而结束,那么就要找个既能存储在硬盘上,又能保证效率的方法,比如程序启动的时候从硬盘加载索引库,而后一切操作都是针对内存中 的索引库进行操作,在程序结束的时候把内存中的索引库存储在硬盘上去.这样就能解决这个不是问题的问题:
public void updateRAMIndex(){ |
// RAMDirectory是Directory的子类,将在内存区保留一段缓存 |
RAMDirectory ram = null ; |
IndexWriter indexWriter = null ; |
try { |
// 将指定目录中的索引加载到内存中来 |
ram = new RAMDirectory(Configuration. directory); |
// 第一个参数决定了这是一个操作内存索引库的IndexWriter |
indexWriter =
new
IndexWriter(ram,Configuration. analyzer,MaxFieldLength. LIMITED); |
// 添加新的数据 |
Article article = new Article(45 ," panpan " ," love you " ); |
indexWriter. addDocument(DocumentUtils. docConvert(article, Document. class )); |
/* *一个索引库只能有一个IndexWriter,一一对应. |
* 同时,同一时刻只能有一个IndexWriter,如果有两个,不能写同一个文件,不然就有问题 |
*/ |
indexWriter. close(); |
/* *这个IndexWriter是针对文件系统的 |
* 第三个参数是指: 如果指定为true,表示重新创建索引库,如果已存在,就删除后再创建; |
* 指定为false,表示追加(默认值) |
* 如果不存在,就抛异常. |
*/ |
indexWriter =
new
IndexWriter(Configuration.
directory,Configuration. analyzer,true ,MaxFieldLength. LIMITED); |
/* * |
* 将指定目录添加到文件系统中,并且不优化 |
* 如果传入一个IndexReader,可以进行优化: |
IndexReader indexReader = IndexReader.open(ram); |
indexWriter.addIndexes(indexReader); |
*/ |
indexWriter. addIndexesNoOptimize(ram); |
} catch (Exception e) { |
throw new RuntimeException(e); |
}finally { |
try { |
ram. close(); |
indexWriter. close(); |
} catch (Exception e) { |
throw new RuntimeException(e); |
} |
} |
} |
发表评论
-
三种solr提交(commit)索引的方式
2014-03-01 13:40 20923三种solr提交索引的 ... -
solr build索引性能
2014-03-01 13:26 1537测试代码 package mytest; impo ... -
Solr性能优化之filterCache
2014-02-27 10:48 940原文:Solr性能优化之filterCache 什么是f ... -
SolrCore查询过程源码分析
2014-02-12 15:37 1241一.环境启动代码 SolrResourceLoader. ... -
通过设置SolrInputDocument 的boost来改变文档评分
2014-01-31 23:18 1542solr通过关键字查询后的文档得分最后要乘以这个boost因 ... -
Solr 使用 Log4j
2014-01-25 21:31 2389原文出处:http://blog.chenlb.com/20 ... -
solr基本概念
2014-01-25 20:49 1398原文:http://www.solrtutorial.com ... -
solr load比较高
2013-12-02 09:50 1063最近solr的load比较高,通过jstack查看: 写 ... -
Solr索引放入内存
2013-08-28 11:45 10021在一次技术群中,中听到一位sina的架构师,他们是采用基于l ... -
solr build索引遇到控制字符的错误
2013-08-23 15:12 617今天build solr 索引遇到如下错误: 发现提 ... -
solr 更新index的最佳实践
2013-08-22 15:24 1011其中重要一点是,solr不运行并发commit。 懒得翻 ... -
Scaling Solr(Solr的扩展)
2013-08-21 20:55 695Solr 的扩展 (Scaling) 当你的索 ... -
Lucene Scoring 评分机制
2013-08-21 20:52 716Lucene 评分体系/机制 ... -
solr build index例子
2013-08-20 14:24 859p = PeopleDO({'userid':101,'s ... -
solr build索引时commit与optimize的概念
2013-08-20 14:22 1930大家都知道,solr在提交索引的时候有commit和opt ... -
solr in action
2013-08-20 13:56 1232solr in action电子书 -
solr的函数查询_FunctionQuery_总结
2012-12-21 15:55 4180作用 函数查询的作用,是影响计算score的算法,并不替 ... -
solr java客户端build索引
2012-12-17 17:08 1078package com.duitang.views.tes ... -
Solr搜索的排序打分规则
2012-11-20 17:48 2441使用Solr搭建搜索引擎很容易,但是如何制定合理的打分规则(b ... -
Solrj
2012-11-20 14:17 976solr 的java客户端可比python复杂一些,贴点代码: ...
相关推荐
- **调试与监控**:利用日志记录和性能监控工具可以帮助开发者更好地理解和优化 Lucene 的运行状态。 #### 知识点六:Lucene 的高级主题 - **高级搜索技术**:包括布尔搜索、模糊搜索、近义词搜索等多种高级搜索...
《Lucene in Action》第二版是一部全面、实用且易于理解的指南,无论你是刚接触Lucene的新手还是寻求进一步提高的高级用户,都能从中获得巨大的价值。通过本书的学习,你将能够掌握如何有效地利用Lucene来满足各种...
文档则代表要索引的数据单元,可以是一个网页、电子邮件或数据库记录。字段是文档的组成部分,比如标题、内容等,每个字段可能有不同的处理方式。术语则是索引的基本单元,通常是分词后的单词。 接下来,我们要了解...
然而,对于新手来说,Lucene的API可能会显得较为复杂。这就是Lucene-Sugar发挥作用的地方。它是一个针对Lucene的轻量级包装器,旨在简化Lucene的常用操作,比如索引构建、查询执行和结果处理。通过引入一些方便的...
同时,Spring的AOP特性可以方便地实现日志记录、权限控制等功能,而其事务管理功能则确保了数据库操作的一致性。 博客系统的具体实现可能包括以下几个模块: 1. 用户模块:用户注册、登录、个人信息管理等,通常会...
2. 兴趣驱动:如果你对某个领域特别感兴趣,比如搜索引擎技术,你可以选择如Lucene这样的项目,通过学习实践深入理解其工作原理。 3. 熟悉的技术栈:选择使用你熟悉编程语言和技术的项目,这将使你更容易理解和贡献...
- 论坛板块表:定义不同的讨论区,如“新手入门”、“技术讨论”等,每个板块有其ID和名称。 - 主题表:存储用户的帖子,包括主题ID、发帖人ID、板块ID、主题标题、发布时间等内容。 - 帖子内容表:包含帖子的...
8. **日志记录与错误处理**:为了便于调试和问题追踪,系统可能会有详细的日志记录功能,并实现异常处理机制。 9. **性能优化**:通过缓存策略、数据库索引优化、负载均衡等手段提升系统性能,确保高并发访问时的...
### JBoss Enterprise BRMS Platform-5-JBoss Rules 5 参考指南 #### 概述 ...无论是对于新手还是有经验的管理员来说,这份指南都提供了丰富的信息和实用的操作指南,是进行BRMS项目不可或缺的重要资源。
3. **文档(Document)**:文档是存储在索引中的基本数据单元,类似于数据库中的记录。文档以 JSON 格式存储,可以包含各种字段。 4. **类型(Type)**:在较早版本的 Elasticsearch 中,每个索引可以有多个类型,但自 ...
8. 性能优化:Solr 7在性能方面进行了优化,如通过使用Lucene库的新特性,提高了搜索速度和内存管理效率。 9. 安全性:Solr 7引入了更多的安全特性,如SolrCloud的安全插件,支持基本的身份验证和授权机制,增强了...
2. **论坛板块管理**:定义论坛的不同分类,如技术讨论、新手入门等,用户可以根据兴趣选择参与。这部分可能涉及到数据库设计,如MySQL,用于存储板块信息。 3. **发帖与回帖**:用户可以创建新主题或回复已有帖子...
它将数据库表映射为Java类,表中的行映射为类的实例,实现了对象与数据库记录之间的透明转换。 2. **版本5.2.8** Hibernate 5.2.8是该框架的一个稳定版本,修复了一些已知问题,增强了性能,并提供了对最新Java...
SVN是一种集中式的版本控制系统,能够有效管理项目的历史版本和变更记录。 #### 三、Lucene、Nutch与Hadoop的重要性 在搜索引擎领域,**Lucene**、**Nutch**和**Hadoop**都是非常重要的项目和技术。其中: - **...
输入文件为c:/mycert.cer,获取一个处理X.509证书的证书工厂…… Java+ajax写的登录实例 1个目标文件 内容索引:Java源码,初学实例,ajax,登录 一个Java+ajax写的登录实例,附有JAVA源文件,JAVA新手朋友可以学习一下...