1. 程式人生 > 實用技巧 >MongoEngine 查詢語法

MongoEngine 查詢語法

本文轉自:MongoEngine 查詢

資料庫查詢

Document物件有一個objects屬性,用來訪問在資料庫中跟這個類有關的物件。這個objects屬性其實是一個QuerySetManager,它會建立和返回一個新的QuerySet物件的訪問。這個QuerySet物件可以從資料庫中遍歷獲取的文件:

  1. # Prints out the names of all the users in the database
  2. for user in User.objects:
  3. print user.name

過濾查詢

可以通過呼叫QuerySet物件的關鍵字引數來對資料查詢進行過濾,關鍵字查詢中的鍵和你想要查詢的Document

中的欄位一致:

  1. # This will return a QuerySet that will only iterate over users whose
  2. # 'country' field is set to 'uk'
  3. uk_users = User.objects(country='uk')

對於內嵌document的欄位可以使用__來代替物件屬性訪問語法中的.進行訪問:

  1. # This will return a QuerySet that will only iterate over pages that have
  2. # been written by a user whose 'country' field is set to 'uk'
  3. uk_pages = Page.objects(author__country='uk')

查詢操作符

在查詢中也可以使用操作符,只要將其加在關鍵字的雙下劃線之後即可:

  1. # Only find users whose age is 18 or less
  2. young_users = Users.objects(age__lte=18)

可用的運算子如下:

  • ne– 不等於

  • lt– 小於<

  • lte– 小於等於

  • gt– 大於>

  • gte– 大於等於

  • not– 否定一個標準的檢查,需要用在其他操作符之前(e.g.Q(age__not__mod=5)

    )

  • in– 值在list

  • nin– 值不在list

  • modvalue%x==y, 其中xy為給定的值

  • alllist裡面所有的值

  • size– 陣列的大小

  • exists– 存在這個值

字串查詢

以下操作符可以快捷的進行正則查詢:

  • exact– 字串型欄位完全匹配這個值

  • iexact– 字串型欄位完全匹配這個值(大小寫敏感)

  • contains– 字串欄位包含這個值

  • icontains– 字串欄位包含這個值(大小寫敏感)

  • startswith– 字串欄位由這個值開頭

  • istartswith– 字串欄位由這個值開頭(大小寫敏感)

  • endswith– 字串欄位由這個值結尾

  • iendswith– 字串欄位由這個值結尾(大小寫敏感)

  • match– 執行$elemMatch操作,所以你可以使用一個數組中的 document 例項

地理查詢

PASS

列表查詢

對於大多數字段,這種語法會查詢出那些欄位與給出的值相匹配的document,但是當一個欄位引用ListField的時候,而只會提供一條資料,那麼包含這條資料的就會被匹配上:

  1. class Page(Document):
  2. tags = ListField(StringField())
  3. # This will match all pages that have the word 'coding' as an item in the
  4. # 'tags' list
  5. Page.objects(tags='coding')

原始查詢

你可以通過__raw__引數來使用一個原始的PyMongo語句來進行查詢,這樣可以進行原始的完整查詢:

Page.objects(__raw__={'tags':'coding'})

限制和跳過結果

就像傳統的ORM一樣,你有時候需要限制返回的結果的數量,或者需要跳過一定數量的結果。QuerySet裡面可以使用limit()skip()這兩個方法來實現,但是更推薦使用陣列切割的語法:

  1. # Only the first 5 people
  2. users = User.objects[:5]
  3. # All except for the first 5 people
  4. users = User.objects[5:]
  5. # 5 users, starting from the 11th user found
  6. users = User.objects[10:15]

你可以指定讓查詢返回一個結果。如果這個條在資料庫中不存在,那麼會引發IndexError錯誤 。使用first()方法在資料不存在的時候會返回None

  1. >>> # Make sure there are no users
  2. >>> User.drop_collection()
  3. >>> User.objects[0]
  4. IndexError: list index out of range
  5. >>> User.objects.first() == None
  6. True
  7. >>> User(name='Test User').save()
  8. >>> User.objects[0] == User.objects.first()
  9. True

預設Document查詢

