iT邦幫忙

2024 iThome 鐵人賽

DAY 4
0

select可以幫助我們選擇EdgeDB內所有的東西,包括scalar typeobject type

Select scalar 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包含的範圍為-3276832767,而32768已超出int16涵蓋的範圍,所以報錯out of range

但是下面這個有趣的query會返回{true},因為<int16>32768的確是顯性指定為int16

select <int16>(32768) is int16;
{true}

EdgeDBSet

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都必須為同一種型別。如果我們試圖建立一個含有int64strEdgeDBSet時,會報錯如下:

 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.02.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型別。

與with的搭配使用

有時候我們需要一些變數來儲存暫時的計算結果,這樣可以使得query看起來更加簡單明瞭。例如:

select (1 + 2) + (3 + 4);
{10}

其中我們特別使用()1 + 23 + 4分別括起來,想要強調此運算將會是由1 + 23 + 4相加而成,雖然結果與直接寫成select 1 + 2 + 3 + 4一樣,但含義卻不相同。

此時,我們可以透過with分別定義兩個變數group1group2後,並於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時,更加容易了解其想表達的函義。

Select object type

我們繼續使用[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 objecttitle propertyauthor 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具有多個propertylink時,也需要花費一番心力。此時EdgeDB提供強大的splat來幫助我們。

針對所選object使用single splat可以顯示該object內所有的property,如:

select Article {*};
{
    default::Article {
        id: 51d03d6c-3f59-11ef-b4b5-5bee6a32e59e, 
        title: 'first article'
    }
}

如果使用double splat可以顯示該object內所有的propertylink,如:

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的話,並不會遞迴地擴展下去。

order by

我們利用nested insert建立一個User及一個Article object

insert Article {
   title:= "second article",
   author:= (insert User {name:= "May"})
};

這樣一來,當前資料庫內就有兩個User objectArticle object

order by可以幫助我們根據不同準則來排序,例如根據Article objecttitle 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 objectname property的長度後,再反向排序:

select User {name} order by len(.name) desc;
{default::User {name: 'John'}, default::User {name: 'May'}}

因為「"John"」有四個英文字母比「"May"」的三個還多,所以反向排序後John會出現在May之前。

limit

limit可以讓我們指定由EdgeDBSet中取回幾個元素,例如:

select User {name} limit 1;
{default::User {name: 'John'}}

備註

註1:len()是EdgeDB內建的函數。在EdgeDB中,我們可以定義多個同名的函數,每一個接收不同數量的參數甚至不同多種型別,EdgeDB將會依所傳入的函數數量及型別,來決定回傳值,這樣的特性稱為function overloading。依len()說明文件來看,其可接受strbytesarray<anytype>三種型態的輸入,並回傳int64


上一篇
[Day03] - 如何insert
下一篇
[Day05] - 如何update
系列文
一起看無間道學EdgeDB30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言