MongoEngine 查詢語法
本文轉自:MongoEngine 查詢
資料庫查詢
Document
物件有一個objects
屬性,用來訪問在資料庫中跟這個類有關的物件。這個objects
屬性其實是一個QuerySetManager
,它會建立和返回一個新的QuerySet
物件的訪問。這個QuerySet
物件可以從資料庫中遍歷獲取的文件:
- # Prints out the names of all the users in the database
- for user in User.objects:
- print user.name
過濾查詢
可以通過呼叫QuerySet
物件的關鍵字引數來對資料查詢進行過濾,關鍵字查詢中的鍵和你想要查詢的Document
- # This will return a QuerySet that will only iterate over users whose
- # 'country' field is set to 'uk'
- uk_users = User.objects(country='uk')
對於內嵌document的欄位可以使用__
來代替物件屬性訪問語法中的.
進行訪問:
- # This will return a QuerySet that will only iterate over pages that have
-
# been written by a user whose 'country' field is set to 'uk'
- uk_pages = Page.objects(author__country='uk')
查詢操作符
在查詢中也可以使用操作符,只要將其加在關鍵字的雙下劃線之後即可:
- # Only find users whose age is 18 or less
- young_users = Users.objects(age__lte=18)
可用的運算子如下:
-
ne
– 不等於≠
-
lt
– 小於<
-
lte
– 小於等於≤
-
gt
– 大於>
-
gte
– 大於等於≥
-
not
– 否定一個標準的檢查,需要用在其他操作符之前(e.g.Q(age__not__mod=5)
-
in
– 值在list
中 -
nin
– 值不在list
中 -
mod
–value%x==y
, 其中x
和y
為給定的值 -
all
–list
裡面所有的值 -
size
– 陣列的大小 -
exists
– 存在這個值
字串查詢
以下操作符可以快捷的進行正則查詢:
-
exact
– 字串型欄位完全匹配這個值 -
iexact
– 字串型欄位完全匹配這個值(大小寫敏感) -
contains
– 字串欄位包含這個值 -
icontains
– 字串欄位包含這個值(大小寫敏感) -
startswith
– 字串欄位由這個值開頭 -
istartswith
– 字串欄位由這個值開頭(大小寫敏感) -
endswith
– 字串欄位由這個值結尾 -
iendswith
– 字串欄位由這個值結尾(大小寫敏感) -
match
– 執行$elemMatch
操作,所以你可以使用一個數組中的 document 例項
地理查詢
PASS
列表查詢
對於大多數字段,這種語法會查詢出那些欄位與給出的值相匹配的document,但是當一個欄位引用ListField
的時候,而只會提供一條資料,那麼包含這條資料的就會被匹配上:
- class Page(Document):
- tags = ListField(StringField())
- # This will match all pages that have the word 'coding' as an item in the
- # 'tags' list
- Page.objects(tags='coding')
原始查詢
你可以通過__raw__
引數來使用一個原始的PyMongo
語句來進行查詢,這樣可以進行原始的完整查詢:
Page.objects(__raw__={'tags':'coding'})
限制和跳過結果
就像傳統的ORM一樣,你有時候需要限制返回的結果的數量,或者需要跳過一定數量的結果。QuerySet
裡面可以使用limit()
和skip()
這兩個方法來實現,但是更推薦使用陣列切割的語法:
- # Only the first 5 people
- users = User.objects[:5]
- # All except for the first 5 people
- users = User.objects[5:]
- # 5 users, starting from the 11th user found
- users = User.objects[10:15]
你可以指定讓查詢返回一個結果。如果這個條在資料庫中不存在,那麼會引發IndexError
錯誤 。使用first()
方法在資料不存在的時候會返回None
:
- IndexError: list index out of range
- True
- True
預設Document
查詢
預設情況下,Document
的objects
屬性返回一個一個QuerySet
物件,它並沒有進行任何篩選和過濾,它返回的是所有的資料物件。這一點可以通過給一個document
定義一個方法來修改 一個queryset
。這個方法需要兩引數__doc_cls
和queryset
。第一個引數是定義這個方法的Document
類名(從這個意義上來說,這個方法像是一個classmethod()
而不是一般的方法),第二個引數是初始化的queryset
。這個方法需要使用queryset_manager()
來裝飾來它,使得它被認可。
- class BlogPost(Document):
- title = StringField()
- date = DateTimeField()
- def objects(doc_cls, queryset):
- # This may actually also be done by defining a default ordering for
- # the document, but this illustrates the use of manager methods
- return queryset.order_by('-date')
你不用呼叫objects
方法,你可以自定義更多的管理方法,例如:
- class BlogPost(Document):
- title = StringField()
- published = BooleanField()
- def live_posts(doc_cls, queryset):
- return queryset.filter(published=True)
- BlogPost(title='test1', published=False).save()
- BlogPost(title='test2', published=True).save()
- assert len(BlogPost.objects) == 2
- assert len(BlogPost.live_posts()) == 1
自定義 QuerySets
當你想自己定義一些方法來過濾document
的時候,繼承QuerySet
類對你來說就是個好的方法。為了在document
裡面使用一個自定義的QuerySet
類,你可以在document
裡的meta
字典裡設定queryset_class
的值來實現它。
- class AwesomerQuerySet(QuerySet):
- def get_awesome(self):
- return self.filter(awesome=True)
- class Page(Document):
- meta = {'queryset_class': AwesomerQuerySet}
- # To call:
- Page.objects.get_awesome()
Aggregation 聚合
MongoDB 提供了開箱即用的聚合方法,但沒有 RDBMS 提供的那樣多。MongoEngine 提供了一個包裝過的內建的方法,同時自身提供了一些方法,它實現了在資料庫服務上執行的 Javascript 程式碼的功能。
結果計數
就像限制和跳過結果一樣,QuerySet
物件提供了用來計數的方法 -count()
,不過還有一個更Pythonic
的方法來實現:
num_users = len(User.objects)
更多功能
當你想為document
的特定的欄位的數量計數的時候,可以使用sum()
:
yearly_expense = Employee.objects.sum('salary')
當你想求某個欄位的平均值的時候,可以使用average()
:
mean_age = User.objects.average('age')
MongoEngine 提供了一個方法來獲取一個在集合裡item
的頻率 -item_frequencies()
。下面一個例子可以生成tag-clouds
:
- class Article(Document):
- tag = ListField(StringField())
- # After adding some tagged articles...
- tag_freqs = Article.objects.item_frequencies('tag', normalize=True)
- from operator import itemgetter
- top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10]
查詢效率和效能
PASS
高階查詢
有時候使用關鍵字引數返回的QuerySet
不能完全滿足你的查詢需要。例如有時候你需要將約束條件進行and
,or
的操作。你可以使用 MongoEngine 提供的Q
類來實現,一個Q
類代表了一個查詢的一部分,裡面的引數設定與你查詢document
的時候相同。建立一個複雜查詢的時候,你需要用&
或|
操作符將Q
物件連結起來。例如:
- from mongoengine.queryset.visitor import Q
- # Get published posts
- Post.objects(Q(published=True) | Q(publish_date__lte=datetime.now()))
- # Get top posts
- Post.objects((Q(featured=True) & Q(hits__gte=1000)) | Q(hits__gte=5000))
Atomic updates(原子更新)
MongoDB 文件
可以通過QuerySet
上的update_one()
、update()
、modify()
方法自動更新。下面幾種操作符可以被用到這幾種方法上:
-
set
– 設定成一個指定的值 -
unset
– 刪除一個指定的值 -
inc
– 將值加上一個給定的數 -
dec
– 將值減去一個給定的數 -
push
– 在list
中新增一個值 -
push_all
– 在list
中新增一個值 -
pop
– 移除list
的第一項或最後一項(根據pop__<field>=val
中val
的值決定刪除第一項還是最後一項,一般情況下,val
為負則刪除第一項,為正則刪除最後一項,參見:mongodb $pop -
pull
– 從list
裡面移除一個值 -
pull_all
– 從list
裡面移除個值 -
add_to_set
– 當要新增的值不在list
中時,新增這個值
原子更新的語法類似於查詢語法,區別在於修飾操作符位於欄位之前,而不是之後:
- 1
- 'Example Post'
- ['database', 'nosql']
如果沒有修飾操作符,則預設為$set
:
- BlogPost.objects(id=post.id).update(title='Example Post')
- BlogPost.objects(id=post.id).update(set__title='Example Post')
伺服器端 JavaScript 執行
可以寫 Javascript函式,然後傳送到伺服器來執行。它返回結果是 Javascript 函式的返回值。這個功能是通過QuerySet()
物件的exec_js()
方法實現。傳遞一個包含一個Javascript函式的字串作為第一個引數。
其餘位置的引數的名字欄位將作為您的Javascript函式的引數傳遞過去。
在 JavaScript 函式範圍中,一些變數可用:
-
collection
– 對應使用的Document
類的集合的名稱 -
query
– 一個QuerySet
物件 -
options
– 一個物件,它包含要傳遞給exec_js()
函式的一些引數
- def sum_field(document, field_name, include_negatives=True):
- code = """
- function(sumField) {
- var total = 0.0;
- db[collection].find(query).forEach(function(doc) {
- var val = doc[sumField];
- if (val >= 0.0 || options.includeNegatives) {
- total += val;
- }
- });
- return total;
- }
- """
- options = {'includeNegatives': include_negatives}
- return document.objects.exec_js(code, field_name, **options)