MongoDB按非索引字段排序的最佳实践

时间:2019-06-04 02:10:29

标签: node.js mongodb sorting meteor

我有一个允许用户使用自己的自定义数据的应用程序,所以我不知道数据是什么。但是,我确实希望允许他们对数据进行排序。 这可能是大量数据,mongodb最终给了我内存错误(限制为32MB)

解决此问题的最佳方法是什么?如何允许用户按未知字段对大量数据进行排序?

3 个答案:

答案 0 :(得分:1)

MongoDB允许您以可以在模式中存储对象和对象关系的方式设计模式,因此您可以允许用户存储任何类型的信息。正如@kevinadi所说,最大限制为32MB。就排序而言,它可以在您的服务器端完成。

这是我在MongoDB和Mongoose ORM中存储对象时尝试的示例

var mongoose = require("mongoose");
var userSchema = new mongoose.Schema({
  email: {
    type: String,
    unique: true,
    required: true,
    lowercase: true,
    trim: true,
    match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, "Please fill a valid email address"]
  },
  custInfo:{
  type:Object,
  required: true
  }
  isConfirmed: {
    type: Boolean,
    required: true,
    default: false
  },
  confirmedOn:{
    type: Date,
    required: true,
    default: Date.now()
  }
});

module.exports = mongoose.model("user",userSchema);

答案 1 :(得分:1)

由于您已经标记了这个问题,因为我假设流星,所以您具有默认的流星环境,可以在其中使用客户端轻量级Mongo collections

这使您有机会发布(发布)/返回(方法)几乎未排序的数据,并让客户处理此任务。

这样想:只有100个客户要求发布有关每个排序操作更新的发布(因为订阅参数发生了变化,因此发布也发生了变化)。

这已经导致您的服务器消耗大量RAM,以使观察者(OPLOG等)运行100个出版物,每个出版物都查询大量文档。

可能的解决方案如下所述。请记住,它们不限于任何前端,而只是一个概念上的描述。根据您的前端环境,您将必须包括反应性等。

选项A-发布未排序的内容,让客户端进行排序

服务器

Meteor.publish('hugeData', function () {
  return MyCollection.find({ ...})
})

客户端

const handle = Meteor.subscribe('hugeData')
if (handle.ready()) {
  const sortedData = MyCollection.find({ ... }, {sort: { someField: -1 } })
}

这里是一个很大的优点,如果使用cursor.observeChanges,则可以通知客户完整性状态。

请注意,如果您想向后扫描(返回最新版本的文档),可以使用hint option on find

Meteor.publish('hugeData', function () {
  return MyCollection.find({ ...}, { hint: { $natural : -1 })
})

{ sort: { fieldName: -1} }相比,它的性能更高。

选项B-返回方法未排序的内容,让客户端进行排序

现在,解决方案A可能仍然存在问题,因为如果有很多订阅者,它仍然要消耗大量RAM。一种替代方法(尤其是在实时数据更改不太重要的情况下)是使用“流星方法”:

服务器

Meteor.method('hugeData', function () {
  return MyCollection.find({ ...}).fetch()
})

请注意,这需要fetch个文档,否则将引发unhandledPromiseRejection

客户端

这需要在客户端上使用LocalCollection,即与服务器端集合不同步,否则您将在文档同步方面遇到问题:

const HugeData = new LocalCollection(null) // note the null as collection name!

const insertUpdate = document => {
  if (LocalCollection.findOne(document._id)) {
    delete document._id
    return LocalCollection.update(document._id, document)
  } else {
    return LocalCollection.insert(document)
  }
}

Meteor.call('hudeData', (err, data) => {
  data.forEach(insertUpdate)
})

然后,您可以在客户端上使用LocalCollection来投影接收到的数据。

总而言之,将负载转移到客户端是一个很好的权衡。只要您让他们知道什么时候需要投影就可以了。

答案 2 :(得分:0)

我当前的想法是另外一个索引集合,其中包含1.entity id,2个字段名和3.field值。 将该集合编入索引,然后从那里提取有序的实体ID,稍后再通过ID加载完整的相关文档。