MongoDB_5:MongoEngine 查询
<h3>转自:MongoEngine 查询(翻译)</h3><p> </p>
<h2 id="item-1">数据库查询</h2>
<p><code>Document</code> 对象有一个 <code>objects</code> 属性,用来访问在数据库中跟这个类有关的对象。这个 <code>objects</code> 属性其实是一个<code>QuerySetManager</code> ,它会创建和返回一个新的 <code>QuerySet</code> 对象的访问。这个 <code>QuerySet</code> 对象可以从数据库中遍历获取的文档:</p>
<pre class="python hljs"><code class="python"><span class="hljs-comment"># Prints out the names of all the users in the database
<span class="hljs-keyword">for user <span class="hljs-keyword">in User.objects:
<span class="hljs-built_in">print user.name</span></span></span></span></code></pre>
<h3 id="item-1-1">过滤查询</h3>
<p>可以通过调用 <code>QuerySet</code> 对象的关键字参数来对数据查询进行过滤,关键字查询中的键和你想要查询的<code>Document</code> 中的字段一致:</p>
<pre class="python hljs"><code class="python"><span class="hljs-comment"># This will return a QuerySet that will only iterate over users whose
<span class="hljs-comment"># 'country' field is set to 'uk'
uk_users = User.objects(country=<span class="hljs-string">'uk')</span></span></span></code></pre>
<p>对于内嵌document的字段可以使用 <code>__</code> 来代替对象属性访问语法中的 <code>.</code> 进行访问:</p>
<pre class="python hljs"><code class="python"><span class="hljs-comment"># This will return a QuerySet that will only iterate over pages that have
<span class="hljs-comment"># been written by a user whose 'country' field is set to 'uk'
uk_pages = Page.objects(author__country=<span class="hljs-string">'uk')</span></span></span></code></pre>
<h3 id="item-1-2">查询操作符</h3>
<p>在查询中也可以使用操作符,只要将其加在关键字的双下划线之后即可:</p>
<pre class="python hljs"><code class="python"><span class="hljs-comment"># Only find users whose age is 18 or less
young_users = Users.objects(age__lte=<span class="hljs-number">18)</span></span></code></pre>
<p>可用的运算符如下:</p>
<ul>
<li>
<p><code>ne</code> – 不等于<code>≠</code></p>
</li>
<li>
<p><code>lt</code> – 小于<code><</code></p>
</li>
<li>
<p><code>lte</code> – 小于等于<code>≤</code></p>
</li>
<li>
<p><code>gt</code> – 大于<code>></code></p>
</li>
<li>
<p><code>gte</code> – 大于等于 <code>≥</code></p>
</li>
<li>
<p><code>not</code> – 否定一个标准的检查,需要用在其他操作符之前(e.g. <code>Q(age__not__mod=5)</code>)</p>
</li>
<li>
<p><code>in</code> – 值在 <code>list</code> 中</p>
</li>
<li>
<p><code>nin</code> – 值不在 <code>list</code> 中</p>
</li>
<li>
<p><code>mod</code> – <code>value % x == y</code>, 其中 <code>x</code> 和 <code>y</code> 为给定的值</p>
</li>
<li>
<p><code>all</code> – <code>list</code> 里面所有的值</p>
</li>
<li>
<p><code>size</code> – 数组的大小</p>
</li>
<li>
<p><code>exists</code> – 存在这个值</p>
</li>
</ul>
<h4>字符串查询</h4>
<p>以下操作符可以快捷的进行正则查询:</p>
<ul>
<li>
<p><code>exact</code> – 字符串型字段完全匹配这个值</p>
</li>
<li>
<p><code>iexact</code> – 字符串型字段完全匹配这个值(大小写敏感)</p>
</li>
<li>
<p><code>contains</code> – 字符串字段包含这个值</p>
</li>
<li>
<p><code>icontains</code> – 字符串字段包含这个值(大小写敏感)</p>
</li>
<li>
<p><code>startswith</code> – 字符串字段由这个值开头</p>
</li>
<li>
<p><code>istartswith</code> – 字符串字段由这个值开头(大小写敏感)</p>
</li>
<li>
<p><code>endswith</code> – 字符串字段由这个值结尾</p>
</li>
<li>
<p><code>iendswith</code> – 字符串字段由这个值结尾(大小写敏感)</p>
</li>
<li>
<p><code>match</code> – 执行 <code>$elemMatch</code> 操作,所以你可以使用一个数组中的 document 实例</p>
</li>
</ul>
<h4>地理查询</h4>
<p>PASS</p>
<h4>列表查询</h4>
<p>对于大多数字段,这种语法会查询出那些字段与给出的值相匹配的document,但是当一个字段引用 <code>ListField</code> 的时候,而只会提供一条数据,那么包含这条数据的就会被匹配上:</p>
<pre class="python hljs"><code class="python"><span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">Page(<span class="hljs-params">Document):
tags = ListField(StringField())
<span class="hljs-comment"># This will match all pages that have the word 'coding' as an item in the
<span class="hljs-comment"># 'tags' list
Page.objects(tags=<span class="hljs-string">'coding')</span></span></span></span></span></span></span></code></pre>
<h4>原始查询</h4>
<p>你可以通过 <code>__raw__</code> 参数来使用一个原始的 <code>PyMongo</code> 语句来进行查询,这样可以进行原始的完整查询:</p>
<pre class="python hljs"><code class="python">Page.objects(__raw__={<span class="hljs-string">'tags': <span class="hljs-string">'coding'})</span></span></code></pre>
<h3 id="item-1-3">限制和跳过结果</h3>
<p>就像传统的ORM一样,你有时候需要限制返回的结果的数量,或者需要跳过一定数量的结果。<code>QuerySet</code> 里面可以使用 <code>limit()</code> 和 <code>skip()</code> 这两个方法来实现,但是更推荐使用数组切割的语法:</p>
<pre class="python hljs"><code class="python"><span class="hljs-comment"># Only the first 5 people
users = User.objects[:<span class="hljs-number">5]
<span class="hljs-comment"># All except for the first 5 people
users = User.objects[<span class="hljs-number">5:]
<span class="hljs-comment"># 5 users, starting from the 11th user found
users = User.objects[<span class="hljs-number">10:<span class="hljs-number">15]</span></span></span></span></span></span></span></code></pre>
<p>你可以指定让查询返回一个结果。如果这个条在数据库中不存在,那么会引发 <code>IndexError</code> 错误 。使用 <code>first()</code> 方法在数据不存在的时候会返回 <code>None</code>:</p>
<pre class="python hljs"><code class="python"><span class="hljs-meta">>>> <span class="hljs-comment"># Make sure there are no users
<span class="hljs-meta">>>> User.drop_collection()
<span class="hljs-meta">>>> User.objects[<span class="hljs-number">0]
IndexError: <span class="hljs-built_in">list index out of <span class="hljs-built_in">range
<span class="hljs-meta">>>> User.objects.first() == <span class="hljs-literal">None
<span class="hljs-literal">True
<span class="hljs-meta">>>> User(name=<span class="hljs-string">'Test User').save()
<span class="hljs-meta">>>> User.objects[<span class="hljs-number">0] == User.objects.first()
<span class="hljs-literal">True</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="item-1-4">默认<code>Document</code> 查询</h3>
<p>默认情况下,<code>Document</code>的<code>objects</code> 属性返回一个一个 <code>QuerySet</code> 对象,它并没有进行任何筛选和过滤,它返回的是所有的数据对象。这一点可以通过给一个 <code>document</code> 定义一个方法来修改 一个<code>queryset</code> 。这个方法需要两参数<code>__doc_cls</code> 和 <code>queryset</code> 。第一个参数是定义这个方法的 <code>Document</code> 类名(从这个意义上来说,这个方法像是一个 <code>classmethod()</code> 而不是一般的方法),第二个参数是初始化的 <code>queryset</code>。这个方法需要使用 <code>queryset_manager()</code>来装饰来它,使得它被认可。</p>
<pre class="python hljs"><code class="python"><span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">BlogPost(<span class="hljs-params">Document):
title = StringField()
date = DateTimeField()
<span class="hljs-meta"> @queryset_manager
<span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">objects(<span class="hljs-params">doc_cls, queryset):
<span class="hljs-comment"># This may actually also be done by defining a default ordering for
<span class="hljs-comment"># the document, but this illustrates the use of manager methods
<span class="hljs-keyword">return queryset.order_by(<span class="hljs-string">'-date')</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>你不用调用 <code>objects</code> 方法,你可以自定义更多的管理方法,例如:</p>
<pre class="python hljs"><code class="python"><span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">BlogPost(<span class="hljs-params">Document):
title = StringField()
published = BooleanField()
<span class="hljs-meta"> @queryset_manager
<span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">live_posts(<span class="hljs-params">doc_cls, queryset):
<span class="hljs-keyword">return queryset.<span class="hljs-built_in">filter(published=<span class="hljs-literal">True)
BlogPost(title=<span class="hljs-string">'test1', published=<span class="hljs-literal">False).save()
BlogPost(title=<span class="hljs-string">'test2', published=<span class="hljs-literal">True).save()
<span class="hljs-keyword">assert <span class="hljs-built_in">len(BlogPost.objects) == <span class="hljs-number">2
<span class="hljs-keyword">assert <span class="hljs-built_in">len(BlogPost.live_posts()) == <span class="hljs-number">1</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="item-1-5">自定义 QuerySets</h3>
<p>当你想自己定义一些方法来过滤 <code>document</code> 的时候,继承 <code>QuerySet</code> 类对你来说就是个好的方法。为了在 <code>document</code> 里面使用一个自定义的 <code>QuerySet</code> 类,你可以在 <code>document</code> 里的 <code>meta</code> 字典里设置 <code>queryset_class</code> 的值来实现它。</p>
<pre class="python hljs"><code class="python"><span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">AwesomerQuerySet(<span class="hljs-params">QuerySet):
<span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">get_awesome(<span class="hljs-params">self):
<span class="hljs-keyword">return self.<span class="hljs-built_in">filter(awesome=<span class="hljs-literal">True)
<span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">Page(<span class="hljs-params">Document):
meta = {<span class="hljs-string">'queryset_class': AwesomerQuerySet}
<span class="hljs-comment"># To call:
Page.objects.get_awesome()</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="item-1-6">Aggregation 聚合</h3>
<p>MongoDB 提供了开箱即用的聚合方法,但没有 RDBMS 提供的那样多。MongoEngine 提供了一个包装过的内置的方法,同时自身提供了一些方法,它实现了在数据库服务上执行的 Javascript 代码的功能。</p>
<h4>结果计数</h4>
<p>就像限制和跳过结果一样, <code>QuerySet</code> 对象提供了用来计数的方法 - <code>count()</code>,不过还有一个更 <code>Pythonic</code> 的方法来实现:</p>
<pre class="python hljs"><code class="python">num_users = <span class="hljs-built_in">len(User.objects)</span></code></pre>
<h4>更多功能</h4>
<p>当你想为 <code>document</code> 的特定的字段的数量计数的时候,可以使用 <code>sum()</code>:</p>
<pre class="python hljs"><code class="python">yearly_expense = Employee.objects.<span class="hljs-built_in">sum(<span class="hljs-string">'salary')</span></span></code></pre>
<p>当你想求某个字段的平均值的时候,可以使用 <code>average()</code>:</p>
<pre class="python hljs"><code class="python">mean_age = User.objects.average(<span class="hljs-string">'age')</span></code></pre>
<p>MongoEngine 提供了一个方法来获取一个在集合里 <code>item</code> 的频率 - <code>item_frequencies()</code>。下面一个例子可以生成 <code>tag-clouds</code>:</p>
<pre class="python hljs"><code class="python"><span class="hljs-class"><span class="hljs-keyword">class <span class="hljs-title">Article(<span class="hljs-params">Document):
tag = ListField(StringField())
<span class="hljs-comment"># After adding some tagged articles...
tag_freqs = Article.objects.item_frequencies(<span class="hljs-string">'tag', normalize=<span class="hljs-literal">True)
<span class="hljs-keyword">from operator <span class="hljs-keyword">import itemgetter
top_tags = <span class="hljs-built_in">sorted(tag_freqs.items(), key=itemgetter(<span class="hljs-number">1), reverse=<span class="hljs-literal">True)[:<span class="hljs-number">10]</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h3 id="item-1-7">查询效率和性能</h3>
<p>PASS</p>
<h3 id="item-1-8">高级查询</h3>
<p>有时候使用关键字参数返回的 <code>QuerySet</code> 不能完全满足你的查询需要。例如有时候你需要将约束条件进行<code>and</code>,<code>or</code> 的操作。你可以使用 MongoEngine 提供的 <code>Q</code> 类来实现,一个 <code>Q</code> 类代表了一个查询的一部分,里面的参数设置与你查询<code>document</code> 的时候相同。建立一个复杂查询的时候,你需要用 <code>&</code> 或 <code>|</code> 操作符将 <code>Q</code> 对象连结起来。例如:</p>
<pre class="python hljs"><code class="python"><span class="hljs-keyword">from mongoengine.queryset.visitor <span class="hljs-keyword">import Q
<span class="hljs-comment"># Get published posts
Post.objects(Q(published=<span class="hljs-literal">True) | Q(publish_date__lte=datetime.now()))
<span class="hljs-comment"># Get top posts
Post.objects((Q(featured=<span class="hljs-literal">True) & Q(hits__gte=<span class="hljs-number">1000)) | Q(hits__gte=<span class="hljs-number">5000))</span></span></span></span></span></span></span></span></code></pre>
<h3 id="item-1-9">Atomic updates(原子更新)</h3>
<p><code>MongoDB 文档</code> 可以通过<code>QuerySet</code> 上的 <code>update_one()</code>、<code>update()</code>、<code>modify()</code> 方法自动更新。下面几种操作符可以被用到这几种方法上:</p>
<ul>
<li>
<p><code>set</code> – 设置成一个指定的值</p>
</li>
<li>
<p><code>unset</code> – 删除一个指定的值</p>
</li>
<li>
<p><code>inc</code> – 将值加上一个给定的数</p>
</li>
<li>
<p><code>dec</code> – 将值减去一个给定的数</p>
</li>
<li>
<p><code>push</code> – 在 <code>list</code> 中添加一个值</p>
</li>
<li>
<p><code>push_all</code> – 在 <code>list</code> 中添加一个值</p>
</li>
<li>
<p><code>pop</code> – 移除<code>list</code> 的第一项或最后一项(根据 <code>pop__<field>=val </code>中<code>val</code> 的值决定删除第一项还是最后一项,一般情况下,<code>val</code> 为负则删除第一项,为正则删除最后一项,参见:mongodb $pop</p>
</li>
<li>
<p><code>pull</code> – 从 <code>list</code> 里面移除一个值</p>
</li>
<li>
<p><code>pull_all</code> – 从 <code>list</code> 里面移除个值</p>
</li>
<li>
<p><code>add_to_set</code> – 当要添加的值不在 <code>list</code> 中时,添加这个值</p>
</li>
</ul>
<p>原子更新的语法类似于查询语法,区别在于修饰操作符位于字段之前,而不是之后:</p>
<pre class="python hljs"><code class="python"><span class="hljs-meta">>>> post = BlogPost(title=<span class="hljs-string">'Test', page_views=<span class="hljs-number">0, tags=[<span class="hljs-string">'database'])
<span class="hljs-meta">>>> post.save()
<span class="hljs-meta">>>> BlogPost.objects(<span class="hljs-built_in">id=post.<span class="hljs-built_in">id).update_one(inc__page_views=<span class="hljs-number">1)
<span class="hljs-meta">>>> post.reload()<span class="hljs-comment"># the document has been changed, so we need to reload it
<span class="hljs-meta">>>> post.page_views
<span class="hljs-number">1
<span class="hljs-meta">>>> BlogPost.objects(<span class="hljs-built_in">id=post.<span class="hljs-built_in">id).update_one(set__title=<span class="hljs-string">'Example Post')
<span class="hljs-meta">>>> post.reload()
<span class="hljs-meta">>>> post.title
<span class="hljs-string">'Example Post'
<span class="hljs-meta">>>> BlogPost.objects(<span class="hljs-built_in">id=post.<span class="hljs-built_in">id).update_one(push__tags=<span class="hljs-string">'nosql')
<span class="hljs-meta">>>> post.reload()
<span class="hljs-meta">>>> post.tags
[<span class="hljs-string">'database', <span class="hljs-string">'nosql']</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>如果没有修饰操作符,则默认为<code>$set</code>:</p>
<pre class="python hljs"><code class="python">BlogPost.objects(<span class="hljs-built_in">id=post.<span class="hljs-built_in">id).update(title=<span class="hljs-string">'Example Post')
BlogPost.objects(<span class="hljs-built_in">id=post.<span class="hljs-built_in">id).update(set__title=<span class="hljs-string">'Example Post')</span></span></span></span></span></span></code></pre>
<h3 id="item-1-10">服务器端 JavaScript 执行</h3>
<p>可以写 Javascript函数,然后发送到服务器来执行。它返回结果是 Javascript 函数的返回值。这个功能是通过<code>QuerySet()</code>对象的<code>exec_js()</code> 方法实现。传递一个包含一个Javascript函数的字符串作为第一个参数。</p>
<p>其余位置的参数的名字字段将作为您的Javascript函数的参数传递过去。</p>
<p>在 JavaScript 函数范围中,一些变量可用:</p>
<ul>
<li>
<p><code>collection</code> – 对应使用的 <code>Document</code> 类的集合的名称</p>
</li>
<li>
<p><code>query</code> – 一个 <code>QuerySet</code> 对象</p>
</li>
<li>
<p><code>options</code> – 一个对象,它包含要传递给 <code>exec_js()</code> 函数的一些参数</p>
</li>
</ul>
<div class="widget-codetool"> </div>
<pre class="python hljs"><code class="python"><span class="hljs-function"><span class="hljs-keyword">def <span class="hljs-title">sum_field(<span class="hljs-params">document, field_name, include_negatives=<span class="hljs-literal">True):
code = <span class="hljs-string">"""
function(sumField) {
var total = 0.0;
db.find(query).forEach(function(doc) {
var val = doc;
if (val >= 0.0 || options.includeNegatives) {
total += val;
}
});
return total;
}
"""
options = {<span class="hljs-string">'includeNegatives': include_negatives}
<span class="hljs-keyword">return document.objects.exec_js(code, field_name, **options)</span></span></span></span></span></span></span></span></code></pre><br><br>
来源:https://www.cnblogs.com/hailin2018/p/15080288.html
頁:
[1]