NDB 查詢

應用程式可利用查詢功能,搜尋 Datastore 中符合篩選器特定搜尋條件的實體。

總覽

應用程式可利用查詢功能,搜尋 Datastore 中符合篩選器特定搜尋條件的實體。舉例來說,記錄多個留言板的應用程式可利用查詢功能,擷取其中一個留言版的訊息,並依日期排序:

from google.appengine.ext import ndb
...
class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)
...
class MainPage(webapp2.RequestHandler):
    GREETINGS_PER_PAGE = 20

    def get(self):
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key('Book', guestbook_name or '*notitle*')
        greetings = Greeting.query_book(ancestor_key).fetch(
            self.GREETINGS_PER_PAGE)

        self.response.out.write('<html><body>')

        for greeting in greetings:
            self.response.out.write(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        self.response.out.write('</body></html>')

有些查詢的結構更為複雜;資料儲存庫需要為這些查詢預先建構索引。這些預先建構的索引是在設定檔 index.yaml 中指定。在開發伺服器上,如果執行的查詢需要用到尚未指定的索引,開發伺服器會自動將其新增至 index.yaml。但在您的網站上,如有查詢需要用到尚未指定的索引,查詢將會失敗。因此,典型的開發週期是先在開發伺服器上嘗試執行新的查詢,然後再更新網站,以便使用自動變更的 index.yaml。若只要更新 index.yaml 而不上傳應用程式,您可以執行 gcloud app deploy index.yaml。如果您的資料儲存庫包含許多實體,就需要較長的時間為實體建立新索引;在此情況下,最好先更新索引定義再上傳使用新索引的程式碼。您可以使用「管理控制台」確認完成建構索引的時間。

App Engine Datastore 內建支援完全相符 (== 運算子) 和比較 (<、<=、> 及 >= 運算子) 篩選器,同時也支援使用布林值 AND 的多個篩選器進行合併運算,但有特定限制 (請見以下說明)。

除原生運算子外,API 還支援 != 運算子 (使用布林值 OR 運算結合多組篩選器),以及檢查是否等於其中一個可能清單值的 IN 運算 (類似 Python 的「in」運算子)。這些運算無法完全對應至 Datastore 的原生運算,因此相對而言較為複雜且速度緩慢。這些運算的實作是透過記憶體內的結果串合併作業進行。請注意,p != v 會實作為「p < v OR p > v」(這對重複屬性而言相當重要)。

限制事項:Datastore 會對查詢強制執行一些限制。違反這些限制會引發例外狀況。舉例來說,結合的篩選器過多、使用多個屬性的不等式運算,或在使用不等式運算的同時要求其他屬性依照特定順序排序,這些都是目前不允許的作業方式。此外,參照多個屬性的篩選器有時還需要設定第二個索引。

不支援項目:Datastore 不直接支援子字串的相符比對、不區分大小寫的相符比對或所謂的全文搜尋。您必須透過其他方式,利用運算屬性來實作不區分大小寫的相符比對,或甚至是全文搜尋。

依屬性值進行篩選

NDB 屬性回呼 Account 類別:

class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

您通常不會想要擷取指定種類的「所有」實體;您需要的只是具有特定屬性且為特定值或特定範圍值的實體。

屬性物件會超載一些運算子,以供傳回可用於控管查詢的篩選器運算式:舉例來說,如要尋找 userid 屬性值剛好為 42 的所有 Account 實體,您可以使用以下運算式:

query = Account.query(Account.userid == 42)

(如果確認該 userid 只有一個 Account,您可能會想要使用 userid 作為金鑰。 Account.get_by_id(...)的運算速度比 Account.query(...).get() 更快。)

NDB 支援下列運算:

property == value
property < value
property <= value
property > value
property >= value
property != value
property.IN([value1, value2])

如要篩選不等式,您可以使用類似以下的語法:

query = Account.query(Account.userid >= 40)

這會尋找 userid 屬性大於或等於 40 的所有 Account 實體。

在這些運算中,!= 和 IN 這兩個運算是以結合其他運算的方式進行實作,有點複雜 (如 != 和 IN 中所述)。

您可以指定多個篩選器:

query = Account.query(Account.userid >= 40, Account.userid < 50)

此範例結合指定的篩選器引數,會傳回其 userid 值大於或等於 40 且小於 50 的所有 Account 實體。

注意:如先前所述,Datastore 拒絕使用不等式篩選多個屬性的查詢。

相對於在單一運算式中指定完整的查詢篩選器,您可能會發現逐步建構查詢的方式會更為方便,例如:

query1 = Account.query()  # Retrieve all Account entitites
query2 = query1.filter(Account.userid >= 40)  # Filter on userid >= 40
query3 = query2.filter(Account.userid < 50)  # Filter on userid < 50 too

query3 等同於先前範例中的 query 變數。請注意,查詢物件無法變更,因此建構 query2 不會影響 query1,而建構 query3 不會影響 query1query2

!= 和 IN 運算

NDB 屬性回呼 Article 類別:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)

