類似關聯式資料庫,應用成是同樣也可以針對 entity 進行查詢 & 排序…等操作。
原本的 where 過濾條件,在 Datastore 中變成了 filters 的方式,而 filter 可用在 property 的 value、keys、或是 ancestor 上。
排序則是使用 sort orders 來進行。
最後的回傳資料可以是完整的 entity 集合,也可以是包含部分屬性的 entity 集合,甚至可以只是 entity keys。
而標準的查詢包含了以下資訊:
- entity kind
- 0 或多個 filter 條件
- 0 或多個用來排序結果的 sort orders 條件
為了確保執行的效能 & 節省記憶體,建議盡量每次查詢都限定回傳的資料筆數。
Query structure
每個 query 會包含有 entity kind、零或多個 filter、以及零或多個 sort order。
Filters
filter 可以用在 property / key / ancestor 三種資訊上。
Property Filter
以下直接用範例來說明:
type Person struct {
Name string
City string
BirthYear int
Height int
}
func handler_BasicQuery(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
//查詢 Height 屬性大於 170 的資料
//根據 Height 屬性進行 desc 排序
q := datastore.NewQuery("Person").Filter("Height > ", 170).Order("-Height")
var people []Person
q.GetAll(c, &people)
for p := range people {
fmt.Fprintf(w, `Name=%q, City=%q, BirthYear=%d, Height=%d\n`, people[p].Name, people[p].City, people[p].BirthYear, people[p].Height)
}
}
Key Filter
若要使用 entity key 來進行 filter,必須使用特別的關鍵字 _key_。
以下舉個例:
q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)
key 值在 datastore 中是如何排序的呢? 是依照以下的順序來排:
- Ancestor path
- Entity kind
- Identifier (Numeric ID 排在自訂的 key name 之前)
Ancestor Filter
指定 ancestor 也是種 filter 的方式,以下舉個簡單的例子:
q := datastore.NewQuery("Person").Ancestor(ancestorKey)
ancestorKey 若是 nil 會產生錯誤,不會回傳 root entities 喔!
Sort orders
要進行排序需要指定 property & 排序方式(asc or desc),以下範例說明:
//Name asc
q := datastore.NewQuery("Person").Order("Name")
//Height desc
q := datastore.NewQuery("Person").Order("-Height")
//多個條件排序
q := datastore.NewQuery("Person").Order("Name").Order("-Height")
Special query types
Kindless queries
若查詢中沒有指定 kind & ancestor,則表示是針對此應用程式在 Datastore 中所有的 entity 進行查詢。
有些特別的資訊(例如:statistics entities & Blobstore metadata entities)就必須要透過這種方式搜尋。
而不指定 kind & ancestor 的搜尋方式也是有限制的,就是不能使用 property 進行搜尋 or 將資料排序,只能使用 _key_ 關鍵字來針對 entity key 進行搜尋。
Keys-only queries
透過 KeysOnly() 方法取得 entity 的 key 值集合,舉個簡單範例:
q := datastore.NewQuery("Person").KeysOnly()
搜尋限制
在 Datastore 上進行搜尋是有些限制的,說明如下:
在搜尋中沒有包含指定 property 的 entity 都將被忽略
同樣 kind 底下的 entity 的 property 內容是可以不同的,因此在 filter or sort 時若指定的 property 有可能不會存在於所有的 entity 中,這時沒有該 property 的 entity 都將被忽略。“不等於”的 filter 最多只能用在一個 property 上
//合法搜尋,兩個 filter 都用在相同的 property 上
q := datastore.NewQuery("Person").
Filter("BirthYear >=", minBirthYear).
Filter("BirthYear <=", maxBirthYear)
//不合法的搜尋,兩個 filter 分別用在不同的 property 上
q := datastore.NewQuery("Person").
Filter("BirthYear >=", minBirthYear).
Filter("Height <=", maxHeight)
//跟"="混用的話,沒有 property 數量限制(但不等於的 filter 還是只能用在同一個 property 上)
q := datastore.NewQuery("Person").
Filter("Name =", targetName).
Filter("City =", targetCity).
Filter("BirthYear >=", minBirthYear).
Filter("BirthYear <=", maxBirthYear)
- “不等於” filter 所指定的 property 必須先進行排序
//合法搜尋,BirthYear 為第一個排序的 property
q := datastore.NewQuery("Person").
Filter("BirthYear >=", minBirthYear).
Order("BirthYear").
Order("Name")
//不合法搜尋,BirthYear 必須放置於第一個 property 進行排序
q := datastore.NewQuery("Person").
Filter("BirthYear >=", minBirthYear).
Order("Name")
//不合法搜尋,Name 為第一個排序的 property (必須是 BirthYear)
q := datastore.NewQuery("Person").
Filter("BirthYear >=", minBirthYear).
Order("Name").
Order("BirthYear")
含有多個 value 的 property 在 filter or sort 時可能會有出乎意料之外的結果
這個部分等我真的遇到再來寫…..在 transaction 中的 query,必須包含 ancestor filter
因為在 transaction 中的 query 必須屬於相同的 entity group,因此必須帶上 ancestor filter 資訊。
取得查詢結果
取得查詢結果有幾種方式,介紹如下:
使用 Run method
透過 Run method 可以得到一個 Iterator,並搭配 Next method 可以一個一個的取得搜尋結果,範例如下:
q := datastore.NewQuery("Person")
t := q.Run(c)
for {
var p Person
k, err := t.Next(&p)
if err == datastore.Done {
break // No further entities match the query.
}
if err != nil {
c.Errorf("fetching next Person: %v", err)
break
}
// Do something with Person p and Key k
}
使用 GetAll method
這個方式可以將搜尋結果一次取回來,可參考之前的範例!
使用 peojection query
可用來取得 entity 中部分的 property 的資訊。
使用 keys-only query
透過 KeyOnly method 僅取得 entity key。
使用 Limit & Offset (達到資料分頁目的)
可透過 Limit & Offset 兩個 method 來取得特定範圍的 entity 資料,達到資料分頁效果,範例如下:
//取得第 6~10 筆的資料
q := datastore.NewQuery("Person").Order("-Height").Limit(5).Offset(5)
比較需要注意的是, 雖然 Offset 可以忽略前幾筆資料,但其實資料還是有回傳的(這些都是要收費的),因此若是要 僅回傳 特定筆數資料,就必須使用 query cursor 來處理。
Query cursors
透過 query cursor 就可以取得特定範圍的資料而沒有 Offset 的問題。
當執行了一個取得資料的動作後,應用程式可以取得一個 cursor(base64 編碼過的字串),用來表是上一次取得結果的索引位置,如此一來就可以用 cursor 來取得下一批的資料。
除了可以透過 cursor 指定取得資料的起始點之外,還可以透過指定 end cursor 來限制回傳資料的範圍。
使用限制
cursor 在使用上有些限制存在:
cursor 僅有在相同的應用程式內,並使用相同的 query(相同的 kind & ancestor & property filter) 時才有效。
若用在有多個值的 property 上時,cursor 的運作可能會不如預期。
新的 App Engine 功能釋出時,可能會造成某些 cursor 不再有效,此時 Datastore 會回傳 error。
沒有留言:
張貼留言