select可以幫助我們選擇EdgeDB內所有的東西,包括scalar type
及object type
。
選擇scalar type
非常簡單,直接使用select
關鍵字即可,如:
select 1;
{1}
我們藉由上述query來討論幾個EdgeDB的細節。
EdgeDB是一個具有嚴格型別規定(strongly typed)的語言,所有的整數如果沒有指定,則隱性定義為int64
。我們可以使用is來確認其所屬型別,如:
select 1 is int64;
{true}
而所有的浮點數如果沒有指定,則隱性定義為float64
。
select 1.0 is float64;
{true}
當整數與浮點數進行運算後,則會自動轉換為浮點數float64
。
select (1 + 1.0) is float64;
{true}
當想要進行型別轉換的時候,可以於前面加上<type>
,例如:
select <int16>(1 + 1.0) is int16;
{true}
即可將(1 + 1.0)
由float64
轉換為int16
。
型別轉換並不保證永遠會成功,當轉換失敗時,則會報錯。例如:
select <int16>(32768);
edgedb error: NumericOutOfRangeError: std::int16 out of range
由於int16
包含的範圍為-32768
到32767
,而32768
已超出int16
涵蓋的範圍,所以報錯out of range
。
但是下面這個有趣的query會返回{true}
,因為<int16>32768
的確是顯性指定為int16
。
select <int16>(32768) is int16;
{true}
query返回的結果永遠都為EdgeDBSet
,其為一無序的multi set
,即同一個object
可以重覆出現在EdgeDBSet
內。例如:
select {1, 1};
{1, 1}
如果返回的結果為空時,則為空EdgeDBSet
。而即使是空的EdgeDBSet
,其也是具有型態的。也就是說如果您想執行下面這樣的query時,EdgeDB將會報錯,因為無法判斷該EdgeDBSet
的型別:
select {};
error: QueryError: expression returns value of indeterminate type
┌─ <query>:1:8
│
1 │ select {};
│ ^^ Consider using an explicit type cast.
必須像下面這樣,顯性指定型別後,才可以成功執行query。
select <int64>{};
{}
此外,EdgeDBSet
內所有的object
都必須為同一種型別。如果我們試圖建立一個含有int64
及str
的EdgeDBSet
時,會報錯如下:
select 1 union "1";
┌─ <query>:1:8
│
1 │ select 1 union "1";
│ ^^^^^^^^^^^ Consider using an explicit type cast or a conversion function.
但是當執行整數與浮點數的運算時,EdgeDB會自動轉換型別,例如下面這個query將會返回一個其內為1.0
及2.0
兩個float64
型別的EdgeDBSet
。
select (1 union 2.0);
{1, 2}
我們可以使用is
來確認:
select (1 union 2.0) is float64;
{true, true}
再次強調,由於EdgeDBSet
是一無序的multi set
,所以您不能斷言第一個true
來自判斷1
是不是float64
型別,而第二個true
來自判斷2.0
是不是float64
型別。
有時候我們需要一些變數來儲存暫時的計算結果,這樣可以使得query看起來更加簡單明瞭。例如:
select (1 + 2) + (3 + 4);
{10}
其中我們特別使用()
將1 + 2
及3 + 4
分別括起來,想要強調此運算將會是由1 + 2
與3 + 4
相加而成,雖然結果與直接寫成select 1 + 2 + 3 + 4
一樣,但含義卻不相同。
此時,我們可以透過with分別定義兩個變數group1
及group2
後,並於select
中使用此兩變數。
with group1:= 1 + 2,
group2:= 3 + 4
select group1 + group2;
{10}
這邊有兩點需要注意:
:=
。,
隔開。由於在with
中,後面的變數可以直接引用前面定義過的變數,所以可以寫出下列的query:
with a:= 1 + 2,
b:= a + 3,
c:= b + 4,
select c;
{10}
這個特性可以讓我們在with
中定義一些中間變數,幫助我們在讀query時,更加容易了解其想表達的函義。
我們繼續使用[Day03]中定義的schema:
type User {
required name: str;
multi followers: User;
}
type Article {
required title: str;
required author: User;
}
我們利用nested insert
建立一個User
及一個Article object
。
insert Article {
title:= "first article",
author:= (insert User {name:= "John"})
};
此時我們可以用shape
搭配select
來顯示所有Article object
的title property
及author link
。
select Article {title, author};
{
default::Article {
title: 'first article',
author: default::User {id: 51d0135a-3f59-11ef-b4b5-e3b7f76b4391}
}
}
但是上述query只顯示了author link
所連接的User id
,此時我們可以針對author
再使用一次shape
,即:
select Article {title, author: {name}};
{
default::Article {
title: 'first article',
author: default::User {name: 'John'}
}
}
這樣一來就能看出目前database
中只有一個Article object
,其有一個title property
為「"first article"」及連接一個User object
,而該User object
內有一個name property
為「"John"」。
雖然shape
的操作十分方便,但是當一個object
具有多個property
或link
時,也需要花費一番心力。此時EdgeDB提供強大的splat來幫助我們。
針對所選object
使用single splat
可以顯示該object
內所有的property
,如:
select Article {*};
{
default::Article {
id: 51d03d6c-3f59-11ef-b4b5-5bee6a32e59e,
title: 'first article'
}
}
如果使用double splat
可以顯示該object
內所有的property
及link
,如:
select Article {**};
{
default::Article {
id: 51d03d6c-3f59-11ef-b4b5-5bee6a32e59e,
title: 'first article',
author: default::User {
id: 51d0135a-3f59-11ef-b4b5-e3b7f76b4391,
name: 'John'
},
},
}
這邊需留意,double splats
針對link
所能擴展的深度僅有一層,也就是說當User object
內有link
的話,並不會遞迴地擴展下去。
我們利用nested insert
建立一個User
及一個Article object
。
insert Article {
title:= "second article",
author:= (insert User {name:= "May"})
};
這樣一來,當前資料庫內就有兩個User object
及Article object
。
order by可以幫助我們根據不同準則來排序,例如根據Article object
的title property
來排:
select Article {title, author:{name}} order by .title;
{
default::Article {
title: 'first article',
author: default::User {name: 'John'}
},
default::Article {
title: 'second article',
author: default::User {name: 'May'}
},
}
因為「"first article"」的「"f"」在英文字母中比「"second article"」的「"s"」還前面,所以title property
為「"first article"」的Article object
會排在最前。
又或者我們可以使用len()(註1)來計算User object
的name property
的長度後,再反向排序:
select User {name} order by len(.name) desc;
{default::User {name: 'John'}, default::User {name: 'May'}}
因為「"John"」有四個英文字母比「"May"」的三個還多,所以反向排序後John
會出現在May
之前。
limit可以讓我們指定由EdgeDBSet
中取回幾個元素,例如:
select User {name} limit 1;
{default::User {name: 'John'}}
註1:len()
是EdgeDB內建的函數。在EdgeDB中,我們可以定義多個同名的函數,每一個接收不同數量的參數甚至不同多種型別,EdgeDB將會依所傳入的函數數量及型別,來決定回傳值,這樣的特性稱為function overloading
。依len()
的說明文件來看,其可接受str
、bytes
及array<anytype>
三種型態的輸入,並回傳int64
。