!= (不等於) 和 IN (成員資格) 運算是使用 OR 運算結合其他篩選器進行實作。就前者來說,

property != value

會實作為

(property < value) OR (property > value)

例如:

query = Article.query(Article.tags != 'perl')

等同於

query = Article.query(ndb.OR(Article.tags < 'perl',
                             Article.tags > 'perl'))

附註:這個查詢不會搜尋未包含「perl」標記的 Article 實體,這點可能超乎某些人的意料。相反地,此查詢會尋找至少有一個標記不等於「perl」的所有實體。舉例來說,查詢結果會出現以下實體,即使該實體將「perl」納為其中一個標記:

Article(title='Perl + Python = Parrot',
        stars=5,
        tags=['python', 'perl'])

但是查詢結果不會包含以下實體:

Article(title='Introduction to Perl',
        stars=3,
        tags=['perl'])

您無法查詢未含標記等於「perl」的實體。

同樣地,下列的 IN 運算

property IN [value1, value2, ...]

會檢查屬性值是否包含在可能值的清單中,並會以下列形式進行實作:

(property == value1) OR (property == value2) OR ...

例如:

query = Article.query(Article.tags.IN(['python', 'ruby', 'php']))

等同於

query = Article.query(ndb.OR(Article.tags == 'python',
                             Article.tags == 'ruby',
                             Article.tags == 'php'))

附註:使用 OR 刪除重複結果的查詢:即使這些實體可能符合兩個或兩個以上的子查詢,結果串不會出現相同實體超過一次。

查詢重複屬性

在上一節中定義的 Article 類別也提供如何查詢重複屬性的範例,特別是類似如下的篩選器

使用單一值,即使 Article.tags 為重複屬性也是如此。您無法將重複屬性與清單物件進行比對 (Datastore 無法辨別),而類似如下的篩選器

Article.tags.IN(['python', 'ruby', 'php'])

相對於搜尋標記值包含在 ['python', 'ruby', 'php'] 清單的 Article 實體,此查詢搜尋的是 tags 值 (視為清單) 包含「至少其中一個值」的實體。

查詢重複屬性值為 None 會出現未定義的行為;因此,請勿執行此操作。

結合 AND 與 OR 運算

您可以任意建立 ANDOR 運算的巢狀結構。例如:

query = Article.query(ndb.AND(Article.tags == 'python',
                              ndb.OR(Article.tags.IN(['ruby', 'jruby']),
                                     ndb.AND(Article.tags == 'php',
                                             Article.tags != 'perl'))))

然而,由於 OR 本身的實作方式,這種形式的查詢會過於複雜,可能會發生例外狀況的失敗情形。比較安全的做法是將這些篩選器正規化,確保運算式樹狀結構最上層 (最多) 僅有一個 OR 運算,且底下也僅有單層的 AND 運算。

如要執行這類正規化作業,您必須記住布林值邏輯的規則,以及 !=IN 篩選器實際的實作規則:

  1. !=IN 運算子展開至原始形式,其中 != 變成檢查小於或大於該值的屬性,而 IN 變成檢查等於清單中第一個值、第二個值,依此類推至最後一個值的屬性。
  2. 內含 OR 運算的 AND 運算,等同於原始 AND 運算元套用多個 AND 運算的 OR 運算,將單一 OR 運算元取代為原始的 OR 運算。舉例來說,AND(a, b, OR(c, d)) 等同於 OR(AND(a, b, c), AND(a, b, d))
  3. 如果 AND 運算的運算元本身也是 AND 運算,則可將巢狀結構的 AND 運算的運算元整合為以括號包含的 AND 運算。舉例來說,AND(a, b, AND(c, d)) 等同於 AND(a, b, c, d)
  4. 如果 OR 運算的運算元本身也是 OR 運算,則可將巢狀結構的 OR 運算的運算元整合為以括號包含的 OR 運算。舉例來說,OR(a, b, OR(c, d)) 等同於 OR(a, b, c, d)

