【问题标题】:Nodejs & Mongo pagination random orderNodejs & Mongo 分页随机顺序
【发布时间】:2018-07-04 23:14:26
【问题描述】:

我正在运行一个 iOS 应用程序,其中显示了当前在线的用户列表。
我有一个 API 端点,我随机返回 10 个(或 N 个)用户,以便您可以继续滚动并始终看到新用户。因此,我想确保我不会返回之前已经返回的用户。
我不能使用光标或正常的分页,因为用户必须随机返回。

我尝试了 2 件事,但我确信有更好的方法:

  • 起初我所做的是在请求的参数中发送已经看到的用户 ID。 前任: 但是,如果用户继续滚动并浏览了 200 个配置文件,那么列表很长而且看起来不干净。
  • 然后,在数据库中,我尝试向每个用户“online_profiles_already_sent”添加一个字段,我将在其中存储已经发送给用户的 ID 数组(我使用的是 MongoDB)

我不知道如何以更好/更清洁的方式做到这一点

编辑: 我找到了一种使用 RAND(seed) 使用 MySQL 的方法 但我不知道是否有办法用 Mongo 做同样的事情
PHP MySQL pagination with random ordering


谢谢你:)

【问题讨论】:

  • 我认为最好将已请求的用户保留在应用程序内的本地数据中 - 如果您为每个客户端或会话/cookie 数据显示不同的用户。或者,如果您要为所有客户端返回一个列表 - 然后只需将列表保留在服务器部分 - 您可以创建临时文件或共享列表等。
  • 嗨@ByteMaster,感谢您的回答,但我的应用程序同时有数千名用户,我不认为将其保存在内存中是最好的。这就是我尝试将其保存在数据库中的原因,但我确信一定有更好的解决方案。我发现了一些使用带有 RAND 和种子的 MySQL 的东西,但我不知道如何使用 Mongo 来做这件事

标签: node.js mongodb random mongoose pagination


【解决方案1】:

我认为,您能够保证用户每次都能看到唯一用户的唯一方法是存储已经看过的用户列表。即使在您链接到的RAND 示例中,也有可能与以前的用户列表相交,因为RAND 不一定会排除以前返回的用户。


随机抽样

如果您确实想进行随机抽样,请考虑使用Random record from MongoDB,它建议使用Aggregation$sample 运算符。实现看起来像这样:

const {
    MongoClient
} = require("mongodb");

const
    DB_NAME = "weather",
    COLLECTION_NAME = "readings",
    MONGO_DOMAIN = "localhost",
    MONGO_PORT = "32768",
    MONGO_URL = `mongodb://${MONGO_DOMAIN}:${MONGO_PORT}`;

(async function () {
    const client = await MongoClient.connect(MONGO_URL),
        db = await client.db(DB_NAME),
        collection = await db.collection(COLLECTION_NAME);

    const randomDocs = await collection
        .aggregate([{
            $sample: {
                size: 5
            }
        }])
        .map(doc => {
            return {
                id: doc._id,
                temperature: doc.main.temp
            }
        });

    randomDocs.forEach(doc => console.log(`ID: ${doc.id} | Temperature: ${doc.temperature}`));
    client.close();
}());

以前用户的缓存

如果您要维护以前查看过的用户列表,您可以使用$nin 过滤器编写一个实现并存储以前查看过的用户的_id

这是一个使用天气数据库的示例,我一次返回 5 个条目,直到所有条目都被打印出来:

const {
    MongoClient
} = require("mongodb");

const
    DB_NAME = "weather",
    COLLECTION_NAME = "readings",
    MONGO_DOMAIN = "localhost",
    MONGO_PORT = "32768",
    MONGO_URL = `mongodb://${MONGO_DOMAIN}:${MONGO_PORT}`;

(async function () {
    const client = await MongoClient.connect(MONGO_URL),
        db = await client.db(DB_NAME),
        collection = await db.collection(COLLECTION_NAME);

    let previousEntries = [], // Track ids of things we have seen
        empty = false;

    while (!empty) {
        const findFilter = {};
        if (previousEntries.length) {
            findFilter._id = {
                $nin: previousEntries
            }
        }

        // Get items 5 at a time
        const docs = await collection
            .find(findFilter, {
                limit: 5,
                projection: {
                    main: 1
                }
            })
            .map(doc => {
                return {
                    id: doc._id,
                    temperature: doc.main.temp
                }
            })
            .toArray();

        // Keep track of already seen items
        previousEntries = previousEntries.concat(docs.map(doc => doc.id));

        // Are we still getting items?
        console.log(docs.length);
        empty = !docs.length;

        // Print out the docs
        docs.forEach(doc => console.log(`ID: ${doc.id} | Temperature: ${doc.temperature}`));
    }
    client.close();
}());

【讨论】:

  • 非常感谢您的回答。我想我会选择这样的东西。唯一的区别是我可能会将“previousEntries”存储在数据库中,在用户的模型上,所以我必须查询两次,因为我不想将它存储在内存中
【解决方案2】:

我也遇到过同样的问题,可以提出替代解决方案。

TL;DR:在第一次登陆时获取集合的所有对象 ID,使用 NodeJS 随机化并稍后使用。

  • 缺点:如果有数百万条记录,首次登陆速度较慢
  • 优势:后续执行可能比其他解决方案更快

让我们来详细解释一下:)

为了更好的解释,我将做以下假设

假设:

  1. 假设编程语言使用 NodeJS
    • 解决方案也适用于其他编程语言
  2. 假设您的收藏中总共有 4 个对象
  3. 假设 分页限制为 2

步骤:

第一次执行时:

  1. 获取所有对象 ID

注意:我确实考虑了性能,对于 10,000 个大小的集合,此执行需要几秒钟的时间。如果您要解决一百万条记录问题,那么可能首先使用某种形式的分区逻辑/使用列出的其他解决方案

db.getCollection('my_collection').find({}, {_id:1}).map(function(item){ return item._id; });

db.getCollection('my_collection').find({}, {_id:1}).map(function(item){ return item._id.valueOf(); });

结果:

ObjectId("FirstObjectID"),
ObjectId("SecondObjectID"),
ObjectId("ThirdObjectID"),
ObjectId("ForthObjectID"),
  1. 使用 NodeJS 随机化数组检索

结果:

ObjectId("ThirdObjectID"),
ObjectId("SecondObjectID"),
ObjectId("ForthObjectID"),
ObjectId("FirstObjectID"),
  1. 存储了这个随机数组:
  • 如果这是为每个用户随机分页的服务器端脚本,请考虑存储在 Cookie / Session
    • 建议使用 Cookie(超时已与浏览器关闭相关联)用于扩展目的

每次检索时:

  1. 检索存储的数组

  2. 抓取分页项,(例如前 2 项)

  3. 使用 find $in 查找这些项目的对象

.

db.getCollection('my_collection')
    .find({"_id" : {"$in" : [ObjectId("ThirdObjectID"), ObjectId("SecondObjectID")]}});
  1. 使用NodeJS,根据检索到的分页项对检索到的对象进行排序

给你!用于分页的随机 MongoDB 查询:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-15
    • 2013-10-04
    • 1970-01-01
    • 1970-01-01
    • 2018-06-01
    • 2020-06-28
    • 2012-11-19
    相关资源
    最近更新 更多