Mongo之pymongo 源码分析 find sort aggregate

4 篇文章 0 订阅
1 篇文章 0 订阅

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有一些不同,但大体实现原理类似!嗯,没了~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pymongo的find()方法用于在MongoDB中查询数据。通过使用find()方法,可以返回一个迭代器,然后可以使用for循环逐条输出查询结果。例如,可以使用以下代码查询数据并输出结果: ```python import pymongo client = pymongo.MongoClient(host='localhost', port=27017) db = client.test collection = db.students result = collection.find() for r in result: print(r) ``` 这段代码会查询名为"students"的集合中的所有数据,并逐条输出结果。输出结果中包含每条数据的字段和对应的值。 #### 引用[.reference_title] - *1* [Python-操作-MongoDB-数据库](https://blog.csdn.net/qq_44830823/article/details/108070489)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Python3中使用PyMongo的方法详解](https://blog.csdn.net/weixin_33749131/article/details/91466442)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [mongo使用find()查询结果为游标(cursor)类型,遍历查找后合并数据](https://blog.csdn.net/aaahuahua/article/details/122608796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值