目錄:
一、數據預覽
本次使用的數據共有43條,每條數據包含姓氏、年齡、活動狀態和特征等六個字段
其中每個特征都有一個隨機字母作為文檔密鑰。特質標簽有英文和德文。
地點由地名和經緯度組成:
二、基本的CRUD
創建集合:
在創建文檔之前,需要創造一個放置它的集合,集合可以通過Web界面,arangosh或驅動程序來創建。AQL無法創建集合。
單擊Web界面中的COLLECTIONS,然后單擊Add Collection並鍵入 Characters
name。使用保存確認。新集合就出現在了列表中。
插入單個對象:
使用AQL插入文檔
INSERT { "name": "Ned", "surname": "Stark", "alive": true, "age": 41, "traits": ["A","H","C","N","P"] } INTO Characters
語法:
INSERT document INTO collectionName
該文檔是一個對象,由屬性鍵和值對組成。屬性鍵的引號在AQL中是可選的。鍵總是字符串,而屬性值可以有不同的類型:
- null
- boolean (true, false)
- number (integer and floating point)
- string
- array
- object
批量插入對象:
AQL不允許INSERT
在單個查詢中針對同一集合的多個操作。但是可以使用FOR
循環體,插入多個文檔。
LET data = [ { "name": "Robert", "surname": "Baratheon", "alive": false, "traits": ["A","H","C"] }, { "name": "Jaime", "surname": "Lannister", "alive": true, "age": 36, "traits": ["A","F","B"] }, { "name": "Catelyn", "surname": "Stark", "alive": false, "age": 40, "traits": ["D","H","C"] }, { "name": "Cersei", "surname": "Lannister", "alive": true, "age": 36, "traits": ["H","E","F"] }, { "name": "Daenerys", "surname": "Targaryen", "alive": true, "age": 16, "traits": ["D","H","C"] }, { "name": "Jorah", "surname": "Mormont", "alive": false, "traits": ["A","B","C","F"] }, { "name": "Petyr", "surname": "Baelish", "alive": false, "traits": ["E","G","F"] }, { "name": "Viserys", "surname": "Targaryen", "alive": false, "traits": ["O","L","N"] }, { "name": "Jon", "surname": "Snow", "alive": true, "age": 16, "traits": ["A","B","C","F"] }, { "name": "Sansa", "surname": "Stark", "alive": true, "age": 13, "traits": ["D","I","J"] }, { "name": "Arya", "surname": "Stark", "alive": true, "age": 11, "traits": ["C","K","L"] }, { "name": "Robb", "surname": "Stark", "alive": false, "traits": ["A","B","C","K"] }, { "name": "Theon", "surname": "Greyjoy", "alive": true, "age": 16, "traits": ["E","R","K"] }, { "name": "Bran", "surname": "Stark", "alive": true, "age": 10, "traits": ["L","J"] }, { "name": "Joffrey", "surname": "Baratheon", "alive": false, "age": 19, "traits": ["I","L","O"] }, { "name": "Sandor", "surname": "Clegane", "alive": true, "traits": ["A","P","K","F"] }, { "name": "Tyrion", "surname": "Lannister", "alive": true, "age": 32, "traits": ["F","K","M","N"] }, { "name": "Khal", "surname": "Drogo", "alive": false, "traits": ["A","C","O","P"] }, { "name": "Tywin", "surname": "Lannister", "alive": false, "traits": ["O","M","H","F"] }, { "name": "Davos", "surname": "Seaworth", "alive": true, "age": 49, "traits": ["C","K","P","F"] }, { "name": "Samwell", "surname": "Tarly", "alive": true, "age": 17, "traits": ["C","L","I"] }, { "name": "Stannis", "surname": "Baratheon", "alive": false, "traits": ["H","O","P","M"] }, { "name": "Melisandre", "alive": true, "traits": ["G","E","H"] }, { "name": "Margaery", "surname": "Tyrell", "alive": false, "traits": ["M","D","B"] }, { "name": "Jeor", "surname": "Mormont", "alive": false, "traits": ["C","H","M","P"] }, { "name": "Bronn", "alive": true, "traits": ["K","E","C"] }, { "name": "Varys", "alive": true, "traits": ["M","F","N","E"] }, { "name": "Shae", "alive": false, "traits": ["M","D","G"] }, { "name": "Talisa", "surname": "Maegyr", "alive": false, "traits": ["D","C","B"] }, { "name": "Gendry", "alive": false, "traits": ["K","C","A"] }, { "name": "Ygritte", "alive": false, "traits": ["A","P","K"] }, { "name": "Tormund", "surname": "Giantsbane", "alive": true, "traits": ["C","P","A","I"] }, { "name": "Gilly", "alive": true, "traits": ["L","J"] }, { "name": "Brienne", "surname": "Tarth", "alive": true, "age": 32, "traits": ["P","C","A","K"] }, { "name": "Ramsay", "surname": "Bolton", "alive": true, "traits": ["E","O","G","A"] }, { "name": "Ellaria", "surname": "Sand", "alive": true, "traits": ["P","O","A","E"] }, { "name": "Daario", "surname": "Naharis", "alive": true, "traits": ["K","P","A"] }, { "name": "Missandei", "alive": true, "traits": ["D","L","C","M"] }, { "name": "Tommen", "surname": "Baratheon", "alive": true, "traits": ["I","L","B"] }, { "name": "Jaqen", "surname": "H'ghar", "alive": true, "traits": ["H","F","K"] }, { "name": "Roose", "surname": "Bolton", "alive": true, "traits": ["H","E","F","A"] }, { "name": "The High Sparrow", "alive": true, "traits": ["H","M","F","O"] } ] FOR d IN data INSERT d INTO Characters
語法:
LET variableName = valueExpression
LET
關鍵字定義了同名稱的變量數據和對象值的數列,格式為[ {...}, {...}, ... ]
FOR variableName IN expression
用於迭代數據數組的每個元素 。在每個循環中,將一個元素分配給變量d。然后在INSERT
語句中使用此變量。相當於下面的格式:
INSERT { "name": "Robert", "surname": "Baratheon", "alive": false, "traits": ["A","H","C"] } INTO Characters INSERT { "name": "Jaime", "surname": "Lannister", "alive": true, "age": 36, "traits": ["A","F","B"] } INTO Characters ...
檢索
檢索集合中的所有文檔:
FOR c IN Characters
RETURN c
語法:
FOR variableName IN collectionName
對於集合中的每個文檔,依次分配給變量c,然后根據循環體返回該文檔。
選取其中一個文檔如下:
{ "_key": "2861650", "_id": "Characters/2861650", "_rev": "_V1bzsXa---", "name": "Ned", "surname": "Stark", "alive": true, "age": 41, "traits": ["A","H","C","N","P"] },
該文檔包含我們存儲的四個屬性,以及數據庫系統添加的另外三個屬性:
_key:文檔鍵,用戶可以在創建文檔時提供文檔鍵,也可以自動分配唯一值,不能改變,只讀
_id:集合名/文檔鍵,只讀
_rev:系統管理的修訂版ID,只讀
檢索特定文檔:
RETURN DOCUMENT("Characters", "2861650") // --- or ---
RETURN DOCUMENT("Characters/2861650")
返回:
[ { "_key": "2861650", "_id": "Characters/2861650", "_rev": "_V1bzsXa---", "name": "Ned", "surname": "Stark", "alive": true, "age": 41, "traits": ["A","H","C","N","P"] } ]
語法:
DOCUMENT()
使用_key或_id檢索特定文檔,該函數還允許一次獲取多個文檔
RETURN DOCUMENT("Characters", ["2861650", "2861653"]) // --- or ---
RETURN DOCUMENT(["Characters/2861650", "Characters/2861653"])
更新文檔:
修改現有文件:
UPDATE "2861650" WITH { alive: false } IN Characters
語法:
UPDATE documentKey WITH object IN collectionName
用列出的屬性更新指定的文檔(如果它們不存在則添加它們),但保持其余不變。要替換整個文檔內容,則要使用REPLACE
函數:
REPLACE "2861650" WITH { name: "Ned", surname: "Stark", alive: false, age: 41, traits: ["A","H","C","N","P"] } IN Characters
該函數也適用於循環,例如為所有文檔添加新屬性:
FOR c IN Characters UPDATE c WITH { season: 1 } IN Characters
刪除文件:
語法:
REMOVE _key IN Collectiosname
要從集合中完全刪除文檔,需要執行REMOVE
操作。它的工作方式與其他修改操作類似,但沒有WITH
子句:
REMOVE "2861650" IN Characters
三、匹配文件
語法:
FILTER
查找滿足比_key相等更復雜的條件的文檔,能夠為要匹配的文檔制定任意條件。
等於條件:
FOR c IN Characters FILTER c.name == "Ned" RETURN c
過濾條件如下:“ 字符文檔的屬性name必須等於字符串Ned ”。如果條件適用,則返回字符文檔。
范圍條件:
FOR c IN Characters FILTER c.age >= 13 RETURN c.name
多種條件:
FOR c IN Characters FILTER c.age < 13 FILTER c.age != null RETURN { name: c.name, age: c.age } //or FOR c IN Characters FILTER c.age < 13 AND c.age != null RETURN { name: c.name, age: c.age }
替代條件:
FOR c IN Characters FILTER c.name == "Jon" OR c.name == "Joffrey" RETURN { name: c.name, surname: c.surname }
四、排序和限制
限制語法:
LIMIT()
LIMIT
后面跟着一個最大顯示數的數字,限制結果顯示行數。
FOR c IN Characters LIMIT 5 RETURN c.name
還可以使用LIMIT來跳過一定數量的記錄返回下一個n個文檔:
FOR c IN Characters LIMIT 2, 5 RETURN c.name
排序語法:
SORT()
DESC
降序來反轉排序順序
FOR c IN Characters SORT c.name DESC LIMIT 10 RETURN c.name
多個字段排序
FOR c IN Characters FILTER c.surname SORT c.surname, c.name LIMIT 10 RETURN { surname: c.surname, name: c.name }
此處FILTER的作用是僅保留surname為非空記錄
五、組合
語法:
MERGE()
MERGE()的
功能是將對象組合在一起。因為使用了原始字符屬性{ traits: ... }
,所以后者被合並覆蓋。
FOR c IN Characters RETURN MERGE(c, { traits: DOCUMENT("Traits", c.traits)[*].en } )
六、圖操作
創建圖:
語法:
INSERT { _from: _id(A), _to: _id(B) } INTO ChildOf
實例:
首先,創建一個新的集合,並確保將集合類型更改為Edge。
然后,通過查詢多個集合的數據,將結果存入邊集合中
LET data = [ //關系數據
{ "parent": { "name": "Ned", "surname": "Stark" }, "child": { "name": "Robb", "surname": "Stark" } }, { "parent": { "name": "Ned", "surname": "Stark" }, "child": { "name": "Sansa", "surname": "Stark" } }, { "parent": { "name": "Ned", "surname": "Stark" }, "child": { "name": "Arya", "surname": "Stark" } }, { "parent": { "name": "Ned", "surname": "Stark" }, "child": { "name": "Bran", "surname": "Stark" } }, { "parent": { "name": "Catelyn", "surname": "Stark" }, "child": { "name": "Robb", "surname": "Stark" } }, { "parent": { "name": "Catelyn", "surname": "Stark" }, "child": { "name": "Sansa", "surname": "Stark" } }, { "parent": { "name": "Catelyn", "surname": "Stark" }, "child": { "name": "Arya", "surname": "Stark" } }, { "parent": { "name": "Catelyn", "surname": "Stark" }, "child": { "name": "Bran", "surname": "Stark" } }, { "parent": { "name": "Ned", "surname": "Stark" }, "child": { "name": "Jon", "surname": "Snow" } }, { "parent": { "name": "Tywin", "surname": "Lannister" }, "child": { "name": "Jaime", "surname": "Lannister" } }, { "parent": { "name": "Tywin", "surname": "Lannister" }, "child": { "name": "Cersei", "surname": "Lannister" } }, { "parent": { "name": "Tywin", "surname": "Lannister" }, "child": { "name": "Tyrion", "surname": "Lannister" } }, { "parent": { "name": "Cersei", "surname": "Lannister" }, "child": { "name": "Joffrey", "surname": "Baratheon" } }, { "parent": { "name": "Jaime", "surname": "Lannister" }, "child": { "name": "Joffrey", "surname": "Baratheon" } } ] FOR rel in data LET parentId = FIRST( //FIRST()提取第一個元素
FOR c IN Characters FILTER c.name == rel.parent.name //篩選條件
FILTER c.surname == rel.parent.surname LIMIT 1 RETURN c._id //返回_id
) LET childId = FIRST( FOR c IN Characters FILTER c.name == rel.child.name FILTER c.surname == rel.child.surname LIMIT 1 RETURN c._id ) FILTER parentId != null AND childId != null //剔除_id都為空的記錄
INSERT { _from: childId, _to: parentId } INTO ChildOf //將數據插入邊集合
RETURN NEW //返回數據
也可以直接創建邊數據:
INSERT { _from: "Characters/robb", _to: "Characters/ned" } INTO ChildOf
遍歷圖:
語法:
FOR v IN 1..1 OUTBOUND _id ChildOf
RETURN v.name
其中1..1為遍歷深度
實例:
FOR c IN Characters FILTER c.name == "Bran" FOR v IN 1..1 OUTBOUND c ChildOf RETURN v.name
返回
[ "Ned", "Catelyn" ]
遍歷的情況如下圖:
若是反向遍歷,則需要使用到INBOUND關鍵字:
FOR c IN Characters FILTER c.name == "Tywin" FOR v IN 2..2 INBOUND c ChildOf RETURN DISTINCT v.name
輸出:
[ "Joffrey" ]
遍歷情況如下:
需要注意的是,“1..1”限制了遍歷深度為1,“2..2”限制了遍歷深度為2,而"1..2"限制遍歷深度既可以為1也可以為2。
七、地理空間查詢
地點數據
創建地點集合:
錄入地點數據:
LET places = [ { "name": "Dragonstone", "coordinate": [ 55.167801, -6.815096 ] }, { "name": "King's Landing", "coordinate": [ 42.639752, 18.110189 ] }, { "name": "The Red Keep", "coordinate": [ 35.896447, 14.446442 ] }, { "name": "Yunkai", "coordinate": [ 31.046642, -7.129532 ] }, { "name": "Astapor", "coordinate": [ 31.50974, -9.774249 ] }, { "name": "Winterfell", "coordinate": [ 54.368321, -5.581312 ] }, { "name": "Vaes Dothrak", "coordinate": [ 54.16776, -6.096125 ] }, { "name": "Beyond the wall", "coordinate": [ 64.265473, -21.094093 ] } ] FOR place IN places INSERT place INTO Locations
設置地理空間索引:
在COLLECTIONS界面,添加新的Indexes,設置為coordinate字段:
查找附近的位置
語法:
NEAR()
找到最接近的坐標參考點
FOR loc IN NEAR(Locations, 53.35, -6.26, 3) RETURN { name: loc.name, latitude: loc.coordinate[0], longitude: loc.coordinate[1] }
輸出:
[ { "name": "Vaes Dothrak", "latitude": 54.16776, "longitude": -6.096125 }, { "name": "Winterfell", "latitude": 54.368321, "longitude": -5.581312 }, { "name": "Dragonstone", "latitude": 55.167801, "longitude": -6.815096 } ]
查找半徑內的位置
語法:
WITHIN()
從參考點搜索給定半徑內的位置
FOR loc IN WITHIN(Locations, 53.35, -6.26, 200 * 1000) RETURN { name: loc.name, latitude: loc.coordinate[0], longitude: loc.coordinate[1] }
輸出
[ { "name": "Vaes Dothrak", "latitude": 54.16776, "longitude": -6.096125 }, { "name": "Winterfell", "latitude": 54.368321, "longitude": -5.581312 } ]
按距離查找位置:
語法:
NEAR()或
WITHIN()
通過添加一個可選的第五個參數返回到參考點的距離。必須是一個字符串:
FOR loc IN NEAR(Locations, 53.35, -6.26, 3, "distance") RETURN { name: loc.name, latitude: loc.coordinate[0], longitude: loc.coordinate[1], distance: loc.distance / 1000 }
輸出:
[ { "name": "Vaes Dothrak", "latitude": 54.16776, "longitude": -6.096125, "distance": 91.56658640314431 }, { "name": "Winterfell", "latitude": 54.368321, "longitude": -5.581312, "distance": 121.66399816395028 }, { "name": "Dragonstone", "latitude": 55.167801, "longitude": -6.815096, "distance": 205.31879386198324 } ]
使用AQL遇到的問題:
問題1:如何對查詢結果進行計數並返回?
解決方法:
RETURN COUNT(FOR v IN visitors FILTER v.ip == "127.0.0.1" RETURN 1)
參考資料: