近期因爲工做須要,須要作一我的口關係大圖的存儲及檢索方案,咱們主要的數據對象有:人口(年齡,身份證號碼,性別。。) ;學校信息(學校地址,學校名稱,學校級別,學校下邊的年級班級。。);就任信息(公司名稱,公司地址,公司企業信用代碼。。)以及論文發佈信息(論文主題,參與人,發佈時間)java
經過關係表中存儲的ID來進行關聯;注意設置的時候是將與一我的全部關聯的信息進行抽象出來並劃分爲五個對象,即上圖的五個表單。 經過上圖咱們能夠和明確的看出來有以下幾種關係: 人 -就讀->學校;web
人 -發佈論文->論文; 人-就任->企業; 人-居住->家庭;這四類的外部關係(以單我的節點爲參照),另一方面,人根根之間也是存在關係的,好比剛開始提到的同事,同窗類的,那麼這類屬於內部關係,內部關係咱們通常須要經過外部關係區分,好比張三和李四隻有在同一個班級上學,那麼菜算得上同窗吧,只有在同一個公司就任纔算得上同事吧.。這樣的話,咱們數據庫設計出來,邏輯也梳理出來,尤爲是內部關係外部關係。redis
我項目採用的是spring+springMvc的環境。spring
注意我是將neo4j的配置及實體放在一塊的,至於其餘的邏輯操做在其餘的包中。數據庫
部分代碼:瀏覽器
設置Configuration屬性到neo4j中
package com.audaque.module.graphData.neo4j.config; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.*; import org.springframework.data.neo4j.config.Neo4jConfiguration; import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; import org.springframework.transaction.annotation.EnableTransactionManagement; /*** *@ClassName AppConfiguration *@desc TODO *@Author xxxxxx *@Date 2019/5/17 0017 下午 10:57 *@version 1.0.1 **/ @Configuration @EnableNeo4jRepositories(basePackages = "com.audaque.module.graphData.neo4j.repos") @EnableTransactionManagement @ComponentScan("com.audaque.module.graphData.neo4j.config") public class AppConfiguration extends Neo4jConfiguration {
@Autowired private org.neo4j.ogm.config.Configuration config; @Bean public org.neo4j.ogm.config.Configuration getConfiguration() { return config; } @Override @Bean public SessionFactory getSessionFactory() { // with domain entity base package(s) return new SessionFactory(config,"com.audaque.module.graphData.neo4j.model"); } @Override @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public Session getSession() throws Exception { return super.getSession(); } }
動態設置屬性到Configuration中屬性到中
package com.audaque.module.graphData.neo4j.config; import org.neo4j.ogm.authentication.UsernamePasswordCredentials; import org.neo4j.ogm.config.Configuration; import static org.neo4j.ogm.config.DriverConfiguration.CREDENTIALS; import static org.neo4j.ogm.config.DriverConfiguration.DRIVER; import static org.neo4j.ogm.config.DriverConfiguration.URI; /*** *@ClassName MyConfiguration *@desc 自定義bean動態配置neo4j數據源。 *@Author xiaokang.ma@audaque.com *@Date 2019/5/24 9:14 *@version 1.0.1 **/ public class MyConfiguration extends Configuration { public MyConfiguration(String driverClass,String driverURL,String userName,String password){ super.set(DRIVER[0],driverClass); super.set(URI[0],driverURL); super.set(CREDENTIALS[0],new UsernamePasswordCredentials(userName, password)); } }
PaperInfoRepository配置(實體的保存):
package com.audaque.module.graphData.neo4j.repos; import com.audaque.module.graphData.neo4j.model.PaperInfo; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.stereotype.Repository; /*** *@ClassName PaperInfoRepository *@desc 論文信息接口 *@Author xxxx *@Date 2019/5/17 0017 下午 5:43 *@version 1.0.1 **/ @Repository public interface PaperInfoRepository extends GraphRepository<PaperInfo> { }
內部關係的Repository我採用的自定義查詢語句查詢,以下:緩存
package com.audaque.module.graphData.neo4j.repos; import com.audaque.module.graphData.neo4j.model.Neo4jRelationInfo; import org.neo4j.consistency.store.paging.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.repository.GraphRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.List; import java.util.Map; /*** *@ClassName Neo4jRelationInfoRepository *@desc 八大關係型數據。 *@Author xxxx *@Date 2019/5/17 0017 下午 5:43 *@version 1.0.1 **/ @Repository public interface Neo4jRelationInfoRepository extends GraphRepository<Neo4jRelationInfo> { /** * pe - pe 鄰居關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:鄰居{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_LJ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> pe 同窗關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:同窗{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_TX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> pe 校友關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) <- [r:校友{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_XY(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> li 居住關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:li) where a.ids={fromId} and b.ids={toId} merge (a) - [r:居住{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_JUZ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> pe 同事關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:同事{direction:{direction}}]-(b)") Neo4jRelationInfo generateGraphDataRelation_TS(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> wu 就任關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:wu) where a.ids={fromId} and b.ids={toId} merge (a) - [r:就任{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_JZGX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> sc 畢業院校關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:sc) where a.ids={fromId} and b.ids={toId} merge (a) - [r:畢業{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_BYYX(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> pa 論文參與關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pa) where a.ids={fromId} and b.ids={toId} merge (a) - [r:參與者{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_PA(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * pe -> pa 論文合做關係 * @param fromId * @param toId * @return */ @Query(value = "match(a:pe),(b:pe) where a.ids={fromId} and b.ids={toId} merge (a) - [r:論文合做{direction:{direction}}]->(b)") Neo4jRelationInfo generateGraphDataRelation_LWHZ(@Param("fromId") String fromId, @Param("toId") String toId, @Param("direction") String direction); /** * web查詢接口,查詢與當前節點爲1的數據---根據節點編號,適用於登陸第二次節點點擊以後的查詢 */ @Query(value = "match(a)-[r]-(b) where a.ids={qid} return a,r,b") List<Map<String,String>> queryRelationDataByIds(@Param("qid") String qid); /** * web查詢接口,查詢與當前節點爲1的數據 */ @Query(value = "match(a:pe)-[r]-(b) where a.cardNo={cardNo} return a,r,b") List<Map<String,String>> queryRelationDataByCardNo(@Param("cardNo") String cardNo); }
bean配置:網絡
<!--初始化配置的Config-->
<bean id="myConfiguration" class="com.audaque.module.graphData.neo4j.config.MyConfiguration">
<constructor-arg name="driverClass" value="org.neo4j.ogm.drivers.http.driver.HttpDriver" index="0" type="java.lang.String"/>
<constructor-arg name="driverURL" value="http://10.229.183.142:7474" index="1" type="java.lang.String"/>
<constructor-arg name="userName" value="neo4j" index="2" type="java.lang.String"/>
<constructor-arg name="password" value="123456" index="3" type="java.lang.String"/>
</bean>
經過上述的簡單配置就在spring環境中搭建到了neo4j。session
/** * 初始化圖數據須要的人員節點數據 * * @param personBaseInfoRepository * @return */ @Override public void resetNeo4jPersonBaseInfoNode(PersonBaseInfoRepository personBaseInfoRepository) { //查詢人員節點列表 List<PersonBaseInfo> personBaseInfos = graphDataDao.queryPersonBaseInfo(); for (PersonBaseInfo personBaseInfo :personBaseInfos){ try { personBaseInfoRepository.save(personBaseInfo); } catch (Throwable t){ //生成節點異常時,繼續跳過。 continue; } } }
初始化內部外部關心數據,代碼以下:oracle
/********************************************開始初始化節點間關係的數據**********************************************/ /** * 初始化五大節點間的關係數據。 * @param neo4jRelationInfoRepository * @return */ @Override public void resetAllRelationInfo(Neo4jRelationInfoRepository neo4jRelationInfoRepository) { long start = System.currentTimeMillis(); //初始化人員-->工做關係,人員->學校;人員->論文;人員->住房信息4個主關係的數據。 List<Map<String,Object>> lists = graphDataDao.queryEntityRelationInfo(); //計算內部關係信息 ,同事,校友,鄰居,合做者關係。 Map<String,List<String>> schoolFriendMap = new ConcurrentHashMap<>(); //校友 Map<String,List<String>> workTogetherMap = new ConcurrentHashMap<>(); //同事 Map<String,List<String>> neighborMap = new ConcurrentHashMap<>(); //鄰居 Map<String,List<String>> collaboratorMap = new ConcurrentHashMap<>();//論文合做者 for(Map<String,Object> map :lists){ String userId = ""; //獲取人員信息 if(StringUtils.isNoneEmpty((String)map.get("USERID")) && StringUtils.isNoneEmpty((String)map.get("USERNAME"))){ //取工做單位字段 UNIT_ID , UNIT_NAME if(StringUtils.isNoneEmpty((String)map.get("UNIT_ID")) && StringUtils.isNoneEmpty((String)map.get("UNIT_NAME"))){ //插入就任關係 neo4jRelationInfoRepository.generateGraphDataRelation_JZGX((String)map.get("USERID"),(String)map.get("UNIT_ID"),(String)map.get("USERID")+"->"+(String)map.get("UNIT_ID")); //處理同事關係 if(workTogetherMap.containsKey((String)map.get("UNIT_ID"))){ List<String> list = workTogetherMap.get((String)map.get("UNIT_ID")); if(!list.contains((String)map.get("USERID"))){ list.add((String)map.get("USERID")); workTogetherMap.put((String)map.get("UNIT_ID"),list); } }else{ List<String> lis = new ArrayList<String>(); lis.add((String)map.get("USERID")); workTogetherMap.put((String)map.get("UNIT_ID"),lis); } } //取學校字段 SCHOOL_ID , SCHOOL_NAME if(StringUtils.isNoneEmpty((String)map.get("SCHOOL_ID")) && StringUtils.isNoneEmpty((String)map.get("SCHOOL_NAME"))){ //插入畢業院校 neo4jRelationInfoRepository.generateGraphDataRelation_BYYX((String)map.get("USERID"),(String)map.get("SCHOOL_ID"),(String)map.get("USERID")+"->"+(String)map.get("SCHOOL_ID")); //處理校友關係 if(schoolFriendMap.containsKey((String)map.get("SCHOOL_ID"))){ List<String> list = schoolFriendMap.get((String)map.get("SCHOOL_ID")); if(!list.contains((String)map.get("USERID"))){ list.add((String)map.get("USERID")); schoolFriendMap.put(this.schoolFriend,list); } }else{ List<String> lis = new ArrayList<String>(); lis.add((String)map.get("USERID")); schoolFriendMap.put((String)map.get("SCHOOL_ID"),lis); } } //取住房信息字段 HOUSE_ID , HOUSE_ADDR if(StringUtils.isNoneEmpty((String)map.get("HOUSE_ID")) && StringUtils.isNoneEmpty((String)map.get("HOUSE_ADDR"))){ //插入畢業院校 neo4jRelationInfoRepository.generateGraphDataRelation_JUZ((String)map.get("USERID"),(String)map.get("HOUSE_ID"),(String)map.get("USERID")+"->"+(String)map.get("HOUSE_ID")); //處理鄰居關係 if(neighborMap.containsKey((String)map.get("HOUSE_ID"))){ List<String> list = neighborMap.get((String)map.get("HOUSE_ID")); if(!list.contains((String)map.get("USERID"))){ list.add((String)map.get("USERID")); neighborMap.put((String)map.get("HOUSE_ID"),list); } }else{ List<String> lis = new ArrayList<String>(); lis.add((String)map.get("USERID")); neighborMap.put((String)map.get("HOUSE_ID"),lis); } } //取論文信息字段 LW_ID , LW_TITLE if(StringUtils.isNoneEmpty((String)map.get("LW_ID")) && StringUtils.isNoneEmpty((String)map.get("LW_TITLE"))){ //插入畢業院校 neo4jRelationInfoRepository.generateGraphDataRelation_PA((String)map.get("USERID"),(String)map.get("LW_ID"),(String)map.get("USERID")+"->"+(String)map.get("LW_ID")); //處理合做者關係 if(collaboratorMap.containsKey((String)map.get("LW_ID"))){ List<String> list = collaboratorMap.get((String)map.get("LW_ID")); if(!list.contains((String)map.get("USERID"))){ list.add((String)map.get("USERID")); collaboratorMap.put((String)map.get("LW_ID"),list); } }else{ List<String> lis = new ArrayList<String>(); lis.add((String)map.get("USERID")); collaboratorMap.put((String)map.get("LW_ID"),lis); } } } } //開始剔除長度爲1的數據,沒有對應的關係。 schoolFriendMap.forEach((key,value) -> { //獨立個體沒法造成關係,剔除 if(((List<String>)value).size()<=1){ schoolFriendMap.remove(key); } }); workTogetherMap.forEach((key,value) -> { //獨立個體沒法造成關係,剔除 if(((List<String>)value).size()<=1){ workTogetherMap.remove(key); } }); neighborMap.forEach((key,value) -> { //獨立個體沒法造成關係,剔除 if(((List<String>)value).size()<=1){ neighborMap.remove(key); } }); collaboratorMap.forEach((key,value) -> { //獨立個體沒法造成關係,剔除 if(((List<String>)value).size()<=1){ collaboratorMap.remove(key); } }); //開始遍歷校友關係數據,生成節點。 schoolFriendMap.forEach((key,value) -> { List<String> li = (List<String>)value; for(int st = 0;st<li.size() ; st++){ for(int ss = li.size()-1;ss>st;ss--){ System.out.println("生成關係"+li.get(st)+"->"+li.get(ss)); neo4jRelationInfoRepository.generateGraphDataRelation_XY(li.get(st),li.get(ss),"<->"); } } }); //同事關係 workTogetherMap.forEach((key,value) -> { List<String> li = (List<String>)value; for(int st = 0;st<li.size() ; st++){ for(int ss = li.size()-1;ss>st;ss--){ System.out.println("生成關係"+li.get(st)+"->"+li.get(ss)); neo4jRelationInfoRepository.generateGraphDataRelation_TS(li.get(st),li.get(ss),"<->"); } } }); //鄰居關係 neighborMap.forEach((key,value) -> { List<String> li = (List<String>)value; for(int st = 0;st<li.size() ; st++){ for(int ss = li.size()-1;ss>st;ss--){ System.out.println("生成關係"+li.get(st)+"->"+li.get(ss)); neo4jRelationInfoRepository.generateGraphDataRelation_LJ(li.get(st),li.get(ss),"<->"); } } }); //合做者關係 collaboratorMap.forEach((key,value) -> { List<String> li = (List<String>)value; for(int st = 0;st<li.size() ; st++){ for(int ss = li.size()-1;ss>st;ss--){ System.out.println("生成關係"+li.get(st)+"->"+li.get(ss)); neo4jRelationInfoRepository.generateGraphDataRelation_LWHZ(li.get(st),li.get(ss),"<->"); } } }); System.out.println("總共花費時間:" + (System.currentTimeMillis() - start) + "ms"); }
而後Control中對外暴露三個接口, 1.初始化節點, 2.初始化關係。 就能夠了。
而後經過瀏覽器直接能夠看到圖數據的結構,分享下我作出來的效果。
至此一個從數據庫存入到neo4j數據庫中,而後能夠對外開放一個查詢的接口,返回與之有關係而且深度爲1的數據,方便其餘系統展現,接口以下:
/** * web查詢接口,查詢與當前節點爲1的數據---根據節點編號,適用於登陸第二次節點點擊以後的查詢。 */ @Query(value = "match(a)-[r]-(b) where a.ids={qid} return a,r,b") List<Map<String,String>> queryRelationDataByIds(@Param("qid") String qid); /** * web查詢接口,用戶登陸以後第一次查詢,需根據身份證號碼查詢。第二次查詢就直接會調用上一個查詢。 */ @Query(value = "match(a:pe)-[r]-(b) where a.cardNo={cardNo} return a,r,b") List<Map<String,String>> queryRelationDataByCardNo(@Param("cardNo") String cardNo);