如果使用比 Python 更簡單的標記法分階段將這些轉換套用到篩選器範例中,得到的結果如下:

  1. IN!= 運算子套用第 1 個規則:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         AND(tags == 'php',
             OR(tags < 'perl', tags > 'perl'))))
  2. AND 運算最內層的 OR 巢狀結構套用第 2 個規則:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         OR(AND(tags == 'php', tags < 'perl'),
            AND(tags == 'php', tags > 'perl'))))
  3. 對另一個 OR 運算內含的 OR 巢狀結構使用第 4 個規則:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         AND(tags == 'php', tags < 'perl'),
         AND(tags == 'php', tags > 'perl')))
  4. AND 運算內含的其他 OR 巢狀結構使用第 2 個規則:
    OR(AND(tags == 'python', tags == 'ruby'),
       AND(tags == 'python', tags == 'jruby'),
       AND(tags == 'python', AND(tags == 'php', tags < 'perl')),
       AND(tags == 'python', AND(tags == 'php', tags > 'perl')))
  5. 使用第 3 個規則收合其他巢狀結構的 AND 運算:
    OR(AND(tags == 'python', tags == 'ruby'),
       AND(tags == 'python', tags == 'jruby'),
       AND(tags == 'python', tags == 'php', tags < 'perl'),
       AND(tags == 'python', tags == 'php', tags > 'perl'))

注意:對於特定篩選器而言,這項正規化作業可能會引發組合爆炸。假設有個 AND 運算包含 3 個 OR 子句,且每個子句又有 2 個基本子句。經過正規化後,就會變成包含 8 個 AND 子句且每個子句有 3 個基本子句的 OR 運算,也就是說 6 個詞組會變成 24 個詞組。

指定排序順序

您可以使用 order() 方法指定查詢傳回其結果的順序。這個方法會使用各種引數,這些引數可能是屬性物件 (依遞增順序排序) 或其否定形式 (表示遞減順序)。例如:

query = Greeting.query().order(Greeting.content, -Greeting.date)

這會擷取所有 Greeting 實體並依 content 屬性值的大小以遞增方式進行排序。具備相同內容屬性的連續實體序列會依 date 屬性值的大小以遞減方式進行排序。您可以執行多個 order() 呼叫來達到相同的效果:

query = Greeting.query().order(Greeting.content).order(-Greeting.date)

附註:將篩選器與 order() 搭配使用時,Datastore 會拒絕特定組合。特別是使用不等式篩選器時,第一個排序順序 (如有的話) 必須指定相同屬性作為篩選器。此外,有時您也需要設定第二個索引。

祖系查詢

您可以透過祖系查詢對資料儲存庫進行同步一致的查詢,不過相同祖系的實體每秒只能寫入一次。以資料儲存庫中的客戶資料與其相關聯的購買資料為例,以下簡單比較祖系查詢與非祖系查詢兩者之間的取捨與結構。

在以下非祖系範例中,每個 Customer 在資料儲存庫中都有一個實體,而每個 Purchase 在資料儲存庫中也有一個實體,並有 KeyProperty 指向客戶。

class Customer(ndb.Model):
    name = ndb.StringProperty()

class Purchase(ndb.Model):
    customer = ndb.KeyProperty(kind=Customer)
    price = ndb.IntegerProperty()

如要找出屬於該客戶的所有購買資料,您可以使用以下查詢:

purchases = Purchase.query(
    Purchase.customer == customer_entity.key).fetch()

在此範例中,資料儲存庫提供高度的寫入總處理量,但僅具有最終一致性。如有加入新的購買資料,您可能會擷取到過時的資料。使用祖系查詢可避免這種行為發生。

如果是進行客戶與購買資料的祖系查詢,則仍會使用相同的結構,但會有兩個不同的實體:客戶部分相同,但在建立購買資料時,您已經不用再指定 KeyProperty()。這是因為使用祖系查詢時,您會呼叫建立購買實體時的客戶實體金鑰。

class Customer(ndb.Model):
    name = ndb.StringProperty()

class Purchase(ndb.Model):
    price = ndb.IntegerProperty()

每一筆購買資料都有一個金鑰,而客戶同樣也有專屬的金鑰,但購買資料的金鑰都會內嵌 customer_entity 的金鑰。請記住,每個祖系每秒限制只能寫入一次。以下是建立帶有祖系的實體:

