Mongo之pymongo 源码分析 find sort aggregate_hpulfc的博客-程序员秘密_pymongo 源码

技术标签: mongo  分析  pymongo  find  sort  数据库  

Mongo之pymongo 源码分析 find  sort  aggregate

一次查询的过程

    简单来说,pymongo就是python关于Mongo db的一个包,主要是通过对一些命令的包装,通过socket发送给mongo服务,获取到一些结果,对结果进行包装,然后以包装为游标对象返回给使用者,其中游标对象实现了 next方法,同时对__next__进行更新,是的用户可以通过python迭代器进行迭代,逐条返回查询结果

来看具体分析:》》》

首先,通常情况下,我们这样使用:

mc = pymongo.MongoClient("remote_uri")
这是先生成了一个mongo客户端,返回的是一个MongoClien对象:
class MongoClient(common.BaseObject):
    HOST = "localhost"
    PORT = 27017

    def __init__(
            self,
            host=None,
            port=None,
            document_class=dict,
            tz_aware=False,
            connect=True,
            **kwargs):
......
......

然后会使用 db=mc["database_name"] 获取相应的数据库对象, 返回的是DataBase的实例:

-- mongo_client.py
    def __getattr__(self, name):
        """Get a database by name.

        Raises :class:`~pymongo.errors.InvalidName` if an invalid
        database name is used.

        :Parameters:
          - `name`: the name of the database to get
        """
        if name.startswith('_'):
            raise AttributeError(
                "MongoClient has no attribute %r. To access the %s"
                " database, use client[%r]." % (name, name, name))
        return self.__getitem__(name)

    def __getitem__(self, name):
        """Get a database by name.

        Raises :class:`~pymongo.errors.InvalidName` if an invalid
        database name is used.

        :Parameters:
          - `name`: the name of the database to get
        """
        return database.Database(self, name) ##################################主要看这里######################################### 

同理,获取对应的集合的时候也是通过db["collection_name"]得到,返回的对象是Collection的实例:这个操作和上个类似,可以试着去找一找

得到collection的实例之后就有我们很常见的一些方法了,如下:

def find(self, *args, **kwargs):
    return Cursoe(self, *args, **kwargs)
def find_one(self, filter=None, *args, **kwargs):
def insert_one(self, document):
def save(self, to_save, manipulate=True, check_keys=True, **kwargs):
还有很多,就不粘贴了。。
经常使用的 find 方法的返回结果是一个Cursor对象, 上面的代码可以看出。

接着重点来了:
很多的常见的函数,如sort, count,distinct,explain,min,max...都是Cursor中的方法,具体的实现原理可以自己去看看,这里只是对sort函数和怎么通过这个游标取数据进行分析。

首先是看一下sort函数都做了哪些事情:

    def sort(self, key_or_list, direction=None):
        """Sorts this cursor's results.
        """
        self.__check_okay_to_chain()
        keys = helpers._index_list(key_or_list, direction)
        self.__ordering = helpers._index_document(keys)
        return self

由代码不难看出,sort函数在经过一系列操作之后,仍然是返回自身,也就是返回的韩式Cursor实例,只不过前面进行了三步操作。

  1. 是检查是否能够在这个游标之后进行其他更多的操作,这里是有可能抛出InvalidOperation异常。
  2. 获取排序的键和对应的方向
  3. 对文档以当前排序的键 建立相应的索引。

三步执行完之后,并没有获取到真正的结果,文档的真实的获取结果是下面这个方法:

    def next(self):
        """Advance the cursor."""
        if self.__empty:
            raise StopIteration
        _db = self.__collection.database
        if len(self.__data) or self._refresh():
            if self.__manipulate:
                return _db._fix_outgoing(self.__data.popleft(),
                                         self.__collection)
            else:
                return self.__data.popleft()
        else:
            raise StopIteration

    __next__ = next
