野生橘子 發表於 2021-7-30 16:28:00

MongoDB_5:MongoEngine 查询

<h3>转自:MongoEngine 查询(翻译)</h3>
<p>&nbsp;</p>
<h2 id="item-1">数据库查询</h2>
<p><code>Document</code>&nbsp;对象有一个&nbsp;<code>objects</code>&nbsp;属性,用来访问在数据库中跟这个类有关的对象。这个&nbsp;<code>objects</code>&nbsp;属性其实是一个<code>QuerySetManager</code>&nbsp;,它会创建和返回一个新的&nbsp;<code>QuerySet</code>&nbsp;对象的访问。这个&nbsp;<code>QuerySet</code>&nbsp;对象可以从数据库中遍历获取的文档:</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>可以通过调用&nbsp;<code>QuerySet</code>&nbsp;对象的关键字参数来对数据查询进行过滤,关键字查询中的键和你想要查询的<code>Document</code>&nbsp;中的字段一致:</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的字段可以使用&nbsp;<code>__</code>&nbsp;来代替对象属性访问语法中的&nbsp;<code>.</code>&nbsp;进行访问:</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>&nbsp;– 不等于<code>≠</code></p>
</li>
<li>
<p><code>lt</code>&nbsp;– 小于<code>&lt;</code></p>
</li>
<li>
<p><code>lte</code>&nbsp;– 小于等于<code>≤</code></p>
</li>
<li>
<p><code>gt</code>&nbsp;– 大于<code>&gt;</code></p>
</li>
<li>
<p><code>gte</code>&nbsp;– 大于等于&nbsp;<code>≥</code></p>
</li>
<li>
<p><code>not</code>&nbsp;– 否定一个标准的检查,需要用在其他操作符之前(e.g.&nbsp;<code>Q(age__not__mod=5)</code>)</p>
</li>
<li>
<p><code>in</code>&nbsp;– 值在&nbsp;<code>list</code>&nbsp;中</p>
</li>
<li>
<p><code>nin</code>&nbsp;– 值不在&nbsp;<code>list</code>&nbsp;中</p>
</li>
<li>
<p><code>mod</code>&nbsp;–&nbsp;<code>value&nbsp;%&nbsp;x&nbsp;==&nbsp;y</code>, 其中&nbsp;<code>x</code>&nbsp;和&nbsp;<code>y</code>&nbsp;为给定的值</p>
</li>
<li>
<p><code>all</code>&nbsp;–&nbsp;<code>list</code>&nbsp;里面所有的值</p>
</li>
<li>
<p><code>size</code>&nbsp;– 数组的大小</p>
</li>
<li>
<p><code>exists</code>&nbsp;– 存在这个值</p>
</li>
</ul>
<h4>字符串查询</h4>
<p>以下操作符可以快捷的进行正则查询:</p>
<ul>
<li>
<p><code>exact</code>&nbsp;– 字符串型字段完全匹配这个值</p>
</li>
<li>
<p><code>iexact</code>&nbsp;– 字符串型字段完全匹配这个值(大小写敏感)</p>
</li>
<li>
<p><code>contains</code>&nbsp;– 字符串字段包含这个值</p>
</li>
<li>
<p><code>icontains</code>&nbsp;– 字符串字段包含这个值(大小写敏感)</p>
</li>
<li>
<p><code>startswith</code>&nbsp;– 字符串字段由这个值开头</p>
</li>
<li>
<p><code>istartswith</code>&nbsp;– 字符串字段由这个值开头(大小写敏感)</p>
</li>
<li>
<p><code>endswith</code>&nbsp;– 字符串字段由这个值结尾</p>
</li>
<li>
<p><code>iendswith</code>&nbsp;– 字符串字段由这个值结尾(大小写敏感)</p>
</li>
<li>
<p><code>match</code>&nbsp;– 执行&nbsp;<code>$elemMatch</code>&nbsp;操作,所以你可以使用一个数组中的 document 实例</p>
</li>
</ul>
<h4>地理查询</h4>
<p>PASS</p>
<h4>列表查询</h4>
<p>对于大多数字段,这种语法会查询出那些字段与给出的值相匹配的document,但是当一个字段引用&nbsp;<code>ListField</code>&nbsp;的时候,而只会提供一条数据,那么包含这条数据的就会被匹配上:</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>你可以通过&nbsp;<code>__raw__</code>&nbsp;参数来使用一个原始的&nbsp;<code>PyMongo</code>&nbsp;语句来进行查询,这样可以进行原始的完整查询:</p>
<pre class="python hljs"><code class="python">Page.objects(__raw__={<span class="hljs-string">'tags':&nbsp;<span class="hljs-string">'coding'})</span></span></code></pre>
<h3 id="item-1-3">限制和跳过结果</h3>
<p>就像传统的ORM一样,你有时候需要限制返回的结果的数量,或者需要跳过一定数量的结果。<code>QuerySet</code>&nbsp;里面可以使用&nbsp;<code>limit()</code>&nbsp;和&nbsp;<code>skip()</code>&nbsp;这两个方法来实现,但是更推荐使用数组切割的语法:</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>你可以指定让查询返回一个结果。如果这个条在数据库中不存在,那么会引发&nbsp;<code>IndexError</code>&nbsp;错误 。使用&nbsp;<code>first()</code>&nbsp;方法在数据不存在的时候会返回&nbsp;<code>None</code>:</p>
<pre class="python hljs"><code class="python"><span class="hljs-meta">&gt;&gt;&gt; <span class="hljs-comment"># Make sure there are no users
<span class="hljs-meta">&gt;&gt;&gt; User.drop_collection()
<span class="hljs-meta">&gt;&gt;&gt; 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">&gt;&gt;&gt; User.objects.first() == <span class="hljs-literal">None
<span class="hljs-literal">True
<span class="hljs-meta">&gt;&gt;&gt; User(name=<span class="hljs-string">'Test User').save()
<span class="hljs-meta">&gt;&gt;&gt; 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>&nbsp;查询</h3>
<p>默认情况下,<code>Document</code>的<code>objects</code>&nbsp;属性返回一个一个&nbsp;<code>QuerySet</code>&nbsp;对象,它并没有进行任何筛选和过滤,它返回的是所有的数据对象。这一点可以通过给一个&nbsp;<code>document</code>&nbsp;定义一个方法来修改 一个<code>queryset</code>&nbsp;。这个方法需要两参数<code>__doc_cls</code>&nbsp;和&nbsp;<code>queryset</code>&nbsp;。第一个参数是定义这个方法的&nbsp;<code>Document</code>&nbsp;类名(从这个意义上来说,这个方法像是一个&nbsp;<code>classmethod()</code>&nbsp;而不是一般的方法),第二个参数是初始化的&nbsp;<code>queryset</code>。这个方法需要使用&nbsp;<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>你不用调用&nbsp;<code>objects</code>&nbsp;方法,你可以自定义更多的管理方法,例如:</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>当你想自己定义一些方法来过滤&nbsp;<code>document</code>&nbsp;的时候,继承&nbsp;<code>QuerySet</code>&nbsp;类对你来说就是个好的方法。为了在&nbsp;<code>document</code>&nbsp;里面使用一个自定义的&nbsp;<code>QuerySet</code>&nbsp;类,你可以在&nbsp;<code>document</code>&nbsp;里的&nbsp;<code>meta</code>&nbsp;字典里设置&nbsp;<code>queryset_class</code>&nbsp;的值来实现它。</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>就像限制和跳过结果一样,&nbsp;<code>QuerySet</code>&nbsp;对象提供了用来计数的方法 -&nbsp;<code>count()</code>,不过还有一个更&nbsp;<code>Pythonic</code>&nbsp;的方法来实现:</p>
<pre class="python hljs"><code class="python">num_users =&nbsp;<span class="hljs-built_in">len(User.objects)</span></code></pre>
<h4>更多功能</h4>
<p>当你想为&nbsp;<code>document</code>&nbsp;的特定的字段的数量计数的时候,可以使用&nbsp;<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>当你想求某个字段的平均值的时候,可以使用&nbsp;<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 提供了一个方法来获取一个在集合里&nbsp;<code>item</code>&nbsp;的频率 -&nbsp;<code>item_frequencies()</code>。下面一个例子可以生成&nbsp;<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>有时候使用关键字参数返回的&nbsp;<code>QuerySet</code>&nbsp;不能完全满足你的查询需要。例如有时候你需要将约束条件进行<code>and</code>,<code>or</code>&nbsp;的操作。你可以使用 MongoEngine 提供的&nbsp;<code>Q</code>&nbsp;类来实现,一个&nbsp;<code>Q</code>&nbsp;类代表了一个查询的一部分,里面的参数设置与你查询<code>document</code>&nbsp;的时候相同。建立一个复杂查询的时候,你需要用&nbsp;<code>&amp;</code>&nbsp;或&nbsp;<code>|</code>&nbsp;操作符将&nbsp;<code>Q</code>&nbsp;对象连结起来。例如:</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) &amp; 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>&nbsp;可以通过<code>QuerySet</code>&nbsp;上的&nbsp;<code>update_one()</code>、<code>update()</code>、<code>modify()</code>&nbsp;方法自动更新。下面几种操作符可以被用到这几种方法上:</p>
<ul>
<li>
<p><code>set</code>&nbsp;– 设置成一个指定的值</p>
</li>
<li>
<p><code>unset</code>&nbsp;– 删除一个指定的值</p>
</li>
<li>
<p><code>inc</code>&nbsp;– 将值加上一个给定的数</p>
</li>
<li>
<p><code>dec</code>&nbsp;– 将值减去一个给定的数</p>
</li>
<li>
<p><code>push</code>&nbsp;– 在&nbsp;<code>list</code>&nbsp;中添加一个值</p>
</li>
<li>
<p><code>push_all</code>&nbsp;– 在&nbsp;<code>list</code>&nbsp;中添加一个值</p>
</li>
<li>
<p><code>pop</code>&nbsp;– 移除<code>list</code>&nbsp;的第一项或最后一项(根据&nbsp;<code>pop__&lt;field&gt;=val&nbsp;</code>中<code>val</code>&nbsp;的值决定删除第一项还是最后一项,一般情况下,<code>val</code>&nbsp;为负则删除第一项,为正则删除最后一项,参见:mongodb $pop</p>
</li>
<li>
<p><code>pull</code>&nbsp;– 从&nbsp;<code>list</code>&nbsp;里面移除一个值</p>
</li>
<li>
<p><code>pull_all</code>&nbsp;– 从&nbsp;<code>list</code>&nbsp;里面移除个值</p>
</li>
<li>
<p><code>add_to_set</code>&nbsp;– 当要添加的值不在&nbsp;<code>list</code>&nbsp;中时,添加这个值</p>
</li>
</ul>
<p>原子更新的语法类似于查询语法,区别在于修饰操作符位于字段之前,而不是之后:</p>
<pre class="python hljs"><code class="python"><span class="hljs-meta">&gt;&gt;&gt; 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">&gt;&gt;&gt; post.save()
<span class="hljs-meta">&gt;&gt;&gt; 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">&gt;&gt;&gt; post.reload()<span class="hljs-comment"># the document has been changed, so we need to reload it
<span class="hljs-meta">&gt;&gt;&gt; post.page_views
<span class="hljs-number">1
<span class="hljs-meta">&gt;&gt;&gt; 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">&gt;&gt;&gt; post.reload()
<span class="hljs-meta">&gt;&gt;&gt; post.title
<span class="hljs-string">'Example Post'
<span class="hljs-meta">&gt;&gt;&gt; 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">&gt;&gt;&gt; post.reload()
<span class="hljs-meta">&gt;&gt;&gt; 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>&nbsp;方法实现。传递一个包含一个Javascript函数的字符串作为第一个参数。</p>
<p>其余位置的参数的名字字段将作为您的Javascript函数的参数传递过去。</p>
<p>在 JavaScript 函数范围中,一些变量可用:</p>
<ul>
<li>
<p><code>collection</code>&nbsp;– 对应使用的&nbsp;<code>Document</code>&nbsp;类的集合的名称</p>
</li>
<li>
<p><code>query</code>&nbsp;– 一个&nbsp;<code>QuerySet</code>&nbsp;对象</p>
</li>
<li>
<p><code>options</code>&nbsp;– 一个对象,它包含要传递给&nbsp;<code>exec_js()</code>&nbsp;函数的一些参数</p>
</li>
</ul>
<div class="widget-codetool">&nbsp;</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 &gt;= 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]
查看完整版本: MongoDB_5:MongoEngine 查询