Cypher 概述與基本語法
1.1 Cypher概述
Cypher是什麼
Cypher是一種宣告式圖資料庫查詢語言,它具有豐富的表現力,能高效地查詢和更新圖資料。
Cypher借鑑了SQL語言的結構——查詢可由各種各樣的語句組合。
Cypher索引、約束、統計
索引
建立索引
使用CREATE INDEX ON可以在擁有某個標籤的所有節點的某個屬性上建立索引。注意,索引是在後臺建立,並不能立刻就生效。
CREATE INDEX ON :Person(name)
本例在擁有Person標籤的所有節點的name屬性上建立了索引。
刪除索引
使用DROP INDEX可以刪除擁有某個標籤的所有節點的某個屬性上的索引。
DROP INDEX ON :Person(name)
使用索引
通常不需要在查詢中指出使用哪個索引,Cypher自己會決定。例
MATCH (person:Person { name: 'Andres' })
RETURN person
在WHERE等式中使用索引
在WHERE語句中的對索引的屬性進行相等比較時,索引將自動被使用。
MATCH (person:Person)
WHERE person.name = 'Andres'
RETURN person
使用WHERE不等式中使用索引
在WHERE語句中的對索引的屬性進行不等(範圍)比較時,索引將自動被使用。
MATCH (person:Person) WHERE person.name > 'B' RETURN person
在 IN中使用索引
下面查詢中針對 person.name的IN斷言將使用Person(name)索引。
MATCH (person:Person)
WHERE person.name IN ['Andres', 'Mark']
RETURN person
在STARTS WITH中使用索引
下面的查詢語句在針對person.name的STARTS WITH斷言將使用Person(name)索引。
查詢
MATCH (person:Person)
WHERE person.name STARTS WITH 'And'
RETURN person
在檢查屬性存在性時使用索引
下面查詢中的has(p.name)斷言將使用Person(name)索引。
MATCH (p:Person)
WHERE exists(p.name)
RETURN p
2.約束
Neo4j通過使用約束來保證資料完整性。約束可應用於節點或者關係。可以建立節點屬性的唯一性約束,也可以建立節點和關係的屬性存在性約束。
可以使用屬性的存在性約束確保擁有特定標籤的所有節點或者擁有特定型別的所有關係的屬性是存在的。所有的試圖建立新的沒有該屬性的節點或關係,以及試圖刪除強制屬性的查詢都將失敗。注意:只有Neo4j企業版才具有屬性存在性約束這個高階功能。
可以對某個給定的標籤新增多個約束,也可以將唯一性約束和存在性約束同時新增到同一個屬性上。
在屬性上新增唯一性約束的時候,同時也會自動為該屬性新增一個索引。因此,不能單獨地新增這樣一個索引。
節點屬性的唯一性約束
使用IS UNIQUE語法建立約束,它能確保資料庫中擁有特定標籤和屬性值的節點是唯一的。
CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE
刪除唯一性約束
使用DROP CONSTRAINT可以刪除資料庫中的一個約束。
DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE
建立遵從屬性唯一性約束的節點
建立一個數據庫中還不存在的isbn的Book節點。
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
建立違背屬性唯一性約束的節點
建立一個數據庫中已經存在的isbn的節點。
查詢
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
這種情況下,節點將建立失敗。
錯誤訊息
Node 0 already exists with label Book and property "isbn"=[1449356265]
因為衝突的節點而建立屬性唯一性約束失敗
當資料庫中已經有兩個Book節點擁有相同的isbn號時,在Book節點的isbn屬性上建立屬性唯一性約束。
CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE
這種情況下約束將建立失敗,因為它與已有的資料衝突。可以選擇建立索引或者移除衝突的節點然後再重新建立約束。
節點屬性存在性約束
使用ASSERT exists(variable.propertyName)建立約束,可確保有指定標籤的所有節點都有一個特定的屬性。
CREATE CONSTRAINT ON (book:Book) ASSERT exists(book.isbn)
刪除節點屬性存在性約束
使用DROP CONSTRAINT可以從資料庫中移除一個約束。
DROP CONSTRAINT ON (book:Book) ASSERT exists(book.isbn)
建立遵從屬性存在性約束的節點
建立一個存在isbn屬性的Book節點。
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
建立違背屬性存在性約束的節點
在:Book(isbn)有存在性約束的情況下,試圖建立一個沒有isbn屬性的Book節點。
CREATE (book:Book { title: 'Graph Databases' })
錯誤訊息
Node 1 with label "Book" must have the property "isbn" due to a constraint
刪除有存在性約束的節點屬性
在:Book(isbn)有存在性約束的情況下,試圖從一個已存在的Book節點移除isbn屬性。
MATCH (book:Book { title: 'Graph Databases' })
REMOVE book.isbn
這種情況下,移除屬性將失敗。
錯誤訊息
Node 0 with label "Book" must have the property "isbn" due to a constraint
因已存在的節點而建立節點屬性存在性約束失敗
當資料庫中存在Book節點沒有isbn屬性時,試圖在Book標籤節點的isbn屬性上建立屬性存在性約束。
CREATE CONSTRAINT ON (book:Book) ASSERT exists(book.isbn)
這種情況因為與已存在的資料衝突,因此約束建立失敗。可以選擇移除衝突的節點,然後再重新建立約束。
錯誤訊息
Unable to create CONSTRAINT ON ( book:Book ) ASSERT exists(book.isbn):
Node(0) with label Book
has no value for property isbn
關係屬性存在性約束
使用ASSERT exists(variable.propertyName)建立約束,可確保特定型別的所有關係都有一個特定的屬性。
CREATE CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day)
刪除關係屬性存在性約束
使用DROP CONSTRAINT從資料庫中移除一個約束。
DROP CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day)
建立遵從屬性存在性約束的關係
建立一個存在day屬性的LIKED關係。
MATCH (TomH:Person {name:'Tom Hanks', born:1956}), (book:Book { isbn: '1449356265', title: 'Graph Databases' })
CREATE (TomH)-[like:LIKED { day: 'yesterday' }]->(book)
return TomH,book
建立違背屬性存在性約束的關係
在有:LIKED(day)存在性約束的情況下,試圖建立一個沒有day屬性的LIKED關係。
MATCH (Nora :Person {name:'Nora Ephron'}), (book:Book { isbn: '1449356265', title: 'Graph Databases' })
CREATE (Nora)-[like:LIKED]->(book)
return Nora,book
這種情況下,關係將建立是吧。
錯誤訊息
Relationship 1 with type "LIKED" must have the property "day" due to a constraint
移除具有存在性約束的關係屬性
有:LIKED(day)存在性約束的情況下,試圖從一個已有LIKED關係中移除day屬性。
MATCH (TomH :Person {name:'Tom Hanks'})-[like:LIKED]-> (book:Book { isbn: '1449356265', title: 'Graph Databases' })
REMOVE like.day
錯誤訊息
Relationship 0 with type "LIKED" must have the property "day" due to a constraint
因已存在的關係而建立關係屬性存在性約束失敗
當資料庫中存在LIKED關係沒有day屬性時,試圖在LIKED關係的day屬性上建立屬性存在性約束。
CREATE CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day)
這種情況因為與已存在的資料衝突,因此約束建立失敗。可以選擇移除衝突的關係,然後再重新建立約束。
錯誤訊息
Unable to create CONSTRAINT ON ()-[ liked:LIKED ]-() ASSERT exists(liked.day):
Relationship(0) with type LIKED
has no value for property day
3.統計
當執行一個Cypher查詢時,它將先編譯為一個執行計劃(execution plan),該計劃可以執行並響應查詢。為了給查詢提供一個高效的計劃,Neo4j需要資料庫的資訊,如schema有什麼索引和約束存在?Neo4j也使用統計資訊來保持資料庫優化執行計劃。有了這些資訊,Neo4j就能決定採用哪種模式將獲得最好的執行計劃。
Neo4j通過對資料取樣來獲得如下統計資訊:
擁有特定標籤的節點的數量
每個索引的可選擇性
按型別分的關係的數量
以擁有指定標籤的節點開始或者結束的關係,按型別分各自的數量
Neo4j以兩種方式來保持這些統計資訊的更新。以標籤數量為例,每當設定或者刪除一個節點的標籤的時候,這些數量都會被更新。Neo4j需要掃描所有索引以獲得可選擇的數量。
配置選項
當上述統計資訊發生變化時,快取的執行計劃將被重新生成。下面的配置項可以控制執行計劃的更新。
dbms.index_sampling.background_enabled
控制當需要更新時索引是否會自動重新取樣。
dbms.index_sampling.update_percentage
控制多大比例的索引被更新後才觸發新的取樣
cypher.statistics_divergence_threshold
多少統計資訊發生變化後當前的執行計劃就被認為過時了(需要重新生成執行計劃)。引數值0.0意味著有變化就更新,1.0意味著永遠都不更新。
手動索引取樣
重新取樣可使用內嵌的db.resampleIndex()和db.resampleOutdatedIndexes()兩個內嵌過程來觸發。
下面是觸發重取樣的例子:
CALL db.resampleIndex(":Person(name)");
CALL db.resampleOutdatedIndexes();