purchase = Purchase(parent=customer_entity.key)

如要查詢特定客戶的購買資料,請使用以下查詢。

purchases = Purchase.query(ancestor=customer_entity.key).fetch()

查詢屬性

查詢物件具有下列唯讀資料屬性:

屬性類型預設值說明
kind str None 種類名稱 (通常為類別名稱)
ancestor Key None 查詢的指定祖系
filters FilterNode None 篩選器運算式
orders Order None 排序順序

列印查詢物件 (或對其呼叫 str()repr()) 會產生格式完整的字串:

print(Employee.query())
# -> Query(kind='Employee')
print(Employee.query(ancestor=ndb.Key(Manager, 1)))
# -> Query(kind='Employee', ancestor=Key('Manager', 1))

篩選結構化屬性值

查詢可直接針對結構化屬性的欄位值進行篩選。舉例來說,如要查詢地址的城市位於 'Amsterdam' 的所有聯絡人:

query = Contact.query(Contact.addresses.city == 'Amsterdam')

如果結合多個此類篩選器來進行查詢,篩選器可能會找出相同 Contact 實體的「不同」Address 子實體。例如:

query = Contact.query(Contact.addresses.city == 'Amsterdam',  # Beware!
                      Contact.addresses.street == 'Spear St')

這可能會尋找地址的城市為 'Amsterdam' 以及地址的街道為 'Spear St' (不同地址) 的聯絡人。不過至少就等式篩選器而言,您可以建立查詢來傳回單一子實體符合多個值的結果:

query = Contact.query(Contact.addresses == Address(city='San Francisco',
                                                   street='Spear St'))

如果採用這個方法,會在查詢中忽略屬性為 None 的子實體。如果屬性具有預設值,您必須明確將其設為 None 以在查詢中忽略這些屬性,否則查詢會包含要求該屬性值等於預設值的篩選條件。舉例來說,如果 Address 模型有個 country 屬性為 default='us',上述範例僅會傳回國家/地區為 'us' 的聯絡人;如要考量其他國家/地區值的聯絡人,您需要使用 Address(city='San Francisco', street='Spear St', country=None) 的篩選條件。

屬性值等於 None 的任何子實體會遭到忽略。因此,篩選屬性值為 None 的子實體不具任何意義。

使用字串命名的屬性

有時候,您可能會想依據字串指定的屬性名稱來篩選或排序查詢。舉例來說,如果您讓使用者輸入類似 tags:python 的搜尋查詢,改為類似下列查詢可能會比較方便:

Article.query(Article."tags" == "python") # does NOT work

如果模型為 Expando,則篩選器可使用 GenericProperty,而 Expando 類別可用於動態屬性:

property_to_query = 'location'
query = FlexEmployee.query(ndb.GenericProperty(property_to_query) == 'SF')

GenericProperty 也適用於模型並非 Expando 的情形,但如果您要確保您只會使用定義的屬性名稱,您也可以使用 _properties 類別屬性

query = Article.query(Article._properties[keyword] == value)

或使用 getattr() 從類別取得:

query = Article.query(getattr(Article, keyword) == value)

兩者的不同在於 getattr() 使用屬性的「屬性名稱」,而 _properties 則是依照屬性的「資料儲存庫名稱」進行索引。這只有在宣告的屬性類似以下時才會有所不同:

class ArticleWithDifferentDatastoreName(ndb.Model):
    title = ndb.StringProperty('t')

此處的 Python 名稱為 title,但資料儲存庫名稱為 t

這些方法也適用排序查詢結果:

expando_query = FlexEmployee.query().order(ndb.GenericProperty('location'))

property_query = Article.query().order(Article._properties[keyword])

查詢疊代器

查詢過程的狀態資訊會保留在疊代器物件中 (大部分應用程式不會直接使用這項資訊;一般常見的做法是呼叫 fetch(20),而非使用疊代器物件)。 有兩個基本方法可取得這類物件:

  • Query 物件使用 Python 內建的 iter() 函式
  • 呼叫 Query 物件的 iter() 方法

前者支援使用 Python for 循環 (間接呼叫 iter() 函式) 以循環執行查詢。

