【问题标题】:Search for Substring in several fields with MongoDB and Mongoose使用 MongoDB 和 Mongoose 在多个字段中搜索 Substring
【发布时间】:2015-07-21 16:24:56
【问题描述】:

非常抱歉,但是经过一天的研究和尝试所有不同的组合和 npm 包后,我仍然不确定如何处理以下任务。

设置:

  • MongoDB 2.6
  • Node.JS 与 Mongoose 4

我有这样的架构:

var trackingSchema = mongoose.Schema({
  tracking_number: String,
  zip_code: String,
  courier: String,
  user_id: Number,
  created: { type: Date, default: Date.now }, 
  international_shipment: { type: Boolean, default: false }, 
  delivery_info: { 
    recipient: String, 
    street: String, 
    city: String
  }
});

现在用户给了我一个搜索字符串,一个字符串数组,这将是我要搜索的 子字符串

var search = ['15323', 'julian', 'administ'];

现在我想找到那些文档,其中任何字段 tracking_numberzip_codedelivery_info 中的这些字段都包含我的搜索元素。

我该怎么做?我知道有索引,但我可能需要一个复合索引,或者可能是一个文本索引?对于搜索,我可以使用RegEx,还是$text $search 语法?

问题是我有几个字符串要查找(我的search)和几个字段要查找。由于其中一个方面,每种方法在某些时候对我来说都失败了。

【问题讨论】:

    标签: regex node.js mongodb search mongoose


    【解决方案1】:

    您的用例非常适合text search

    在可搜索字段的架构上定义文本索引:

    trackingSchema.index({
        tracking_number: 'text',
        zip_code: 'text',
        'delivery_info.recipient': 'text',
        'delivery_info.street': 'text',
        'delivery_info.city': 'text'
    }, {name: 'search'});
    

    将您的搜索词加入单个字符串并使用$text 查询运算符执行搜索:

    var search = ['15232', 'julian'];
    Test.find({$text: {$search: search.join(' ')}}, function(err, docs) {...});
    

    即使这会将所有搜索值作为单个字符串传递,这仍然会执行值的逻辑 OR 搜索。

    【讨论】:

    • 是的,我认为文本搜索非常适合。两个问题:第一个:如果我做search.join(' '),它将在我的搜索字段中搜索'15232 julian',而不是'15232'的出现@ 987654331@,还是会?因为我想单独搜索每个搜索字符串。 第二:这会比我使用普通索引的解决方案更快(在我的回答中)。
    • @Julian 查看更新的答案。这可能比您的解决方案具有更好的性能,因为使用正则表达式的索引是limited,因为它没有锚定到字符串的开头。
    • 啊,我明白了!感谢您的澄清!有没有办法对所有搜索字符串强制执行逻辑 and
    • 其实用多了之后发现text-search不太合适。如MongoDB docs 中所述,文本搜索匹配完整的词干。例如,如果文档字段包含单词 blueberry,则对术语 blue 的搜索将与文档不匹配。 因此,将永远找不到子字符串。
    • @Julian 好的,您问题中的示例搜索词似乎是整个单词,所以这就是这个答案的基础。
    【解决方案2】:

    为什么不试试

    var trackingSchema = mongoose.Schema({
      tracking_number: String,
      zip_code: String,
      courier: String,
      user_id: Number,
      created: { type: Date, default: Date.now }, 
      international_shipment: { type: Boolean, default: false }, 
      delivery_info: { 
        recipient: String, 
        street: String, 
        city: String
      }
    });
    var Tracking = mongoose.model('Tracking', trackingSchema );
    
    var search = [ "word1", "word2", ...]
    var results = []
    for(var i=0; i<search.length; i++){
        Tracking.find({$or : [
          { tracking_number : search[i]}, 
          {zip_code: search[i]}, 
          {courier: search[i]}, 
          {delivery_info.recipient: search[i]}, 
          {delivery_info.street: search[i]}, 
          {delivery_info.city: search[i]}] 
        }).map(function(tracking){
        //it will push every unique result to variable results
        if(results.indexOf(tracking)<0) results.push(tracking);
        });
    

    【讨论】:

    • 有趣。我认为将逻辑移动到数据库会更好。嗯。
    • 这主要取决于您想要实现的目标,但我认为这样的逻辑应该保留在数据库中。数据库引擎有自己的搜索模块/查询,无需在您的应用程序部分再次开发。不过,如果您每分钟要处理 3000k 个请求,您可能需要重新设计这个解决方案(postgreSQL 是个好主意,但那时需要一些 orm)。
    【解决方案3】:

    好的,我想出了这个。

    我的架构现在有一个额外的字段 search,其中包含我所有可搜索字段的数组:

    var trackingSchema = mongoose.Schema({
        ...
        search: [String]
    });
    

    使用预保存挂钩,我填充此字段:

    trackingSchema.pre('save', function(next) {
    
      this.search = [ this.tracking_number ];
    
      var searchIfAvailable = [
        this.zip_code,
        this.delivery_info.recipient,
        this.delivery_info.street,
        this.delivery_info.city
      ];
    
      for (var i = 0; i < searchIfAvailable.length; i++) {
        if (!validator.isNull(searchIfAvailable[i])) {
          this.search.push(searchIfAvailable[i].toLowerCase());
        }
      }
    
      next();
    });
    

    为了提高性能,我还索引了该字段(也是user_id,因为我以此来限制搜索结果):

    trackingSchema.index({ search: 1 });
    trackingSchema.index({ user_id: 1 });
    

    现在,在搜索时,我首先列出我想在数组中查找的所有子字符串:

    var andArray = [];
    var searchTerms = searchRequest.split(" ");
    searchTerms.forEach(function(searchTerm) {
      andArray.push({
        search: { $regex: searchTerm, $options: 'i'
        }
      });
    });
    

    我在我的find() 中使用这个数组并将它与$and 链接起来:

      Tracking.
        find({ $and: andArray }).
          where('user_id').equals(userId).
          limit(pageSize).
          skip(pageSize * page).
          exec(function(err, docs) {
             // hooray!
          });
    

    这行得通。

    【讨论】:

    • 如果所有的索引都设置好了,这个速度非常快。
    猜你喜欢
    • 2022-01-25
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 2017-05-14
    • 2021-05-16
    • 2018-07-13
    • 1970-01-01
    相关资源
    最近更新 更多