解决runCommand只查询到101条数据

4 篇文章 0 订阅
3 篇文章 0 订阅

最近在开发中使用runCommand查询数据时,发现每次返回的数据量都是101条,而我需要查询的是全部的数据,带着问题,扒了一下runCommand数据查询操作的官方文档,得到了问题的答案。

准备运行环境

MongoClient

这里我是用的Mongo驱动是3.4.3版本的(目前项目中使用的是这个驱动);

ServerAddress serverAddress = new ServerAddress("127.0.0.1", 27017);
MongoClientOptions mongoClientOptions = MongoClientOptions.builder().build();
MongoCredential mongoCredential = MongoCredential.createCredential("username", "db", "password".toCharArray());

MongoClient mongoClient = new MongoClient(Collections.singletonList(serverAddress),
        Collections.singletonList(mongoCredential), mongoClientOptions);

代码中使用mongodb地址、库、用户名、密码等信息创建了MongoClient对象

初始化数据

MongoClient对象创建完成后,我们使用单元测试向MongoDB库的test集合中批量插入1024条数据

@Test
public void testInsert() {
    MongoDatabase database = mongoClient.getDatabase("db");

    BasicDBObject basicDBObject = new BasicDBObject();
    basicDBObject.put("insert", "test");

    BasicDBList dbList = new BasicDBList();
    for(int i = 1; i <= 1024; i ++) {
        BasicDBObject document = new BasicDBObject();
        document.put("_id", i);
        document.put("name", "name_" + i);
        dbList.add(document);
    }

    basicDBObject.put("documents", dbList);
    database.runCommand(basicDBObject);
}

问题复现

编写了一个与线上查询逻辑一致的单元测试用例进行问题复现

@Test
public void testFindWithQuestion() {
    MongoDatabase database = mongoClient.getDatabase("db");

    BasicDBObject basicDBObject = new BasicDBObject();
    basicDBObject.put("find", "test");
    BasicDBObject projection = new BasicDBObject();
    projection.put("_id", 1);
    basicDBObject.put("projection", projection);
    Document document = database.runCommand(basicDBObject);

    double ok = document.getDouble("ok");
    if(1 == ok) {
        Document cursor = document.get("cursor", Document.class);
        if(cursor.containsKey("firstBatch")) {
            List<Document> documentList = cursor.get("firstBatch", List.class);
            System.out.println(documentList);
            System.out.println(documentList.size());
        }
    }
}

单元测试执行完之后打印了一下数据条数,结果真的是101条,与线上问题一致:

解决问题

问题来源

带着问题去MongoDB官方文档中查询find操作命令相关的文档:

find — MongoDB Manual

在文档中查找到了batchSize参数,是这样描述的:

此参数用于指定firstBatch中返回的文档数量,默认是101;

看到这里终于明白为什么查询时返回的文档数量是101了,那么接下来的问题就是如何查询到全部的文档。

解决办法

带着问题,继续看文档,最终看到了Output返回参数的介绍。

返回的字段中包含了cursor字段,cursor字段中包含了游标编号和fisrtBatch文档;文档中强调了可以使用getMore命令查询剩余的文档。

getMore文档地址getMore — MongoDB Manual

getMore命令可以和返回游标的命令结合使用,可用于查询find命令或者aggregate命令的游标指向文档的后续批次文档。到这里基本上是找到了如何解决问题的答案。

代码实现

@Test
public void testFind() {
    MongoDatabase database = mongoClient.getDatabase("db");

    BasicDBObject basicDBObject = new BasicDBObject();
    basicDBObject.put("find", "test");
    BasicDBObject projection = new BasicDBObject();
    projection.put("_id", 1);
    basicDBObject.put("projection", projection);
    boolean hasNext = true;
    do {
        Document document = database.runCommand(basicDBObject);
        MongoDocumentList mongoDocumentList = resolveDocument(document);
        System.out.println(mongoDocumentList.documentList);
        System.out.println(mongoDocumentList.documentList.size());
        hasNext = mongoDocumentList.hasNext();
        if(hasNext) {
            basicDBObject = new BasicDBObject();
            basicDBObject.put("getMore", mongoDocumentList.cursor);
            basicDBObject.put("collection", "test");
        }
    } while (hasNext);
}

private MongoDocumentList resolveDocument(Document document) {
    double ok = document.getDouble("ok");
    if(1 == ok) {
        Document cursor = document.get("cursor", Document.class);
        List<Document> documentList = null;
        if(cursor.containsKey("firstBatch")) {
            documentList = cursor.get("firstBatch", List.class);
        } else if (cursor.containsKey("nextBatch")) {
            documentList = cursor.get("nextBatch", List.class);
        }

        return new MongoDocumentList(cursor.getLong("id"), documentList);
    }
    return null;
}

@AllArgsConstructor
private static class MongoDocumentList {
    private Long cursor;
    private List<Document> documentList;

    public boolean hasNext() {
        return 0 != cursor;
    }
}

代码中定义了MongoDocumentList实体,用于封装每次查询的结果,代码中判断查询是否结束是根据getMore命令返回的数据中的cursor编号是否为0进行判断的。

执行单元测试的结果如下:

查询的总数据量刚好是1024条

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的示例程序,用于解决你的专业问题: ```python import tkinter as tk import requests from bs4 import BeautifulSoup import sqlite3 import matplotlib.pyplot as plt class MyProgram: def __init__(self): self.window = tk.Tk() self.window.title("专业问题解决程序") # 创建界面元素 self.file_label = tk.Label(self.window, text="选择文件:") self.file_label.pack() self.file_button = tk.Button(self.window, text="选择", command=self.select_file) self.file_button.pack() self.plot_button = tk.Button(self.window, text="绘图", command=self.plot_data) self.plot_button.pack() self.website_label = tk.Label(self.window, text="输入网址:") self.website_label.pack() self.website_entry = tk.Entry(self.window) self.website_entry.pack() self.crawl_button = tk.Button(self.window, text="爬取数据", command=self.crawl_data) self.crawl_button.pack() # 创建数据库连接 self.conn = sqlite3.connect("data.db") self.cur = self.conn.cursor() self.cur.execute("CREATE TABLE IF NOT EXISTS data (title TEXT, link TEXT)") self.conn.commit() def select_file(self): # 在这里编写选择文件的逻辑 # 示例:选择文件并读入数据 self.filename = tk.filedialog.askopenfilename() with open(self.filename, "r") as f: self.data = [float(line.strip()) for line in f] def plot_data(self): # 在这里编写绘图的逻辑 # 示例:绘制折线图 plt.plot(self.data) plt.show() def crawl_data(self): # 在这里编写爬取数据的逻辑 # 示例:爬取百度搜索结果的标题和链接,并保存到数据库中 url = self.website_entry.get() response = requests.get(url) soup = BeautifulSoup(response.text, "html.parser") for result in soup.find_all("h3", class_="t"): title = result.get_text() link = result.find("a")["href"] self.cur.execute("INSERT INTO data (title, link) VALUES (?, ?)", (title, link)) self.conn.commit() def run(self): self.window.mainloop() if __name__ == "__main__": program = MyProgram() program.run() ``` 这个示例程序包含了三个功能: 1. 从文件中读入数据并绘制折线图(使用 matplotlib 库); 2. 爬取指定网站的数据(这里使用了百度搜索结果),并保存到数据库中(使用 sqlite3 库); 3. 利用 tkinter 设计程序界面。 注意,这个示例程序只是一个简单的框架,您需要根据自己的需求进行修改和扩展。例如,您可能需要对爬取到的数据进行进一步处理,或者需要设计更复杂的界面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值