您好,登錄后才能下訂單哦!
這篇文章主要講解了“Nebula Graph和SpringBoot環(huán)境連接及查詢?nèi)绾螌?shí)現(xiàn)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Nebula Graph和SpringBoot環(huán)境連接及查詢?nèi)绾螌?shí)現(xiàn)”吧!
Nebula Graph 是一款開源的、分布式的、易擴(kuò)展的原生圖數(shù)據(jù)庫(kù),能夠承載包含數(shù)千億個(gè)點(diǎn)和數(shù)萬(wàn)億條邊的超大規(guī)模數(shù)據(jù)集,并且提供毫秒級(jí)查詢。
當(dāng)前Nebula Graph的最新版本是3.2.1, 根據(jù)官方的文檔進(jìn)行配置
支持分布式. 相對(duì)于Neo4j, TigerGraph這些圖數(shù)據(jù)庫(kù), Nebula 是面向分布式設(shè)計(jì)的, 因此對(duì)集群的支持比較完備, 在規(guī)模上上限要高很多. 在實(shí)際項(xiàng)目中存儲(chǔ)了180億的點(diǎn)邊, 這個(gè)數(shù)量對(duì)于Neo4j和TigerGraph是比較困難的.
支持圖空間. 各個(gè)圖空間的ID是互不干擾的, 但是在同一個(gè)圖空間里ID的類型和長(zhǎng)度必須一致. 注意這個(gè)一致約束的是所有的點(diǎn)和邊. Nebula 可以使用int64作為ID, 也可以用字符串, 但是字符串需要指定一個(gè)長(zhǎng)度, 例如64個(gè)字節(jié). 相對(duì)于只能用長(zhǎng)整數(shù)的Neo4j, ID設(shè)計(jì)上更自由靈活.
點(diǎn)對(duì)應(yīng)的類型叫TAG, 邊對(duì)應(yīng)的類型叫EDGE
TAG和EDGE都會(huì)對(duì)應(yīng)一組的屬性(map, 或者說dict)
一個(gè)點(diǎn)可以對(duì)多個(gè)TAG, 每個(gè)TAG一組屬性, 多組屬性. 項(xiàng)目中建議一開始不要用多TAG, 在整個(gè)圖結(jié)構(gòu)穩(wěn)定后, 再做合并
一個(gè)邊只對(duì)應(yīng)一個(gè)EDGE, 一組屬性
Nebula 用的是自定義的查詢語(yǔ)法 GQL, 和 cypher 語(yǔ)法基本一樣
除了點(diǎn)邊的ID和關(guān)聯(lián)關(guān)系外, 只有帶索引的屬性可以查詢. 這點(diǎn)和其它圖數(shù)據(jù)庫(kù)不一樣, 其它數(shù)據(jù)庫(kù)即使沒有索引, 慢是慢點(diǎn)但是不報(bào)錯(cuò), Nebula直接給你返回錯(cuò)誤.
對(duì)于返回?cái)?shù)量較大的查詢, Nebula會(huì)強(qiáng)制查詢必須帶limit
Nebula 單節(jié)點(diǎn)穩(wěn)定性是有問題的, 在3.2.1版本中觀察到偶爾會(huì)出現(xiàn)服務(wù)自行退出, 如果在生產(chǎn)環(huán)境使用, 需要有后臺(tái)監(jiān)控進(jìn)行心跳檢測(cè)和自動(dòng)啟動(dòng)
下面列出一些常用的查詢
-- 列出圖空間 SHOW SPACES; -- 列出tag(點(diǎn)類型)和edge(邊類型), 需要先 USE 一個(gè)圖空間 SHOW TAGS; SHOW EDGES;
列出某一類型的點(diǎn)和邊
MATCH ()-[e:follow]-() RETURN e MATCH (v:player) RETURN v
帶條件的查詢, 在結(jié)果數(shù)量較多時(shí)必須帶limit, 否則Nebula會(huì)報(bào)錯(cuò)
match (v:ADDRESS)-[e]-() where id(v)==\"ADD:82388116\" return v,e limit 100
在上面的鏈接中, 提供了最小的配置和測(cè)試代碼
對(duì)于Nebula Graph 3.2.1, 需要使用3.0.0的版本. client的每個(gè)版本只能對(duì)應(yīng)特定的一兩個(gè)服務(wù)端版本
<dependency> <groupId>com.vesoft</groupId> <artifactId>client</artifactId> <version>3.0.0</version> </dependency>
Java調(diào)用主要是三部分, 創(chuàng)建連接池, 創(chuàng)建會(huì)話, 執(zhí)行查詢
連接到地址127.0.0.1, 端口9669, 連接池大小100. 注意地址和端口是一個(gè)列表, Nebula是支持集群的. 連接時(shí)不需要用戶和密碼
NebulaPool pool = new NebulaPool(); try { NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig(); nebulaPoolConfig.setMaxConnSize(100); List<HostAddress> addresses = Arrays.asList(new HostAddress("127.0.0.1", 9669)); Boolean initResult = pool.init(addresses, nebulaPoolConfig); if (!initResult) { log.error("pool init failed."); return; } } catch () //...
創(chuàng)建會(huì)話時(shí)需要用戶名和密碼
Session session = pool.getSession("root", "nebula", false);
創(chuàng)建一個(gè)SPACE, 然后使用這個(gè)SPACE, 創(chuàng)建一個(gè)TAG person, 創(chuàng)建一個(gè)EDGE like
String createSchema = "CREATE SPACE IF NOT EXISTS test(vid_type=fixed_string(20)); " + "USE test;" + "CREATE TAG IF NOT EXISTS person(name string, age int);" + "CREATE EDGE IF NOT EXISTS like(likeness double)"; ResultSet resp = session.execute(createSchema); if (!resp.isSucceeded()) { log.error(String.format("Execute: `%s', failed: %s", createSchema, resp.getErrorMessage())); System.exit(1); }
添加一個(gè)點(diǎn)記錄
String insertVertexes = "INSERT VERTEX person(name, age) VALUES " + "'Bob':('Bob', 10), " + "'Lily':('Lily', 9), " + "'Tom':('Tom', 10), " + "'Jerry':('Jerry', 13), " + "'John':('John', 11);"; ResultSet resp = session.execute(insertVertexes); if (!resp.isSucceeded()) { log.error(String.format("Execute: `%s', failed: %s", insertVertexes, resp.getErrorMessage())); System.exit(1); }
查詢
String query = "GO FROM \"Bob\" OVER like " + "YIELD $^.person.name, $^.person.age, like.likeness"; ResultSet resp = session.execute(query); if (!resp.isSucceeded()) { log.error(String.format("Execute: `%s', failed: %s", query, resp.getErrorMessage())); System.exit(1); } printResult(resp);
<dependency> <groupId>com.vesoft</groupId> <artifactId>client</artifactId> <version>3.0.0</version> </dependency>
配合@Bean(destroyMethod = "close")
, 創(chuàng)建一個(gè)工廠類, 接收pool并實(shí)現(xiàn)close()方法
public class NebulaSessionFactory { private final NebulaPool pool; private final String username; private final String password; public NebulaSessionFactory(NebulaPool pool, String username, String password) { this.pool = pool; this.username = username; this.password = password; } public Session getSession() { try { return pool.getSession(username, password, false); } catch (NotValidConnectionException|IOErrorException|AuthFailedException|ClientServerIncompatibleException e) { throw new RuntimeException("Nebula session exception", e); } } public void close() { pool.close(); } }
為什么不直接將 NebulaPool 配置為Bean? 因?yàn)?Session 每次創(chuàng)建時(shí)需要帶用戶名密碼, 將密碼作為config注入到每個(gè)Service中肯定是大家都不愿意看到的.
這里的值如果不打算使用profile配置, 可以直接寫入
hosts是逗號(hào)分隔的地址端口列表, 例如 10.22.33.33:9669,10.22.33.34:9669
myapp: nebula: hosts: @nebula.hosts@ username: @nebula.username@ password: @nebula.password@ max-conn: @nebula.max-conn@
應(yīng)用啟動(dòng)時(shí)讀取配置, 創(chuàng)建 NebulaPool, 并實(shí)例化 NebulaSessionFactory, destroyMethod = "close"
, 這個(gè)表示在項(xiàng)目shutdown時(shí)會(huì)調(diào)用Bean的close方法釋放資源.
@Configuration public class NebulaGraphConfig { @Value("${myapp.nebula.hosts}") private String hosts; @Value("${myapp.nebula.max-conn}") private int maxConn; @Value("${myapp.nebula.username}") private String username; @Value("${myapp.nebula.password}") private String password; @Bean(destroyMethod = "close") public NebulaSessionFactory nebulaSessionFactory() { List<HostAddress> hostAddresses = new ArrayList<>(); String[] hostList = hosts.split(",[ ]*"); for (String host : hostList) { String[] hostParts = host.split(":"); if (hostParts.length != 2 || !hostParts[1].matches("\\d+")) { throw new RuntimeException("Invalid host name set for Nebula: " + host); } hostAddresses.add(new HostAddress(hostParts[0], Integer.parseInt(hostParts[1]))); } NebulaPoolConfig poolConfig = new NebulaPoolConfig(); poolConfig.setMaxConnSize(maxConn); NebulaPool pool = new NebulaPool(); try { pool.init(hostAddresses, poolConfig); } catch (UnknownHostException e) { throw new RuntimeException("Unknown Nebula hosts"); } return new NebulaSessionFactory(pool, username, password); } }
在 Service 中進(jìn)行調(diào)用
@Service @Slf4j public class GraphServiceImpl implements GraphService { @Autowired private NebulaSessionFactory sessionFactory; @Override public <T> NebulaResult<T> query(String graphSpace, String gql) { Session session = null; try { log.info("GQL: {}", gql); session = sessionFactory.getSession(); NebulaResult<Void> res = query(session, "USE " + graphSpace); if (!res.isSuccess() || res.getResults() == null || res.getResults().size() == 0) { log.error("Failed to use space:{}", graphSpace); return null; } if (!graphSpace.equals(res.getResults().get(0).getSpaceName())) { log.error("Failed to use space:{}, result:{}", graphSpace, res.getResults().get(0).getSpaceName()); return null; } return query(session, gql); } catch (IOErrorException e) { log.error(e.getMessage(), e); return null; } finally { if (session != null) { session.release(); } } } private <T> NebulaResult<T> query(Session session, String gql) throws IOErrorException { String json = session.executeJson(gql); return JacksonUtil.extractByType(json, new TypeReference<>() {}); } }
這里定義了 json 格式響應(yīng)的外層結(jié)構(gòu)
@Data public class NebulaResult<T> implements Serializable { private List<Error> errors; private List<Result<T>> results; @JsonIgnore public boolean isSuccess() { return (errors != null && errors.size() == 1 && errors.get(0).getCode() == 0); } @Data public static class Error implements Serializable { private int code; } @Data @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public static class Result<T> implements Serializable { private String spaceName; private List<Element<T>> data; private List<String> columns; private Error errors; private long latencyInUs; } @Data public static class Element<T> implements Serializable { private List<Meta<T>> meta; private List<Serializable> row; } @Data public static class Meta<T> implements Serializable { private String type; private T id; } }
內(nèi)層因?yàn)閰^(qū)分Edge和Vertex, 結(jié)構(gòu)不一樣. 如果是混合返回的結(jié)果, 可以用 Serializable
String gql = "match (v:ADDR)-[e]-() where id(v)==\"ADD:123123\" return v,e limit 100"; NebulaResult<Serializable> res = graphService.query("insurance", gql); log.info(JacksonUtil.compress(res)); Assertions.assertThat(res).isNotNull();
對(duì)于邊, 需要使用結(jié)構(gòu)化的ID
@Data @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public class EdgeId implements Serializable { private int ranking; private int type; private String dst; private String src; private String name; }
用這個(gè)結(jié)構(gòu)進(jìn)行查詢
NebulaResult<EdgeId> res3 = graphService.query("t_test1", "MATCH ()-[e:follow]-() RETURN e");
對(duì)于點(diǎn), ID就是String
NebulaResult<String> res2 = graphService.query("t_test1", "MATCH (v:player) RETURN v");
感謝各位的閱讀,以上就是“Nebula Graph和SpringBoot環(huán)境連接及查詢?nèi)绾螌?shí)現(xiàn)”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Nebula Graph和SpringBoot環(huán)境連接及查詢?nèi)绾螌?shí)現(xiàn)這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。