一辆憔、介紹
1.1、項(xiàng)目描述
bbs論壇系統(tǒng)是一個(gè)相對(duì)比較輕量級(jí)的java社區(qū)系統(tǒng)报嵌,上手簡(jiǎn)單虱咧、學(xué)習(xí)成本低、比較容易擴(kuò)展锚国,主要包括用戶注冊(cè)腕巡、登錄、退出血筑、發(fā)帖回帖绘沉、主頁(yè)、貼子置頂刪除等功能云挟。其中還具有全局用戶錯(cuò)誤處理梆砸、語(yǔ)法直觀,性能高超,功能能齊全的模板引擎和強(qiáng)大的搜索技術(shù)等特點(diǎn)园欣。
1.2帖世、項(xiàng)目特點(diǎn)
bbs論壇系統(tǒng)采用spring boot2,Spring Cache沸枯,Elastic Search日矫,Beetl,BeetlSQL框架基于MVC機(jī)制開(kāi)發(fā)的一套簡(jiǎn)易的社區(qū)論壇系統(tǒng)绑榴,可拿來(lái)即用哪轿。強(qiáng)大、靈活的搜索滿足絕大部分社區(qū)用戶的需求翔怎。支持MySQL窃诉、Oracle等主流數(shù)據(jù)庫(kù)杨耙。
Elastic Search : 是一個(gè)分布式可擴(kuò)展的實(shí)時(shí)搜索和分析引擎,一個(gè)建立在全文搜索引擎 Apache Lucene(TM) 基礎(chǔ)上的搜索引擎.當(dāng)然 Elasticsearch 并不僅僅是Lucene 那么簡(jiǎn)單,它不僅包括了全文搜索功能飘痛,還可以進(jìn)行以下工作珊膜;分布式實(shí)時(shí)文件存儲(chǔ),并將每一個(gè)字段都編入索引宣脉,使其可以被搜索车柠。實(shí)時(shí)分析的分布式搜索引擎∷懿可以擴(kuò)展到上百臺(tái)服務(wù)器竹祷,處理PB級(jí)別的結(jié)構(gòu)化或非結(jié)構(gòu)化數(shù)據(jù)。Elastic Search的兩個(gè)基本特征就是儲(chǔ)存和索引羊苟,Elasticsearch是面向文檔的塑陵,是面向文檔型數(shù)據(jù)庫(kù),即一條數(shù)據(jù)就是一個(gè)文檔蜡励,它可以存儲(chǔ)整個(gè)對(duì)象或文檔猿妈。然而它不僅僅是存儲(chǔ),還會(huì)索引每個(gè)文檔的內(nèi)容使之可以被搜索巍虫。在Elasticsearch中,你可以對(duì)文檔(而非成行成列的數(shù)據(jù))進(jìn)行索引鳍刷、搜索占遥、排序、過(guò)濾输瓜。這種理解數(shù)據(jù)的方式與以往完全不同瓦胎,這也是Elasticsearch能夠執(zhí)行復(fù)雜的全文搜索的原因之一,同時(shí)具有強(qiáng)大的索引能力使得大大提升了搜索性能尤揣。因此搜索便是Elastic Search 最核心功能搔啊。后面的發(fā)帖模塊的分析會(huì)給出Elastic Search的基本使用,也可翻閱官方api地址:https://es.xiaoleilu.com/
Beetl :java模板引擎北戏,Beetl相對(duì)于其他java模板引擎负芋,具有功能齊全,語(yǔ)法直觀,性能超高嗜愈,以及編寫(xiě)的模板容易維護(hù)等特點(diǎn)旧蛾。使得開(kāi)發(fā)和維護(hù)模板有很好的體驗(yàn)。Beetl的核心是GroupTemplate蠕嫁,是一個(gè)重量級(jí)對(duì)象锨天,實(shí)際使用的時(shí)候建議使用單模式創(chuàng)建,創(chuàng)建GroupTemplate需要倆個(gè)參數(shù)剃毒,一個(gè)是模板資源加載器病袄,一個(gè)是配置類搂赋,可參考:http://ibeetl.com/guide/#beetl
BeetlSQL:BeetSql是一個(gè)全功能DAO工具類似我們常用的ORM框架Mybatis、Hibernate 益缠、JPA脑奠,但BeerlSQL同時(shí)具有Hibernate 和 Mybatis優(yōu)點(diǎn)功能,適用于承認(rèn)以SQL為中心左刽,同時(shí)又需求工具能自動(dòng)能生成大量常用的SQL的應(yīng)用捺信。使用大量?jī)?nèi)置SQL,SQL 模板基于Beetl實(shí)現(xiàn)欠痴,更容易寫(xiě)和調(diào)試迄靠,以及擴(kuò)展可以針對(duì)單個(gè)表(或者視圖)代碼生成pojo類和sql模版,甚至是整個(gè)數(shù)據(jù)庫(kù)喇辽。能減少代碼編寫(xiě)工作量提高開(kāi)發(fā)效率掌挚。其它可參考:http://ibeetl.com/guide/#beetlsql
對(duì)于大部分開(kāi)發(fā)者這3個(gè)框架也許是我們用得最少的,因此這個(gè)社區(qū)論壇系統(tǒng)也是我們學(xué)習(xí)Elastic Search菩咨,Beetl吠式,BeetlSQL這幾個(gè)框架很好的一個(gè)demo
1.3 、項(xiàng)目介紹
此社區(qū)系統(tǒng)為單模塊應(yīng)用抽米,未實(shí)行前后端分離特占,前后端交互是采用MVC? ModelAndView 的那套機(jī)制與原理,使用ModelAndView類用來(lái)存儲(chǔ)處理完后的結(jié)果數(shù)據(jù)云茸,以及顯示該數(shù)據(jù)的視圖是目。業(yè)務(wù)處理器調(diào)用模型層處理完用戶請(qǐng)求后,把結(jié)果數(shù)據(jù)存儲(chǔ)在該類的model屬性中标捺,把要返回的視圖信息存儲(chǔ)在該類的view屬性中懊纳,然后讓該ModelAndView返回該Spring?MVC框架對(duì)該對(duì)象進(jìn)行解析,最后把結(jié)果數(shù)據(jù)顯示在指定的頁(yè)面上亡容。?
1.4嗤疯、本地部署
.??環(huán)境要求:jdk1.8+ 、MySql5.5+
. 創(chuàng)建表database?數(shù)據(jù)庫(kù)編碼為 UTF-8
. 執(zhí)行數(shù)據(jù)庫(kù)腳本闺兢,執(zhí)行 install-mysql.sql 文件茂缚,初始化數(shù)據(jù)
. git clone 項(xiàng)目 運(yùn)行 BbsMain.class類
.項(xiàng)目訪問(wèn)路徑:http://localhost:8084/bbs/bbs/index
.賬號(hào)密碼:admin/123456
.必須安裝elastic search 作為全文搜索:?
下載?https://www.elastic.co/downloads/past-releases/elasticsearch-5-4-3(更高級(jí)的版本目前暫時(shí)未驗(yàn)證通過(guò)),進(jìn)入bin目錄,調(diào)用elasticsearch 啟動(dòng)
.安裝elastic search 分詞:進(jìn)入bin目錄列敲,運(yùn)行 ./elasticsearch-plugin install analysis-smartcn
然后重新啟動(dòng)elasticsearch
注:本地部署測(cè)試只需要安裝elasticsearch阱佛,然后cmd進(jìn)入安裝elasticsearch的bin目錄調(diào)用elasticsearch啟動(dòng)即可進(jìn)行發(fā)帖搜索等相關(guān)功能實(shí)踐
二、項(xiàng)目后端源碼分析
2.1戴而、分析項(xiàng)目的思路
當(dāng)我們熟悉一個(gè)項(xiàng)目時(shí)凑术,我們首先應(yīng)該要清楚的知道這個(gè)項(xiàng)目運(yùn)用了哪些技術(shù),其次我們就需要知道這個(gè)項(xiàng)目是怎么樣進(jìn)行數(shù)據(jù)交互的(即:前后端是怎樣進(jìn)行數(shù)據(jù)交互的)所意,再深層次我們則可了解設(shè)計(jì)原理淮逊、思路以及其它相關(guān)技術(shù)的原理等等催首。(下面的登錄模塊詳細(xì)介紹了其交互流程)
?2.2 、登錄模塊
在首頁(yè)index.html頁(yè)面中引入了layout.html,如下:
在layout.html中有如下一段代碼泄鹏,其采用了ajax方式登錄
在后臺(tái)登錄接口
核心代碼有兩行:
1郎任、?BbsUser user =bbsUserService.getUserAccount(userName, password);通過(guò)用戶名和密碼檢測(cè)獲取用戶信息
2、WebUtils.loginUser(request, response, user, true); 將用戶信息set到cookie使戶登陸狀態(tài)維持备籽,cookie設(shè)計(jì)為:des(私匙).encode(userId~time~maxAge~password~ip),并且為了保證安全同時(shí)將cookie指定為httpOnly
2.3舶治、發(fā)帖模塊
2.3.1、首先我們安裝 Elasticsearch后安裝Marvel车猬,Marvel是Elasticsearch的管理和監(jiān)控工具霉猛,在開(kāi)發(fā)環(huán)境下免費(fèi)使用。它包含了一個(gè)叫做Sense的交互式控制臺(tái)珠闰,使用戶方便的通過(guò)瀏覽器直接與Elasticsearch進(jìn)行交互惜浅。安裝Marvel不是必須的,但是它可以通過(guò)在你本地Elasticsearch集群中運(yùn)行示例代碼伏嗜。Marvel是一個(gè)插件坛悉,可在Elasticsearch目錄中運(yùn)行以下命令來(lái)下載和安裝:
./bin/plugin -i elasticsearch/marvel/latest
echo'marvel.agent.enabled: false'>> ./config/elasticsearch.yml? ? ?這個(gè)命令則是關(guān)閉監(jiān)控
運(yùn)行?Elasticsearch啟動(dòng)速度會(huì)有點(diǎn)慢耐心等待會(huì),其索引地址默認(rèn)端口為9200
web管理界面head 安裝 進(jìn)入bin目錄下承绸,打開(kāi)cmd裸影,進(jìn)入dos界面 輸入:plugin install mobz/elasticsearch-head 進(jìn)行下載,注冊(cè)服務(wù)進(jìn)入bin目錄下军熏,打開(kāi)cmd空民,進(jìn)入dos界面 依次輸入: service.bat install service.bat start 成功之后,再輸入 services.msc 跳轉(zhuǎn)到Service服務(wù)界面羞迷,可以直接查看es的運(yùn)行狀態(tài)!?
訪問(wèn)http://localhost:9200/頁(yè)面顯示有東西則表示啟動(dòng)成功,接下來(lái)我們可以開(kāi)始各種實(shí)驗(yàn)了我們?cè)陧?xiàng)目的pom文件中加入elasticsearch依賴
2.3.2画饥、我們?cè)陧?xiàng)目的配置文件 application.properties 配置Elasticsearch的地址
#Elasticsearch
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
#Elasticsearch bbs索引地址
elasticsearch.bbs.content.url=http://127.0.0.1:9200/bbs/content/_search
2.3.3衔瓮、Elasticsearch 模板配置,這個(gè)模板根據(jù)自己的需求自行配置下面以發(fā)帖為例簡(jiǎn)述其使用,當(dāng)然這里只是最基本的一些使用抖甘,高級(jí)點(diǎn)的還有分布式集群热鞍、聚合以及索引的種類等想要了解更多的可參考官方文檔或相關(guān)博客。
發(fā)帖模塊算是整個(gè)論壇系統(tǒng)的核心模塊之一衔彻,這里用spring AOP技術(shù)(此處使用的是AOP的環(huán)繞通知@Around 在這里其底層使用的是cglib動(dòng)態(tài)代理)通過(guò)匹配持有指定注解@EsIndexs或@EsIndexType的方法實(shí)現(xiàn)了創(chuàng)建索對(duì)象引并且Elasticsearch數(shù)據(jù)保存薇宠。我們也以發(fā)帖模塊為例來(lái)說(shuō)明項(xiàng)目中如何來(lái)使用Elasticsearch?
@EsIndexType和EsIndexs中用@Document標(biāo)記,表示這個(gè)類為文檔文檔對(duì)象艰额,@Document注解是Elasticsearch的常用的注解之一澄港,其常用的屬性有①indexName:對(duì)應(yīng)索引庫(kù)名稱;②type:對(duì)應(yīng)在索引庫(kù)中的類型柄沮;④shards:分片數(shù)回梧;replicas:副本數(shù)废岂;indexStoreType:索引文件存儲(chǔ)類型;createIndex:是否創(chuàng)建索引狱意。另外Elasticsearch還有@Field湖苞,作用在成員變量,標(biāo)記為文檔的字段并制定映射屬性详囤。@Id:作用在成員變量财骨,標(biāo)記一個(gè)字段為id主鍵;一般id字段或是域不需要存儲(chǔ)也不需要分詞藏姐;type:字段的類型隆箩,取值是枚舉FieldType;index:是否索引包各,布爾值類型摘仅,默認(rèn)是true;store:是否存儲(chǔ)问畅,布爾值類型娃属,默認(rèn)值是false;analyzer:分詞器名稱护姆,一般情況下Elasticsearch都會(huì)把該字段存儲(chǔ)到Field域中矾端,只是當(dāng)store=?false時(shí),默認(rèn)設(shè)置卵皂;那么給字段只存儲(chǔ)在"_source"的Field域中秩铆,當(dāng)store = true時(shí),該字段的value會(huì)存儲(chǔ)在一個(gè)跟_source平級(jí)的獨(dú)立Field域中灯变;同時(shí)也會(huì)存儲(chǔ)在_source中殴玛,因此,我們?cè)谑褂胹tore時(shí)需注意以下兩點(diǎn):(1)source field在索引的mapping中disable了添祸。這種情況下如果不將某個(gè)field定義成store=true滚粟,那些將無(wú)法在返回的查詢結(jié)果中看到這個(gè)field;(2)__source的內(nèi)容非常大我們想要在返回的_source document中解釋出某個(gè)field的值的話,開(kāi)銷會(huì)很大(當(dāng)然你也可以定義source filtering將減少network overhead)刃泌,比例某個(gè)document中保存的是一本書(shū)凡壤,所以document中可能有這些field: title, date, content弦赖。假如我們只是想查詢書(shū)的title 跟date信息疗涉,而不需要解釋整個(gè)_source(非常大),這個(gè)時(shí)候我們可以考慮將title, date這些field設(shè)置成store=true厅瞎。(3)雖然ield store可以減少查詢的開(kāi)銷但其實(shí)這樣也會(huì)加大disk的訪問(wèn)頻率俗扇。假如你將_source中的10個(gè)field都定義store硝烂,那么在你查詢這些field的時(shí)候會(huì)將會(huì)有10次disk seek的操作。而返回_source只有一次disk seek的操作铜幽。所以這個(gè)也是我們?cè)诙x的時(shí)候需要blance的钢坦。
基本用法:
@Field(index=true)表示是否索引究孕;
@Field(analyzer="ik_max_word",searchAnalyzer="ik_max_word")表示是否分詞,如果是分詞就會(huì)按照分詞的單詞搜索爹凹,如果不是分詞就按照整體搜索
@Field(store=true)是否存儲(chǔ)厨诸,也就是頁(yè)面上顯示。
注:analyzer在Elasticsearch中扮演者分析器的角色禾酱,在全文搜索中微酬,詞是一個(gè)搜索單元,表示文本中的一個(gè)詞颤陶,標(biāo)記表示在文本字段中出現(xiàn)的詞颗管,由詞的文本、在原始文本中的開(kāi)始和結(jié)束偏移量滓走、以及數(shù)據(jù)類型等組成垦江。ElasticSearch 把文檔數(shù)據(jù)寫(xiě)到倒排索引(Inverted Index)的結(jié)構(gòu)中,倒排索引建立詞(Term)和文檔之間的映射搅方,索引中的數(shù)據(jù)是面向詞比吭,而不是面向文檔的。分析器(Analyzer)的作用就是分析(Analyse)姨涡,用于把傳入Lucene的文檔數(shù)據(jù)轉(zhuǎn)化為倒排索引衩藤,把文本處理成可被搜索的詞。分析器由一個(gè)分詞器(Tokenizer)和零個(gè)或多個(gè)標(biāo)記過(guò)濾器(TokenFilter)組成涛漂,也可以包含零個(gè)或多個(gè)字符過(guò)濾器(Character Filter)赏表。
在ElasticSearch引擎中,分析器的任務(wù)是分析(Analyze)文本數(shù)據(jù)匈仗,分析是分詞瓢剿,規(guī)范化文本的意思,其工作流程是:首先悠轩,字符過(guò)濾器對(duì)分析(analyzed)文本進(jìn)行過(guò)濾和處理跋选,例如從原始文本中移除HTML標(biāo)記,根據(jù)字符映射替換文本等哗蜈,過(guò)濾之后的文本被分詞器接收,分詞器把文本分割成標(biāo)記流坠韩,也就是一個(gè)接一個(gè)的標(biāo)記距潘,然后,標(biāo)記過(guò)濾器對(duì)標(biāo)記流進(jìn)行過(guò)濾處理只搁,例如音比,移除停用詞,把詞轉(zhuǎn)換成其詞干形式氢惋,把詞轉(zhuǎn)換成其同義詞等洞翩,最終稽犁,過(guò)濾之后的標(biāo)記流被存儲(chǔ)在倒排索引中;ElasticSearch引擎在收到用戶的查詢請(qǐng)求時(shí)骚亿,會(huì)使用分析器對(duì)查詢條件進(jìn)行分析已亥,根據(jù)分析的結(jié)構(gòu),重新構(gòu)造查詢来屠,以搜索倒排索引虑椎,完成全文搜索請(qǐng)求,可見(jiàn)俱笛,分析器扮演的是處理索引數(shù)據(jù)和查詢條件的重要角色捆姜。在2.4版本中,ElasticSearch?預(yù)定義了7個(gè)分析器迎膜,并且支持用戶根據(jù)預(yù)定義的字符過(guò)濾器泥技,分詞器和標(biāo)記過(guò)濾器創(chuàng)建自定義的分析器,以滿足用戶多樣性的文本分析需求磕仅。用戶在創(chuàng)建索引時(shí)配置索引的分析珊豹,通過(guò)向ElasticSearch發(fā)送請(qǐng)求,在請(qǐng)求body的settings 配置節(jié)中設(shè)置索引的分析器宽涌,例如平夜,為索引配置默認(rèn)的分析器:
我們也可自定義分詞如:
分詞發(fā)生在文檔創(chuàng)建和搜索的時(shí)候即讀時(shí)分詞和寫(xiě)時(shí)分詞,讀時(shí)分詞發(fā)生在用戶查詢時(shí)卸亮,ElasticSearch會(huì)即時(shí)地對(duì)用戶輸入的關(guān)鍵詞進(jìn)行分詞忽妒,分詞結(jié)果只存在內(nèi)存中,當(dāng)查詢結(jié)束時(shí)兼贸,分詞結(jié)果也會(huì)隨即消失段直。而寫(xiě)時(shí)分詞發(fā)生在文檔寫(xiě)入時(shí),ElasticSearch會(huì)對(duì)文檔進(jìn)行分詞后溶诞,將結(jié)果存入倒排索引鸯檬,該部分最終會(huì)以文件的形式存儲(chǔ)于磁盤(pán)上,不會(huì)因查詢結(jié)束或者 ElasticSearch重啟而丟失螺垢。ElasticSearch中處理分詞的部分被稱作分詞器喧务,英文是Analyzer,它決定了分詞的規(guī)則枉圃。ElasticSearch自帶了很多默認(rèn)的分詞器功茴,比如Standard、Keyword孽亲、Whitespace等等坎穿,默認(rèn)是Standard。當(dāng)我們?cè)谧x時(shí)或者寫(xiě)時(shí)分詞時(shí)可以指定要使用的分詞器。
發(fā)帖這部分代碼使用的是aop的環(huán)繞通知首先獲取索引的注解集合玲昧,EsIndexType索引類型栖茉,EsIndexs索引,然后獲取獲取索引的數(shù)據(jù)集合孵延,調(diào)用原方法保存相關(guān)數(shù)據(jù)至數(shù)據(jù)庫(kù)吕漂,最后組裝指定索引類型的數(shù)據(jù),保存索引
@Pointcut("@annotation(com.ibeetl.bbs.es.annotation.EsIndexType) || @annotation(com.ibeetl.bbs.es.annotation.EsIndexs)")
private void anyMethod(){//定義ES的切入點(diǎn)?
}
@Around("anyMethod()")public Object simpleAop(ProceedingJoinPoint pjp) throws Throwable{
?try {
? ? ?Signature sig = pjp.getSignature();
? ? ? MethodSignature msig = (MethodSignature) sig;//代理方法
? ? ? ?Object target = pjp.getTarget();//代理類
? ? ? ? Method method = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
? ? ? ? ?EsIndexType esIndexType = method.getAnnotation(EsIndexType.class);
? ? ? ? ? EsIndexs esIndexs = method.getAnnotation(EsIndexs.class);
? ? ? ? ? ?//獲取索引的注解集合
? ? ? ? ? ? List types =new ArrayList<>();
? ? ? ? ? ? if(esIndexs !=null){
? ? ? ? ? ? ? ? ?types.addAll(Arrays.asList(esIndexs.value()));
? ? ? ? ? ? ?}
? ? ? ? ? ? ?if(esIndexType !=null){
? ? ? ? ? ? ? ? ? ?types.add(esIndexType);
? ? ? ? ? ? ? }
? ? ? ? ? ? ?//獲取索引的數(shù)據(jù)集合
? ? ? ? ? ? ? List typeDatas =new ArrayList<>();
? ? ? ? ? ? ? //當(dāng)操作為刪除時(shí)隙袁,需要提前獲取id
? ? ? ? ? ? ? ?for (EsIndexType index : types) {
????????????????????if(index.operateType() == EsOperateType.DELETE) {
? ? ? ? ? ? ? ? ? ? ? ? ? String key = index.key();
? ? ? ? ? ? ? ? ? ? ? ? ? ?Map parameterNames =this.getParameterNames(pjp);
? ? ? ? ? ? ? ? ? ? ? ? ? ? Integer id = (Integer)parameterNames.get(key);
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(id ==null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger.error(target.getClass().getName()+"$"+msig.getName()+":未獲取到主鍵痰娱,
????????????????????????????????無(wú)法更新索引");?
????????????????????????????}else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BbsIndex bbsIndex =esService.createBbsIndex(index.entityType(), (Integer)id);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //創(chuàng)建Elasticsearch索引,可根據(jù)自己的規(guī)則創(chuàng)建唯一索引菩收,這里的規(guī)則為:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //bbs_post梨睁、bbs_reply、bbs_topic 這三張表的主鍵id拼接
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String md5Id = EsUtil.getEsKey(bbsIndex.getTopicId(),bbsIndex.getPostId()
????????????????????????????????????????????????????????????????????????????????????,bbsIndex.getReplyId());
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? EsIndexTypeData data = new EsIndexTypeData(
????????????????????????????????????????????????????index.entityType(), index.operateType(), md5Id);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? typeDatas.add(data);
? ? ? ? ? ????????????????????}
????????????????????????}
????????????????}
? ??????????????Object o = pjp.proceed();
? ? ? ? ? ? ? ? ?//當(dāng)操作為更新時(shí)娜饵,可以從返回值中獲取id
? ? ????????????? for (EsIndexType index : types) {
? ? ? ? ? ? ? ? ? ? ? if(index.operateType() != EsOperateType.DELETE) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? Integer id =null;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?String key = index.key();
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Map parameterNames =this.getParameterNames(pjp);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?id = (Integer)parameterNames.get(key);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?boolean resultErr =false;
? ? ? ? ? ????????????????????if(id ==null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(oinstanceof ModelAndView) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ModelAndView modelAndView = (ModelAndView)o;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?id = (Integer)modelAndView.getModel().get(key);
? ? ? ? ? ????????????????????????? }else if(oinstanceof JSONObject) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? JSONObject json = (JSONObject)o;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? id = json.getInteger(key);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? resultErr =1 == json.getInteger("err")?true:false;
? ? ? ? ? ????????????????????????? }
????????????????????????????}
????????????????????????????if(id ==null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if(!resultErr){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger.error(target.getClass().getName()+"$"+msig.getName()+":未獲取到主鍵坡贺,
????????????????????????????????????????????????無(wú)法更新索引");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
????????????????????????}else {
? ? ? ? ? ? ? ? ? ? ? ? ? ?EsIndexTypeData data =new EsIndexTypeData(index.entityType(), index.operateType(), id);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?typeDatas.add(data);
? ? ? ? ????????????????? }
????????????????????}
????????????????}
? ? ? ? ? ? ? ?for (EsIndexTypeData esIndexTypeData : typeDatas) {
? ? ? ? ? ? ? ? ? ?esService.editEsIndex(esIndexTypeData.getEntityType(), esIndexTypeData.getOperateType(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?esIndexTypeData.getId());
? ? ????????? }
????????????return o;
? ? ?}catch (Exception e) {
????????????throw e;
? ? ?}
}
BbsIndexRepository類是? extends?? CrudRepository<T,ID>, 而CrudRepository 的實(shí)現(xiàn)AbstractElasticsearchRepository中的save(),當(dāng)我們?cè)L問(wèn)配置文件中配置的Elasticsearch中的索引地址如下就會(huì)有內(nèi)容了箱舞。
現(xiàn)在Elasticsearch中已經(jīng)存儲(chǔ)了一些數(shù)據(jù)遍坟,我們可以根據(jù)業(yè)務(wù)需求開(kāi)始進(jìn)行搜索實(shí)踐了
2.4、論壇首頁(yè)select模塊
如果不是通過(guò)關(guān)鍵字搜索則僅僅走數(shù)據(jù)庫(kù)查詢晴股,如果是通過(guò)關(guān)鍵字查詢則先查詢索引即先查詢Elasticsearch所存儲(chǔ)的文襠值然后再通過(guò)相關(guān)對(duì)應(yīng)值查詢數(shù)據(jù)庫(kù)愿伴。這里只分析下通過(guò)關(guān)鍵字搜索即索引查詢。對(duì)PageQuery searcherKeywordPage =this.esService.getQueryPage(keyword,p);的getQueryPage()方法進(jìn)行分析电湘。當(dāng)我們檢索一個(gè)關(guān)鍵字帖子時(shí)我們只要執(zhí)行HTTP GET請(qǐng)求并指出文檔的“地址”——索引隔节、類型和ID既可。根據(jù)這三部分信息寂呛,我們就可以返回原始JSON文檔:
try {
????????HttpHeaders headers =new HttpHeaders();
? ? ? ? ?headers.setContentType(MediaType.APPLICATION_JSON);
? ? ? ? ?//渲染模板,bssContent.html為模板
? ? ? ? ?Template template =beetlTemplate.getTemplate("/bssContent.html");
? ? ? ? ? template.binding("pageSize", pageSize);
? ? ? ? ? template.binding("pageNumber", pageNumber);
? ? ? ? ? template.binding("keyword", keyword);
? ? ? ? ? String esJson = template.render();
? ? ? ? ? HttpEntity? httpEntity =new HttpEntity(esJson,headers);
? ? ? ? ?//使用restTemplate.postForObject()發(fā)送請(qǐng)求獲取本地Elasticsearch服務(wù)上的值怎诫;
? ? ? ? ?// elasticsearch.bbs.content.url為配置文件中配置的Elasticsearch bbs索引地址
? ? ? ? ? String result =restTemplate.postForObject(env.getProperty("elasticsearch.bbs.content.url"),?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? httpEntity, String.class);
? ? ? ? ? List indexObjectList =new ArrayList<>();
? ? ? ? ?//使用ObjectMapper對(duì)含有任意key的JSON進(jìn)行反序列化將多個(gè)實(shí)體放到Map中
? ? ? ? JsonNode root =new ObjectMapper().readTree(result);
? ? ? ? //獲取Elasticsearch服務(wù)上的值的總條數(shù)
? ? ? ? long total = root.get("hits").get("total").asLong();
? ? ? ? //獲取Elasticsearch服務(wù)上的值文本list
? ? ? ? Iterator iterator = root.get("hits").get("hits").iterator();
? ? ? ? //循環(huán)iterator
? ? ? ?while(iterator.hasNext()) {
? ? ? ? ? ?JsonNode jsonNode = iterator.next();
? ? ? ? ? ?double score = jsonNode.get("_score").asDouble();
? ? ? ? ? ? //循環(huán)獲取值并序列化為BbsIndex
? ? ? ? ? ? BbsIndex index =new ObjectMapper().convertValue(jsonNode.get("_source"), BbsIndex.class);
? ? ? ? ? ? index.setContent(jsonNode.get("highlight").get("content").get(0).asText());
? ? ? ? ? ?//獲取數(shù)據(jù)庫(kù)相關(guān)信息并set到indexObject對(duì)象中
? ? ? ? ? if(index.getTopicId() !=null) {
????????????????IndexObject indexObject =null;
? ? ? ????????? BbsTopic topic =bbsService.getTopic(index.getTopicId());
? ? ? ????????? BbsUser user = topic.getUser();
? ? ? ? ? ? ? ?BbsModule module = topic.getModule();
? ? ? ? ? ? ? ? ? .......................................省略其他
最后通過(guò)ModelAndView渲染到頁(yè)面
?2.5贷痪、帖子刪除
帖子刪除同樣用到了spring AOP環(huán)繞通知技術(shù)幻妓,這里AOP底層同樣是cjlib動(dòng)態(tài)代理,通過(guò)掃描注解獲取索引的注解集合劫拢,通過(guò)id從數(shù)據(jù)庫(kù)查早相應(yīng)信息后用發(fā)帖時(shí)相同的規(guī)則MD5加密肉津,刪除數(shù)據(jù)庫(kù)bbs_topic、bbs_reply舱沧、bbs_post表的相關(guān)數(shù)據(jù)妹沙,最后通過(guò)id刪除Elasticsearch服務(wù)上存儲(chǔ)的值
通過(guò)索引刪除Elasticsearch對(duì)應(yīng)的數(shù)據(jù)
2.6、回復(fù)帖子狗唉、置頂、精華模塊
回復(fù)帖子置頂和設(shè)置精華功能模塊實(shí)現(xiàn)與發(fā)帖和刪帖類似涡真,同樣是spring AOP環(huán)繞通知分俯,回帖對(duì)bbs_topic表的post_count(回帖總數(shù))進(jìn)行統(tǒng)計(jì)加以肾筐,用戶和帖子關(guān)聯(lián)表bbs_message對(duì)應(yīng)數(shù)據(jù)插入,內(nèi)容表bbs_post插入回帖數(shù)據(jù)同時(shí)更改用戶表的等級(jí)等相關(guān)信息缸剪,Elasticsearch服務(wù)上對(duì)應(yīng)索引的值也做相應(yīng)更改
2.7吗铐、注冊(cè)、退出
注冊(cè)時(shí)password使用MD5加密杏节,退出移除session
以上就是整個(gè)bbs論壇系統(tǒng)后端的分析唬渗,前端部分很基礎(chǔ),所有的請(qǐng)求都是ajax請(qǐng)求
三奋渔、學(xué)習(xí)目的
1镊逝、在分布式系統(tǒng)尤其一些電商核心業(yè)務(wù)交易系統(tǒng)的設(shè)計(jì)都需要實(shí)時(shí)大數(shù)據(jù)搜索分析,比如嫉鲸,商品中心的上千萬(wàn)的數(shù)據(jù)需要實(shí)時(shí)搜索撑蒜,再到海量的在線訂單實(shí)時(shí)查詢都需要用到搜索,在一些DevOps的工具中都需要提供強(qiáng)大的實(shí)時(shí)搜索功能玄渗,elasticsearch就是一套很好的解決方案它可以幫助你用前所未有的速度去處理大規(guī)模數(shù)據(jù)座菠。
2、beetl模板也是我們?cè)趙eb開(kāi)發(fā)中經(jīng)常使用的藤树,他可生成特定格式的文檔使用戶界面與業(yè)務(wù)數(shù)據(jù)分離浴滴。