使用MongoDB和Mongoose在多个字段中搜索Substring

时间:2015-07-21 16:24:57

标签: regex node.js mongodb search mongoose

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

设置:

  • MongoDB 2.6
  • 使用Mongoose 4的Node.JS

我有一个这样的架构:

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),以及要查看的几个字段。由于其中一个方面,每个方法在某些时候都失败了。

3 个答案:

答案 0 :(得分:2)

您的使用案例非常适合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) {...});

即使这会将您的所有搜索值作为单个字符串传递,但仍会对值进行逻辑或搜索。

答案 1 :(得分:1)

好的,我想出了这个。

我的架构现在有一个额外的字段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!
      });

这很有效。

答案 2 :(得分:1)

为什么不尝试

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);
    });