Nodejs& Mongo分页随机顺序

时间:2018-01-25 16:14:40

标签: node.js mongodb random mongoose pagination

我正在运行iOS应用,其中显示当前在线的用户列表 我有一个API端点,我随机返回10(或N)个用户,这样你就可以继续滚动并始终看到新用户。因此,我想确保我不会返回之前已经返回的用户 我无法使用游标或普通分页,因为用户必须随机返回。

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

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

我无法弄清楚如何以更好/更清洁的方式做到这一点

编辑: 我找到了一种方法来使用MySQL,使用RAND(种子) 但我无法弄清楚是否有办法与Mongo做同样的事情 PHP MySQL pagination with random ordering


谢谢:))

2 个答案:

答案 0 :(得分: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();
}());

答案 1 :(得分:0)

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

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 查询 :)