您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Nebula Graph和SpringBoot環(huán)境連接和查詢怎么實(shí)現(xiàn)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Nebula Graph和SpringBoot環(huán)境連接和查詢怎么實(shí)現(xiàn)”吧!
Nebula Graph 是一款開源的、分布式的、易擴(kuò)展的原生圖數(shù)據(jù)庫,能夠承載包含數(shù)千億個(gè)點(diǎn)和數(shù)萬億條邊的超大規(guī)模數(shù)據(jù)集,并且提供毫秒級查詢。
當(dāng)前Nebula Graph的最新版本是3.2.1, 根據(jù)官方的文檔進(jìn)行配置
支持分布式. 相對于Neo4j, TigerGraph這些圖數(shù)據(jù)庫, Nebula 是面向分布式設(shè)計(jì)的, 因此對集群的支持比較完備, 在規(guī)模上上限要高很多. 在實(shí)際項(xiàng)目中存儲了180億的點(diǎn)邊, 這個(gè)數(shù)量對于Neo4j和TigerGraph是比較困難的.
支持圖空間. 各個(gè)圖空間的ID是互不干擾的, 但是在同一個(gè)圖空間里ID的類型和長度必須一致. 注意這個(gè)一致約束的是所有的點(diǎn)和邊. Nebula 可以使用int64作為ID, 也可以用字符串, 但是字符串需要指定一個(gè)長度, 例如64個(gè)字節(jié). 相對于只能用長整數(shù)的Neo4j, ID設(shè)計(jì)上更自由靈活.
點(diǎn)對應(yīng)的類型叫TAG, 邊對應(yīng)的類型叫EDGE
TAG和EDGE都會對應(yīng)一組的屬性(map, 或者說dict)
一個(gè)點(diǎn)可以對多個(gè)TAG, 每個(gè)TAG一組屬性, 多組屬性. 項(xiàng)目中建議一開始不要用多TAG, 在整個(gè)圖結(jié)構(gòu)穩(wěn)定后, 再做合并
一個(gè)邊只對應(yīng)一個(gè)EDGE, 一組屬性
Nebula 用的是自定義的查詢語法 GQL, 和 cypher 語法基本一樣
除了點(diǎn)邊的ID和關(guān)聯(lián)關(guān)系外, 只有帶索引的屬性可以查詢. 這點(diǎn)和其它圖數(shù)據(jù)庫不一樣, 其它數(shù)據(jù)庫即使沒有索引, 慢是慢點(diǎn)但是不報(bào)錯(cuò), Nebula直接給你返回錯(cuò)誤.
對于返回?cái)?shù)量較大的查詢, Nebula會強(qiáng)制查詢必須帶limit
Nebula 單節(jié)點(diǎn)穩(wěn)定性是有問題的, 在3.2.1版本中觀察到偶爾會出現(xiàn)服務(wù)自行退出, 如果在生產(chǎn)環(huán)境使用, 需要有后臺監(jiān)控進(jìn)行心跳檢測和自動啟動
下面列出一些常用的查詢
-- 列出圖空間 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會報(bào)錯(cuò)
match (v:ADDRESS)-[e]-() where id(v)==\"ADD:82388116\" return v,e limit 100
在上面的鏈接中, 提供了最小的配置和測試代碼
對于Nebula Graph 3.2.1, 需要使用3.0.0的版本. client的每個(gè)版本只能對應(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)建會話, 執(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)建會話時(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是逗號分隔的地址端口列表, 例如 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)用啟動時(shí)讀取配置, 創(chuàng)建 NebulaPool, 并實(shí)例化 NebulaSessionFactory, destroyMethod = "close"
, 這個(gè)表示在項(xiàng)目shutdown時(shí)會調(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();
對于邊, 需要使用結(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");
對于點(diǎn), ID就是String
NebulaResult<String> res2 = graphService.query("t_test1", "MATCH (v:player) RETURN v");
到此,相信大家對“Nebula Graph和SpringBoot環(huán)境連接和查詢怎么實(shí)現(xiàn)”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。