5、Schema和數據建模
每個JanusGraph都有一個schema,該schema由edge labels,property keys,和vertex組成.JanusGraph schema可以顯示(明確)定義,也可以隱式定義,鼓勵用戶在開發應用程式時顯示定義schema。
明確定義的schema是一個健壯的圖應用程序的重要組成部分,並且極大的改善軟件的協同開發。要注意的是,JanusGraph schema會隨着時間的推移而不斷改變,並且不會中斷正常的數據庫操作。擴展schema並不會拖慢查詢的速度,也不需要數據庫停機。
5.1 Defining Edge Labels(定義邊標簽)
鏈接兩個定點的邊都有一個定義關系的標簽。例如,頂點A和頂點B之間的邊標簽friend標志着兩人之間的友誼關系。
要定義邊標簽,請在打開的圖形或管理事務上調用MakeEdgeLabel ( String ),並提供邊標簽的名稱作為參數。邊標簽名稱在圖中必須唯一。方法會返回一個允許定義邊標簽多樣性的builder。邊標簽的多樣性定義了具有該標簽的邊的多樣性約束,既一對頂點間的最大邊個數。JanusGraph支持如下的多樣性。
5.1.1 Edge Lable Multiplicity(邊標簽的多樣性)
Multiplicity Settings(多樣性設置)
- MULTI:任意一對的頂點間允許多個標簽相同的邊。也就是說,該圖是關於這種邊緣標簽的多圖。邊的多樣性沒有約束。
- SIMPLE:任意一對頂點之間只允許一條此類標簽的邊。也就是說,該圖是關於標簽的簡單圖。確保對於給定的標簽和一對頂點邊是唯一的。
- MANY2ONE:在圖形中的任何頂點上最多允許此標簽的一個輸出邊,但不限制輸入邊個數。邊緣標簽mother是MANY2ONE多樣性的一個例子,因為每個人最多只有一個母親,但母親可以有多個孩子。
- ONE2MANY:在圖形中的任何頂點上最多允許此標簽的一個輸入邊,但不限制輸出邊。邊標簽winnerOf是ONE2MANY多樣性的一個例子,因為每個比賽最多只贏一個人,但一個人可以贏得多個比賽。
- ONE2ONE:在圖表的任何頂點上最多允許此標簽的一個輸入邊和一個輸出邊。邊標簽married是ONE2ONE多樣性的一個例子,因為一個人與另一個人結婚。
默認的多樣性是MULTI。通過調用make()構建器上的方法來完成邊標簽的定義,該構建器返回定義的邊標簽,如以下示例所示。
mgmt = graph.openManagement()
follow = mgmt.makeEdgeLabel('follow').multiplicity(MULTI).make()
mother = mgmt.makeEdgeLabel('mother').multiplicity(MANY2ONE).make()
mgmt.commit()
5.2 Defining Property Keys (定義屬性鍵)
頂點和邊的屬性是鍵值對.例如,屬性name='Daniel'具有鍵name和值'Daniel'。
屬性鍵是JanusGraph架構的一部分,可以約束允許的數據類型和值的Cardinality(基數)。
要定義屬性鍵,請調用makePropertyKey(String)打開的圖或管理事務器(management transaction),並提供屬性鍵的名稱作為參數。屬性鍵名稱在圖形中必須是唯一的,建議避免使用屬性名稱中的空格或特殊字符。此方法返回屬性鍵的構建器。
5.2.1 Property Key Data Type(屬性鍵數據類型)
使用dataType(Class)來定義屬性鍵的數據類型。JanusGraph將強制執行與關聯鍵的所有值,確人都具有已配置的數據類型,從而確保添加到圖中的數據有效。例如,可以定義為name鍵具有String數據類型。
定義數據類型Object.class,以便允許任何(可序列化)值與鍵關聯。但是,鼓勵盡可能使用具體的數據類型。配置的數據類型必須是實體類,而不是接口或抽象類。JanusGraph強制實現類相等,因此不允許添加已配置數據類型的子類。
JanusGraph支持以下數據類型。
表5.1 原生JanusGraph數據類型
| 名稱 | 描述 |
|---|---|
| String | 字符串 |
| Character | 單個字符 |
| Boolean | true或false |
| Byte | byte值 |
| Short | short值 |
| Integer | integer值 |
| Long | long值 |
| Float | 4字節浮點數 |
| Double | 8字節浮點數 |
| Date | 時間(java.util.Date) |
| Geoshape | 地理形狀,如點,圓或框 |
| UUID | 通用唯一標識符(java.util.UUID) |
5.2.2 Property Key Cardinality(屬性鍵的基數)
使用cardinality(Cardinality)定義與在任何給定的頂點的鍵關聯的值允許的基數。
基數的設置
- SINGLE:對於此鍵,每個元素最多允許一個值。換句話說,鍵→值映射對於圖中的所有元素都是唯一的。屬性鍵birthDate是SINGLE基數的一個例子,因為每個人只有一個出生日期。
- LIST:允許每個元素的任意數量的值用於此類鍵。換句話說,鍵與允許重復值的值列表相關聯。假設我們將傳感器建模為圖形中的頂點,則屬性鍵sensorReading是具有LIST基數的示例,以允許記錄大量(可能重復的)傳感器讀數。
- SET:允許多個但不重復的值用於此類鍵。換句話說,鍵與一組不重復的值相關聯。name如果我們想要捕獲個人的所有姓名(包括昵稱,婚前姓名等),則屬性鍵具有SET基數。
默認的基數是SINGLE。要注意的是,邊和屬性使用SINGLE作為基數,是不支持為為邊或屬性附加多個值。
mgmt = graph.openManagement()
birthDate = mgmt.makePropertyKey('birthDate').dataType(Long.class).cardinality(Cardinality.SINGLE).make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
sensorReading = mgmt.makePropertyKey('sensorReading').dataType(Double.class).cardinality(Cardinality.LIST).make()
mgmt.commit()
5.3 Relation Types(關系類型)
邊標簽和屬性鍵共同稱為關系類型。關系類型的名稱在圖中必須唯一,這意味着屬性鍵和邊緣標簽不能具有相同的名稱。JanusGraph API中有一些方法可以查詢是否存在或檢索包含屬性鍵和邊標簽的關系類型。
mgmt = graph.openManagement()
if (mgmt.containsRelationType('name'))
name = mgmt.getPropertyKey('name')
mgmt.getRelationTypes(EdgeLabel.class)
mgmt.commit()
5.4 Defining Vertex Labels(定義頂點標簽)
像邊一樣,頂點也有標簽。但與邊緣標簽不同,頂點標簽是可選的。頂點標簽可用於區分不同類型的頂點,例如用戶頂點和產品頂點。
盡管標簽在概念和數據模型級別是可選的,但JanusGraph將標簽作為內部實現分配各每個頂點。由AddVertex方法創建的頂點使用JanuSgraph的默認標簽。
要創建標簽,調用makeVertexLabel(String).make()打開的圖形或管理事務,並提供頂點標簽的名稱作為參數。頂點標簽名稱在圖表中必須是唯一的。
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
mgmt.commit()
// Create a labeled vertex
person = graph.addVertex(label, 'person')
// Create an unlabeled vertex
v = graph.addVertex()
graph.tx().commit()
5.5 Automatic Schema Maker(自動創建schema)
如果沒有明確定義邊標簽,屬性鍵或頂點標簽,則在添加邊,頂點或屬性設置期間首次使用時,將隱式定義邊標簽,屬性鍵或頂點標簽。DefaultSchemaMaker配置用於JanusGraph圖定義這樣的類型。
默認情況下,隱式創建的邊標簽具有多樣性MULTI,隱式創建的屬性鍵具有基數SINGLE和數據類型Object.class。用戶可以通過實現和注冊他們自己的DefaultSchemaMaker來控制schema元素的自動創建。
強烈建議通過schema.default=none在JanusGraph圖形配置中進行設置,明確定義所有schema元素並禁用自動schema創建。
5.6 Changing Schema Elements(更改schema元素)
邊標簽,屬性鍵或頂點標的定義一旦提交到圖,就無法更改。可以通過JanusGraphManagement.changeName(JanusGraphSchemaElement, String) 更改屬性鍵的名稱,如下示例屬性鍵place重命名為location.
mgmt = graph.openManagement()
place = mgmt.getPropertyKey('place')
mgmt.changeName(place, 'location')
mgmt.commit()
要注意的是,在當前運行的事務和集群中的其他JanusGraph圖形實例中,模式名稱更改可能不會立即可見。盡管存儲后端向所有的JanusGraph實例通知schema名稱的更改,但模式schema的更改可能需要一段時間才能生效,並且在某些故障情況,如網絡分區,可能需要重新啟動實例。如果這些與重命名同時發生。因此,用戶必須確保滿足以下任一條件:
- 重命名的標簽和鍵當前未處於活躍狀態(既寫入或讀取),並且在所有JanusGraph應答schema名稱更改前不會使用。
- 運行事務會主動適應短暫的中間時期,在這期間,舊名稱或新名稱都是有效的,這取決於JanuSgraph實例和名稱更改公告的狀態。這可能意味着同時到查詢兩個名稱的schema。
如果需要重新定義現有schema類型,建議將此類型的名稱修改成現在未使用(並且永不使用)。之后,可以使用原始名稱定義新的標簽或key,從而有效的替換舊標簽或鍵。但需要注意的是,這不會影響以前現有類型寫入的頂點、邊或屬性。
不支持在線重新定義現有圖形元素,必須通過批量圖形轉換完成。
5.7 Schema Constraints(schema約束)
模式的定義允許用戶配置顯式屬性和連接約束。屬性可以綁定到特定的頂點標簽和(或)邊標簽。此外,鏈接約束允許用戶明確定義那兩個頂點標簽允許可以通過邊標簽連接。這些約束可用於確保圖形與給定的域模型匹配。例如在眾神圖,一個god可以是另一個兄弟的god,不是一個monster的兄弟,神可以有年齡屬性,但是location不能有年齡屬性。默認情況下禁用這些約束。
啟用這些schema約束需要設置schema.constraints=true。此設置取決於設置schema.default。如果schema.default設置成none, 則違反schema約束會拋出IllegalArgumentException。如果schema.default沒有設置成none,則會自動創建schema約束,但不會拋出異常。激活模式約束對現有數據沒有影響,因為這些模式約束僅作用在插入的過程。因此,這些約束完全不會影響數據的讀取。
可以使用JanusGraphManagement.addProperties(VertexLabel, PropertyKey...)綁定多個屬性到頂點,例如:
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
birthDate = mgmt.makePropertyKey('birthDate').dataType(Long.class).cardinality(Cardinality.SINGLE).make()
mgmt.addProperties(person, name, birthDate)
mgmt.commit()
可以使用JanusGraphManagement.addProperties(EdgeLabel, PropertyKey...)為邊綁定多個屬性。例如:
mgmt = graph.openManagement()
follow = mgmt.makeEdgeLabel('follow').multiplicity(MULTI).make()
name = mgmt.makePropertyKey('name').dataType(String.class).cardinality(Cardinality.SET).make()
mgmt.addProperties(follow, name)
mgmt.commit()
可以使用JanusGraphManagement.addConnection(EdgeLabel, VertexLabel out, VertexLabel in)定義傳出,傳入和邊之間的連接,例如:
mgmt = graph.openManagement()
person = mgmt.makeVertexLabel('person').make()
company = mgmt.makeVertexLabel('company').make()
works = mgmt.makeEdgeLabel('works').multiplicity(MULTI).make()
mgmt.addConnection(works, person, company)
mgmt.commit()
