資料的關係,會決定你應該用哪種模型。
Conference on Data System Language(CODASYL)
找到「年輕的註冊者的職業」
MOVE 'ACCOUNTANT' TO TITLE IN JOB.
FIND FIRST JOB USING TITLE.
IF NOT-FOUND GO TO EXIT.
FIND FIRST EMP WITHIN ASSIGN.
IF END-OF-SET GO TO 0.
GET EMP.
IF EMP.BIRTHYR I 1950 GO TO N.
FIND OWNER WITHIN WORKS-IN.
GET DEPT.
...
FIND NEXT EMP WITHIN ASSIGN.
GO TO M.
FIND NEXT JOB USING TITLE.
GO TO L.
EXIT.
– RH Katz.: "Decompiling CODASYL DML into Relational Queries.", 1982
– Edgar Codd(1970)
他走回頭路了嗎?
取得使用者 ID 123 的第三個工作經歷
SELECT experience FROM experience_relation WHERE use_id=123 AND index=3
db.get("users.123.experiences.3");
關聯式資料庫 v.s. 文件式資料庫
資料的關係中,有大量多對多(many-to-many)
每個點和線會有很多屬性:
CREATE TABLE vertices (
vertex_id integer PRIMARY KEY,
vertex_type text,
properties json
)
CREATE TABLE edges (
edge_id integer PRIMARY KEY,
tail_vertex integer REFERENCES vertices (vertex_id),
head_vertex integer REFERENCES vertices (vertex_id),
label text,
properties json
)
CREATE
/* vertices */
(NAmerica:Location {name:'North America', type:'continent'}),
( USA:Location {name:'United States', type:'country' }),
( Idaho:Location {name:'Idaho', type:'state' }),
( Lucy:Person {name:'Lucy'}),
/* edges */
(Idaho) -[:WITHIN]-> (USA) -[:WITHIN]-> (NAmerica),
(Lucy) -[:BORN_IN]-> (Idaho)
/* 建立 */
CREATE
...
(Idaho) -[:WITHIN]-> (USA) -[:WITHIN]-> (NAmerica),
(Lucy) -[:BORN_IN]-> (Idaho)
/* 搜尋 */
MATCH
(Person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (us:Location {name:'United States'}),
(Person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (en:Location {name:'Europe'}),
RETURN Person.name
和屬性圖模型大同小異,以 (subject,predicate,object)
方式存在。
建立
@prefix : <urn:example:>. _:lucy a :Person; :name "Lucy"; :bornIn _:idaho. _:idaho a :Location; :name "Idaho"; :type "state"; :within _:usa _:usa a :Loaction; :name "United States"; :type "country"; :within _:namerica. _:namerica a :Location; :name "North America"; :type "continent".
搜尋
PREFIX : <urn:example:>
SELECT ?personName WHERE {
?person :name ?personName.
?person :bornIn / :within* / :name "United States".
?person :livesIn / :within* / :name "Europe".
}
return animals.filter((animal) => animal.family === 'Sharks');
SELECT * FROM animals WHERE family = 'Sharks'
function getSharks(animals) {
var sharks = [];
for (var i = 0; i < animals.length; i++) {
if (animals[i].family === "Sharks") {
sharks.push(animals[i]);
}
}
return sharks;
}
每月觀察到的鯊魚數
db.observations.mapReduce( function map() { var year = this.observationTimestamp.getFullYear(); var month = this.observationTimestamp.getMonth() + 1; emit(year + "-" + month, this.numAnimals); }, function reduce(key, values) { return Array.sum(values); }, { query: { family: "Sharks" }, out: "monthlySharkReport" } );
發生一場命案,請透過互斥的證詞找出誰在說謊:
% 定義證詞 testimony(a, friend(b)). % testimony(a, knew(b)). testimony(a, enemy(c)). % testimony(a, knew(c)). % testimony(a, innocent(a)). testimony(b, out_of_town(b)). testimony(b, stranger(b)). testimony(c, in_town(c)). % testimony(c, innocent(c)). testimony(c, in_town(a)). testimony(c, in_town(b)). % 宣告什麼是衝突的 inconsistent(friend(X), enemy(X)). inconsistent(friend(X), stranger(X)). inconsistent(enemy(X), stranger(X)). inconsistent(out_of_town(X), in_town(X)). % 找出說謊者 lier(L) :- member(L, [a, b, c]), % 從 a, b, c 中拉出一個人叫 L(lier) select(L, [a, b, c], Witness), % 剩下的人算進證人 consistent(Witness). % 證人的證詞是合理的 % 群組中大家證詞都是合理的 consistent(W) :- \+ inconsistent_testimony(W). % 群組中有人有衝突的證詞 inconsistent_testimony(W) :- member(X, W), % 從群組中挑出 X 和 Y member(Y, W), X \= Y, % X 和 Y 不同人 testimony(X, XT), % 拿出 X 的其中一個證詞 testimony(Y, YT), % 拿出 Y 的其中一個證詞 inconsistent(XT, YT). % 他們是衝突的
Datalog
within_recursive(Location, Name) :- name(Location, Name).
within_recursive(Location, Name) :- within(Location, BiggerLoc),
within_recursive(BiggerLoc, Name).
migrated(Name, BornIn, LivingIn) :- name(Person, Name),
born_in(Person, BornLoc),
within_recursive(BornLoc, BornIn),
lives_in(Person, LivingLoc),
within_recursive(LivingLoc, LivingIn).
?- migrated(Who, 'United States', 'Europe').
/* Who = 'Lucy'. */
如果現在有一個製作菜單的應用程式,其資料架構如下:
漢堡
├── 火腿漢堡
│ ├── 火腿
│ └── 麵包
└── 起司漢堡
├── 起司
└── 麵包
飲料
├── 鮮奶茶
│ ├── 牛奶
│ └── 紅茶
└── 紅茶
└── 紅茶
隨著應用程式演進,新增了「成分庫存系統」的功能:
漢堡
├── 火腿漢堡
│ ├── 火腿
│ │ └── 20
│ └── 漢堡
│ └── 30
└── 起司漢堡
├── 起司
│ └── 15
└── 漢堡
└── 30
收斂。
補充: