【问题标题】:[Node.js, MongoDB/Mongoose]: Generating a unique list of tags using indexOf is very slow[Node.js, MongoDB/Mongoose]:使用 indexOf 生成唯一的标签列表非常慢
【发布时间】:2021-05-18 22:57:41
【问题描述】:

我有两个模型,一个视频模型和一个全局统计模型。视频模型存储标签的字符串数组。全局统计模型存储了一个包含tagcounttagCountSchema 数组。

我正在编写一个函数,该函数使用视频文档中的数据删除和重建全局统计文档。这包括在全局统计文档中重建唯一标签列表及其计数。

const videoSchema = new mongoose.Schema({
    tags: [{ type: String }],
});

const tagCountSchema = new mongoose.Schema({
    tag: { type: String, required: true },
    count: { type: Number, default: 1 },
}, { _id: false });

const statisticSchema = new mongoose.Schema({
    is: { type: String, default: 'global' },
    tags: [tagCountSchema],
});

const Statistic = mongoose.model('Statistic', statisticSchema );
const Video = mongoose.model('Video', videoSchema );
// Rebuild the statistics document
let statistics = await Statistic.findOne({ is: 'global' });
let videos = await Video.find({});

let map = statistics.tags.map(e => e.tag);

for (let video of videos) {    
    for (let tag of video.tags) {
        const index = map.indexOf(tag);
        if (index === -1) {
            statistics.tags.push({ tag: tag, count: 1 });
            map.push(tag);
        } else {
            statistics.tags[index].count++;
        }
    }
}

await statistics.save();

但是,在上面的函数中使用indexOf() 使得重建统计数据需要非常 很长时间。由于视频有很多唯一标签,全局统计文档中的唯一标签数组变得非常长,而且由于每个视频的每个标签都需要调用indexOf(),因此该函数需要很长时间才能完成。

我测试了这个函数的一个版本,它将标签作为对象存储在数据库中,并使用Object.keys 来更新统计文档中的标签。这速度快了一个数量级,但我已经意识到,如果将标签名称直接作为对象存储在数据库中会导致问题,如果标签名称非法用作数据库键。

从技术上讲,我也可以对标签对象进行字符串化以存储它,但这对于如何在我的代码的其他地方使用此函数并不传统。随着该函数循环播放视频,它还更新了其他文档(例如上传器)的类似统计信息,为了简单起见,我在代码中省略了这些统计信息。这意味着它需要对每个视频的对象进行字符串化和去字符串化。

我可以怎样提高这个功能的速度?

【问题讨论】:

  • 你能展示一下map 之后的样子吗:let map = statistics.tags.map(e => e.name);
  • @codemonkey 抱歉,name 应该是 tag,更新了问题。 map 是从tagCountSchema 数组构建的标签名称数组,用于获取标签的索引。
  • 我们所说的数组有多大?里面有多少元素?我要求探索在该循环之前对其进行预处理以使其查找速度更快的可能性。
  • 唯一标签的数量目前接近 11,000 个,但它有增长的潜力。

标签: node.js arrays mongodb mongoose


【解决方案1】:

也许你的方法不太好。 如果您在注册视频时更新统计信息会更简单。

这样可以避免构建索引问题。或者您可以使用队列来更新您的数据。

我会这样做

【讨论】:

  • 这就是我的代码在 99% 的情况下已经工作的方式。我在这里做一些不同的事情并重建统计数据的原因是这个函数是我的脚本的更新实用程序的一部分,其中需要重建文档以匹配新格式、添加新统计数据等。这个函数只运行一次(应用更新)并且再也不会,但是,这是有问题的,因为在当前状态下,它可能需要几个小时才能完成。
【解决方案2】:

我在我的工作中遇到了类似的问题,我不得不有点创意来避免使用indexOf(在我的情况下为find)功能,因为正如您所知,这是一项非常昂贵的操作.

另一方面,您可能知道,查找对象的键几乎是即时的。所以我会像这样重写构建统计文档的代码:

const map = {};
statistics.tags.map((e, i) => {
    map[e.tag] = i
});

for (let video of videos) {
    for (let tag of video.tags) {
        if (tag in map) {
            statistics.tags[map[tag]].count++;
        } else {
            statistics.tags.push({ tag, count: 1 });
            map[tag] = Object.keys(map).length;
        }
    }
}

这将显着加快您的嵌套循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 2014-11-04
    • 1970-01-01
    相关资源
    最近更新 更多