这个函数结尾对__next__进行了重新赋值,使得可以通过python的迭代器进行迭代获取值。
纵观这个函数,发现获取的数据容器是 self.__data。在当前代码文件中搜索发现,他是一个deque对象,(插一句,双端队列支持从队列两端进行数据操作,插入/移除.),再次搜索发现,对这个变量进行赋值的方法还有下面这个:
    def __send_message(self, operation):
        """Send a query or getmore operation and handles the response.
        """
        client = self.__collection.database.client

        if operation:
            kwargs = {
                "read_preference": self.__read_preference,
                "exhaust": self.__exhaust,
            }
            if self.__address is not None:
                kwargs["address"] = self.__address

            try:
                response = client._send_message_with_response(operation,
                                                              **kwargs)
                self.__address = response.address
                if self.__exhaust:
                    # 'response' is an ExhaustResponse.
                    self.__exhaust_mgr = _SocketManager(response.socket_info,
                                                        response.pool)

                data = response.data
            except AutoReconnect:
               
                self.__killed = True
                raise
        else:
            # Exhaust cursor - no getMore message.
            try:
                data = self.__exhaust_mgr.sock.receive_message(1, None)
            except ConnectionFailure:
                self.__die()
                raise

        try:
            doc = helpers._unpack_response(response=data,
                                           cursor_id=self.__id,
                                           codec_options=self.__codec_options)
        except OperationFailure:
            .......
        except NotMasterError:
            .......
        self.__id = doc["cursor_id"]
        if self.__id == 0:
            self.__killed = True

        self.__retrieved += doc["number_returned"]
        self.__data = deque(doc["data"]) ##########################################这里######################################

        ......

搜索这个__send_messae发现__refresh使用了这个方法,同时在next方法中发现了__refresh的踪迹,可见获取数据主要是通过不同的渠道进行__send_message获取得到的。

查看__send_message方法发现,是通过MongClient的实例调用_send_message_with_response方法获取,这个客户端又是通过回调的方式,调用了Server实例的send_message_with_response方法,返回的是Response对象(就是通过读取套接字中的内容,然后进行封装的),其代码如下:

class Response(object):
    __slots__ = ('_data', '_address')

    def __init__(self, data, address):
        """Represent a response from the server.

        :Parameters:
          - `data`: Raw BSON bytes.
          - `address`: (host, port) of the source server.
        """
        self._data = data
        self._address = address

    @property
    def data(self):
        """Server response's raw BSON bytes."""
        return self._data

    @property
    def address(self):
        """(host, port) of the source server."""
        return self._address

主要是data属性,文档数据就存放在这个地方,到这,真正的数据也就找到了!

    总结来说, 就是通过实例化相应的类,然后主要是通过Colllection对象的方法获取Cursor对象,对文档进行多种操作。本文主要是对find方法获取数据的过程进行分析,然后,简单说了一下常用的sort方法包含哪些过程,以加深对pymongo的理解。其中涉及到的类主要有:MongoClient, Collection, Cursor, Deque, Server, SocketInfo(获取信息), Response 。最后,有兴趣的可以看一下Collection下的aggregate方法,与find有一些不同,但大体实现原理类似!嗯,没了~~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hpulfc/article/details/79611326

智能推荐

bzoj 做起走 -- bzoj 1009 GT 考试_weixin_30548917的博客-程序员秘密

  现在每次做一道bzoj上的题,整个人都感觉升华了。。。  先是在网上各种搜题解。要么只有代码,要么有点讲解看不懂,对于从来没有耐心看完别人代码的我,只能一篇一篇的翻。。然后终于在某2011级同学的某段话中找到了灵感,把它给A了。  我还是好好记录一下这道题的做题过程,不要又被其他人喷“只有做过的人才看得懂了!”  首先说说这道题的思路吧:dp+矩阵优化。dp虽然不那么明显...

C# Task.Run 和 Task.Factory.StartNew 区别_lindexi_gd的博客-程序员秘密

有小伙伴问我,为什么不推荐他使用 Task.Factory.StartNew ,因为 Task.Run 是比较新的方法。本文告诉大家 Task.Run 和 Task.Factory.StartNew 区别

移植uboot2010.06到TQ2440开发板详解之一_在路上028的博客-程序员秘密