預設情況下,Documentobjects屬性返回一個一個QuerySet物件,它並沒有進行任何篩選和過濾,它返回的是所有的資料物件。這一點可以通過給一個document定義一個方法來修改 一個queryset。這個方法需要兩引數__doc_clsqueryset。第一個引數是定義這個方法的Document類名(從這個意義上來說,這個方法像是一個classmethod()而不是一般的方法),第二個引數是初始化的queryset。這個方法需要使用queryset_manager()來裝飾來它,使得它被認可。

  1. class BlogPost(Document):
  2. title = StringField()
  3. date = DateTimeField()
  4. @queryset_manager
  5. def objects(doc_cls, queryset):
  6. # This may actually also be done by defining a default ordering for
  7. # the document, but this illustrates the use of manager methods
  8. return queryset.order_by('-date')

你不用呼叫objects方法,你可以自定義更多的管理方法,例如:

  1. class BlogPost(Document):
  2. title = StringField()
  3. published = BooleanField()
  4. @queryset_manager
  5. def live_posts(doc_cls, queryset):
  6. return queryset.filter(published=True)
  7. BlogPost(title='test1', published=False).save()
  8. BlogPost(title='test2', published=True).save()
  9. assert len(BlogPost.objects) == 2
  10. assert len(BlogPost.live_posts()) == 1

自定義 QuerySets

當你想自己定義一些方法來過濾document的時候,繼承QuerySet類對你來說就是個好的方法。為了在document裡面使用一個自定義的QuerySet類,你可以在document裡的meta字典裡設定queryset_class的值來實現它。

  1. class AwesomerQuerySet(QuerySet):
  2. def get_awesome(self):
  3. return self.filter(awesome=True)
  4. class Page(Document):
  5. meta = {'queryset_class': AwesomerQuerySet}
  6. # To call:
  7. 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

  1. class Article(Document):
  2. tag = ListField(StringField())
  3. # After adding some tagged articles...
  4. tag_freqs = Article.objects.item_frequencies('tag', normalize=True)
  5. from operator import itemgetter
  6. top_tags = sorted(tag_freqs.items(), key=itemgetter(1), reverse=True)[:10]

查詢效率和效能

PASS

高階查詢

有時候使用關鍵字引數返回的QuerySet不能完全滿足你的查詢需要。例如有時候你需要將約束條件進行andor的操作。你可以使用 MongoEngine 提供的Q類來實現,一個Q類代表了一個查詢的一部分,裡面的引數設定與你查詢document的時候相同。建立一個複雜查詢的時候,你需要用&|操作符將Q物件連結起來。例如:

  1. from mongoengine.queryset.visitor import Q
  2. # Get published posts
  3. Post.objects(Q(published=True) | Q(publish_date__lte=datetime.now()))
  4. # Get top posts
  5. 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>=valval的值決定刪除第一項還是最後一項,一般情況下,val為負則刪除第一項,為正則刪除最後一項,參見:mongodb $pop

  • pull– 從list裡面移除一個值

  • pull_all– 從list裡面移除個值

  • add_to_set– 當要新增的值不在list中時,新增這個值

原子更新的語法類似於查詢語法,區別在於修飾操作符位於欄位之前,而不是之後:

  1. >>> post = BlogPost(title='Test', page_views=0, tags=['database'])
  2. >>> post.save()
  3. >>> BlogPost.objects(id=post.id).update_one(inc__page_views=1)
  4. >>> post.reload() # the document has been changed, so we need to reload it
  5. >>> post.page_views
  6. 1
  7. >>> BlogPost.objects(id=post.id).update_one(set__title='Example Post')
  8. >>> post.reload()
  9. >>> post.title
  10. 'Example Post'
  11. >>> BlogPost.objects(id=post.id).update_one(push__tags='nosql')
  12. >>> post.reload()
  13. >>> post.tags
  14. ['database', 'nosql']

如果沒有修飾操作符,則預設為$set

  1. BlogPost.objects(id=post.id).update(title='Example Post')
  2. 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()函式的一些引數

  1. def sum_field(document, field_name, include_negatives=True):
  2. code = """
  3. function(sumField) {
  4. var total = 0.0;
  5. db[collection].find(query).forEach(function(doc) {
  6. var val = doc[sumField];
  7. if (val >= 0.0 || options.includeNegatives) {
  8. total += val;
  9. }
  10. });
  11. return total;
  12. }
  13. """
  14. options = {'includeNegatives': include_negatives}
  15. return document.objects.exec_js(code, field_name, **options)