for greeting in greetings:
    self.response.out.write(
        '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

後者使用 Query 物件的 iter() 方法傳送選項給疊代器來影響其行為。舉例來說,如要在 for 循環中使用僅限金鑰的查詢,您可以寫入:

for key in query.iter(keys_only=True):
    print(key)

查詢疊代器還提供其他實用方法:

方法說明
__iter__() Python 的疊代器通訊協定部分。
next() 傳回下一個結果,或在沒有下一個結果時發出 StopIteration 例外狀況。

has_next() 如果後續的 next() 呼叫會傳回結果,則傳回 True;如果是發出 StopIteration,則傳回 False

在這個問題出現已知答案前會封鎖,並在您透過 next() 擷取結果時緩衝處理結果 (如有的話)。
probably_has_next() 類似於 has_next(),但使用較快 (但有時不正確) 的捷徑。

可能傳回錯誤的肯定結果 (在 next() 實際上是發出 StopIteration 時傳回 True),但不會傳回錯誤的否定結果 (在 next() 實際上有傳回結果時傳回 False)。
cursor_before() 傳回查詢游標,代表傳回的是最後一個結果之前的小點。

在沒有可用的游標時發出例外狀況 (特別是未傳送 produce_cursors 查詢選項的情形)。
cursor_after() 傳回查詢游標,代表的是傳回最後一個結果之後的小點。

在沒有可用的游標時發出例外狀況 (特別是未傳送 produce_cursors 查詢選項的情形)。
index_list() 傳回已執行的查詢使用的索引清單,包括主要、複合、種類及單一屬性索引。

查詢游標

「查詢游標」是小型的不透明資料結構,代表查詢的繼續執行點。查詢游標可用來讓使用者一次檢視一頁的結果,也可用來處理可能需要停止後再繼續執行的長期工作。查詢游標通常會與查詢的 fetch_page() 方法搭配使用。其運作方式類似 fetch(),但會傳回三個值:(results, cursor, more)。傳回 more 標記代表可能還有更多結果;舉例來說,UI 可使用這個標記取代 [下一頁] 按鈕或連結。如需要求後續頁面,必須將 fetch_page() 呼叫傳回的游標傳送給下個呼叫。如果傳送無效的游標,則會發出 BadArgumentError。請注意,驗證作業僅會檢查其值是否採用 base64 編碼。您必須執行後續的必要驗證。

因此,為了讓使用者查看符合查詢的所有實體,將這些實體擷取在同一頁,使用的程式碼可能類似:

from google.appengine.datastore.datastore_query import Cursor
...
class List(webapp2.RequestHandler):
    GREETINGS_PER_PAGE = 10

    def get(self):
        """Handles requests like /list?cursor=1234567."""
        cursor = Cursor(urlsafe=self.request.get('cursor'))
        greets, next_cursor, more = Greeting.query().fetch_page(
            self.GREETINGS_PER_PAGE, start_cursor=cursor)

        self.response.out.write('<html><body>')

        for greeting in greets:
            self.response.out.write(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        if more and next_cursor:
            self.response.out.write('<a href="/list?cursor=%s">More...</a>' %
                                    next_cursor.urlsafe())

        self.response.out.write('</body></html>')

請留意,此處使用 urlsafe()Cursor(urlsafe=s) 進行游標的序列化與取消序列化作業。透過此方式,您可以在回應要求時將游標傳送給網路用戶端,然後在後續要求中從該用戶端接收到游標。

附註:即使已經沒有其他結果,fetch_page() 方法通常還是會傳回游標,但這並非絕對:傳回的游標值可能為 None。另外也請留意,由於 more 標記是使用疊代器的 probably_has_next() 方法進行實作,在罕見情況下,即使下一頁空白,可能還是會傳回 True

有些 NDB 查詢不支援查詢游標,但您可以修正這個問題。如果查詢使用 INOR!=,則「除非」依照金鑰排序,否則查詢結果無法使用游標。如果應用程式並未依金鑰排序結果並呼叫 fetch_page(),則會得到 BadArgumentError。如果 User.query(User.name.IN(['Joe', 'Jane'])).order(User.name).fetch_page(N) 發生錯誤,請將其變更為 User.query(User.name.IN(['Joe', 'Jane'])).order(User.name, User.key).fetch_page(N)

相對於「分頁」瀏覽查詢結果,您可以使用查詢的 iter() 方法取得確切位置的游標。如要執行此操作,請將 produce_cursors=True 傳送至 iter();在疊代器位於正確位置時,呼叫 cursor_after() 以取得位於該位置之後的游標。(同樣地,您也可以呼叫 cursor_before() 取得位於該位置之前的游標。)請注意,呼叫 cursor_after()cursor_before() 可能會產生封鎖的 Datastore 呼叫,重新執行部分的查詢來擷取指向批次中間的游標。

如要使用游標在查詢結果中往前翻頁,請建立反向查詢:

# Set up.
q = Bar.query()
q_forward = q.order(Bar.key)
q_reverse = q.order(-Bar.key)

# Fetch a page going forward.
bars, cursor, more = q_forward.fetch_page(10)

# Fetch the same page going backward.
r_bars, r_cursor, r_more = q_reverse.fetch_page(10, start_cursor=cursor)

針對各個實體呼叫函式 (「對應」)

假設您需要取得查詢傳回的 Message 實體所對應的 Account 實體。您可以寫入類似下方的內容:

message_account_pairs = []
for message in message_query:
    key = ndb.Key('Account', message.userid)
    account = key.get()
    message_account_pairs.append((message, account))

不過,這種作業方式相當沒有效率:您必須先等待擷取實體,然後使用該實體,接著再等待下個實體,然後再使用該實體。大多數的時間都是在等待。另一種做法是寫入對應查詢結果的回呼函式:

def callback(message):
    key = ndb.Key('Account', message.userid)
    account = key.get()
    return message, account

message_account_pairs = message_query.map(callback)
# Now message_account_pairs is a list of (message, account) tuples.

由於這個版本的部分作業可並行執行,因此作業速度會比先前的簡易 for 循環來得快一些。然而,由於 callback() 中的 get() 呼叫依然會同步執行,因此效益不大。建議您在這個情況下使用非同步擷取的做法。

GQL

GQL 是類似 SQL 的語言,可從 App Engine Datastore 中擷取實體或金鑰。雖然 GQL 的功能與傳統關聯資料庫的查詢語言不同,但 GQL 的語法與 SQL 類似。如需 GQL 語法的相關說明,請參閱 GQL 參考資料

您可以使用 GQL 建構查詢。這類似於使用 Model.query() 建立查詢,但這會使用 GQL 語法來定義查詢篩選器與排序。使用說明如下:

  • ndb.gql(querystring) 會傳回 Query 物件 (與 Model.query() 傳回的類型相同)。所有常見的方法在這些 Query 物件上都可使用 (包括 fetch()map_async()filter() 等)。
  • Model.gql(querystring)ndb.gql("SELECT * FROM Model " + querystring) 的簡寫。一般而言,querystring 會是類似以下的字串:"WHERE prop1 > 0 AND prop2 = TRUE"
  • 如要查詢包含結構化屬性的模型,您可以在 GQL 語法中使用 foo.bar 參照子屬性。
  • GQL 支援類似 SQL 的參數繫結。應用程式可定義查詢並且將值繫結到查詢中:
    query = ndb.gql("SELECT * FROM Article WHERE stars > :1")
    query2 = query.bind(3)
    
    或是
    query = ndb.gql("SELECT * FROM Article WHERE stars > :1", 3)

    呼叫查詢的 bind() 函式會傳回新的查詢;這不會變更原始查詢。

  • 如果模型類別覆寫了 _get_kind() 類別方法,GQL 查詢應使用該函式傳回的種類,而非類別名稱。
  • 如果模型中的屬性覆寫了其名稱 (例如 foo = StringProperty('bar')),GQL 查詢應使用已覆寫的屬性名稱 (即範例中的 bar)。

如果查詢有部分值為使用者提供的變數,則請務必使用參數繫結功能。這可避免駭客透過語法進行攻擊。

查詢尚未匯入或 (更廣泛來說) 尚未定義的模型會發生錯誤。

除非模型為 Expando,否則使用模型類別尚未定義的屬性名稱也會發生錯誤。

針對查詢的 fetch() 所指定的限制或位移,將會覆寫 GQL 的 OFFSETLIMIT 子句設定的限制或位移。請勿將 GQL 的 OFFSETLIMITfetch_page() 搭配使用。請注意,App Engine 對查詢設定的 1,000 個結果數上限,同樣也會套用到此處的位移和限制。

如果您習慣使用 SQL,請注意在使用 GQL 時會採取錯誤的假設。GQL 會解譯為 NDB 的原生查詢 API。這跟一般的物件關聯 Mapper 不同 (例如 SQLAlchemy 或 Django 的資料庫支援),其中 API 呼叫會先解譯為 SQL,然後才傳輸到資料庫伺服器。GQL 不支援對 Datastore 進行修改 (插入、刪除或更新);GQL 僅支援查詢。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境