最近在学习uboot的移植,也看过很多文章,但多多少少都有些不尽人意的地方,自己手动移植也出现过种种问题。尤其是不能对移植的过程有很好的理解,基于这个原因,我打算深入学习一下uboot的移植过程,并对移植过程详细记录,方便那些想学习uboot移植确找不到门路的新手。        鉴于本人也是新手,文章难免有疏漏之处,也有自己不是很明白的地方,已经标记出来,还望各位多多指教。如果觉得好想转载,

下载 axios.js 文件到本地_于小猿的博客-程序员秘密

1、百度搜索 GitHub 官网:https://github.com/2、搜索 axios3、点击 axios/axios4、下载到本地5、解压,进入到 dist 文件夹自己下载,不求人。

xamarin.android蓝牙,c# - Xamarin forms and Android Bluetooth - Stack Overflow_温绚的博客-程序员秘密

I am developing a cross platform app using Xamarin forms.I have an ObserveableCollection and I want to populate it with bluetooth devices that have been found during a search.The search is/has to be p...

随便推点

matlab 线性方程组 限定解的个数,[转载]Matlab解线性方程组笔记_隅子酱的博客-程序员秘密

1. 范数分析 (norm, normest) 常用于误差估计对于线性空间中某个向量 x = { x1, x2, ..., xn}, 其对应的p级范数定义为 abs(x)_p =(sum(abs(xi)^p),i=1,n)^(1/p), 而 +Inf 范数值 abs(x)_+Inf =max(abs(xi)), -Inf 为 abs(x)_-Inf = min(abs(xi));对于矩阵abs(A...

SpringCloud——Zuul 文件上传_zuul 支持zip_winner_corl的博客-程序员秘密

目录1、上传文件微服务2、zuul微服务(最简单的配置)3、利用zuul上传文件4、如果上传过大文件出现问题如下——超时1官方文档解决办法——可以传比较大的zip压缩图片啥的。。。。5、如果上传过大文件出现问题如下——the request was rejected because its size (26246240) exceeds the configured...

Django Rest framework 分页的使用_as_view()无法分页_过往如烟。的博客-程序员秘密

第一种分页 PageNumberPagination基本使用(1)urls.pyurlpatterns = [ re_path('(?P<version>[v1|v2]+)/page1/', Pager1View.as_view(),) #分页1](2)api/utils/serializers/pager.py# api/utils/serializse...

openwrt 框架分析_openwrt架构_潘多拉的面的博客-程序员秘密

这次讲讲openwrt的结构.1. 代码上来看有几个重要目录package, target, build_root, bin, dl....---build_dir/host目录是建立工具链时的临时目录---build_dir/toolchain-<arch>*是对应硬件的工具链的目录---staging_dir/toolchain-<arch>* 则是工具链的安装位置---target/linux/<platform>目录里面是各个平台(arc.

win10:JDK14.0.2环境变量配置_Mr.tortoise的博客-程序员秘密

下载并安装(下载地址:https://www.oracle.com/java/technologies/javase-jdk14-downloads.html)配置环境变量(管理员/系统)总之,方式都是一样的。我安装的路径是:D:\Program Files (x86)\Java\jdk-14.0.2配置系统变量,则在这部分配置JAVA_HOME和Path在用户变量中新建JAVA_HOME :D:\Program Files (x86)\Java\jdk-14.0.2CLASSPATH :.;%

微信小程序 人脸识别功能 代码 wx.faceDetect_飞奔的喀纳斯蜗牛的博客-程序员秘密

刚好遇到要写微信小程序人脸识别的功能,就翻了一下微信小程序文档人脸识别跳转文档缺点:照片也可以成功。我试了一下眨眼,但是好像不行,闭眼都能识别到眼睛。然后没有demo,就自己研究了,记录一下。用uniapp写的,所以需要条件编译。HTML(需要用到相机组件)<view class=""> <camera v-if="showcamera" device-position="front" resolution="high" style="width: 100%; .

推荐文章

热门文章

相关标签