在运行查询之前未创建Mongoose索引

时间:2017-06-10 03:49:29

标签: mongodb mongoose

运行我的测试时,我收到一条错误消息,指出我没有在标题字段上设置文本索引,但在运行我的应用程序文本搜索时,使用相同的模型可以正常搜索并且不会抛出错误。

   $ text查询所需的文本索引(没有这样的集合' test-db.torrents')

import mongoose from 'mongoose';
import Category from './category';

const Schema = mongoose.Schema;

const Torrent = new Schema({
    title: {
        type: String
    },
    category: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Category',
        required: true,
        index: true
    },
    size: Number,
    details: [
        {
            type: String
        }
    ],
    swarm: {
        seeders: Number,
        leechers: Number
    },
    lastmod: {
        type: Date,
        default: Date.now()
    },
    imported: {
        type: Date,
        default: Date.now()
    },
    infoHash: {
        type: String,
        unique: true,
        index: true
    }
});

Torrent.index({
    title: 'text'
}, {
    background: false
});

export default mongoose.model('Torrent', Torrent);

我使用ava进行测试,这是我的测试用例。

import mongoose from 'mongoose';
import request from 'supertest';
import test from 'ava';
import {makeApp} from '../helpers';

test.before(() => {
    mongoose.Promise = Promise;
    mongoose.connect('mongodb://localhost:27017/astro-test-db');
});

test.after.always(() => {
    mongoose.connection.db.dropDatabase(() => {
        mongoose.disconnect();
    });
});

// Should return [] since HL3 doesn't exist.
test('should return no search results', async t => {
    const app = makeApp();
    const res = await request(app).get(`/api/search?q=HL3`);

    t.is(res.status, 200);
    t.is(res.body.error, {});
    t.is(res.body.torrents.length, 0);
});

这里是ava的完整输出,因为你可以看到标题的索引没有用' text'来创建。或background: false

➜  astro git:(develop) ✗ yarn ava test/routes/search.js -- --verbose
yarn ava v0.24.6
$ "/Users/xo/code/astro/node_modules/.bin/ava" test/routes/search.js --verbose

Mongoose: categories.ensureIndex({ title: 1 }, { unique: true, background: true })
Mongoose: torrents.ensureIndex({ category: 1 }, { background: true })
Mongoose: torrents.count({}, {})
Mongoose: categories.ensureIndex({ slug: 1 }, { unique: true, background: true })
Mongoose: torrents.find({ '$text': { '$search': 'x' } }, { limit: 100, sort: { score: { '$meta': 'textScore' }, 'swarm.seeders': -1 }, fields: { score: { '$meta': 'textScore' } } })
  ✖ should return no search results 

  1 test failed [13:59:07]

  should return no search results
  /Users/xo/code/astro/test/routes/search.js:24

   23:     t.is(res.status, 200);            
   24:     t.is(res.body.error, {});         
   25:     t.is(res.body.torrents.length, 0);

  Difference:

    - Object {
    -   code: 27,
    -   codeName: "IndexNotFound",
    -   errmsg: "text index required for $text query (no such collection \'astro-test-db.torrents\')",
    -   message: "text index required for $text query (no such collection \'astro-test-db.torrents\')",
    -   name: "MongoError",
    -   ok: 0,
    - }
    + Object {}

  _callee$ (test/routes/search.js:24:7)
  tryCatch (node_modules/regenerator-runtime/runtime.js:65:40)
  Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:303:22)
  Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:117:21)
  step (test/routes/search.js:19:191)

error Command failed with exit code 1.

1 个答案:

答案 0 :(得分:4)

您应该确保索引是在"前景"中创建的。自"背景"创建是默认的。

Torrent.index({
    title: 'text'
},{ "background": false });

至少对于您的测试,否则查询可能会在创建索引之前运行。设置{ background: false }可确保在其他操作可以运行之前索引存在。这与MongoDB的default behavior相反,因此它需要是一个明确的设置。

在生产环境中,通常最好通过其他方式部署索引。还有"前景"创建导致较小的索引大小,但当然"块" IO,但在生产中至少一次这样做仍然更好。

来自documentation

的引文
  

默认情况下,MongoDB在前台构建索引,这会在索引构建时阻止对数据库的所有读写操作。此外,在前台索引构建期间,不会对所有数据库(例如listDatabases)执行读取或写入锁定的操作。

这意味着发生这种情况时不会发生读取或写入操作。因此,当在"前景"中创建索引时,无法插入数据并且无法运行查询。创作模式。

就尺寸而言,距离引文相同的页面稍远一点:

  

后台索引构建需要更长时间才能完成,并且导致索引最初比前台中构建的索引更大或更紧凑。随着时间的推移,在后台构建的索引的紧凑性将接近前景构建的索引。

因此,您可以在后台创建索引"随着时间的推移" 在生产环境中缩小到更紧凑的尺寸。但是对于测试和开发目的,您的默认值应该始终是在"前景"为了不被时间问题所困扰。

作为最小测试用例:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.set('debug', true);

var testSchema = new Schema({
  title: { type: String }
});

testSchema.index({
  title: 'text'
},{ background: false });

var Test = mongoose.model('Test', testSchema);

mongoose.connect('mongodb://localhost/texttest');

Test.create({ title: 'something here' },function(err,doc) {

  Test.find({ "$text": { "$search": "something" } },function(err,doc) {
    if (err) throw err;
   console.log(doc);

   Test.collection.drop(function(err) {
    if (err) throw err;
    mongoose.disconnect();
   });
  });
});

作为替代方法,请自动关闭mongooses自动索引功能并手动设置,然后通过.ensureIndexes()手动调用创建:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.set('debug', true);

var testSchema = new Schema({
  title: { type: String }
},{ autoIndex: false });

testSchema.index({
  title: 'text'
},{ background: false });

var Test = mongoose.model('Test', testSchema);

mongoose.connect('mongodb://localhost/texttest');

// Manually set indexing to on
Test.schema.options.autoIndex = true;
//console.log(Test.schema.options);

Test.ensureIndexes(function(err) {
  if (err) throw err;

  Test.create({ title: 'something here' },function(err,doc) {

    Test.find({ "$text": { "$search": "something" } },function(err,doc) {
      if (err) throw err;
      console.log(doc);

      Test.collection.drop(function(err) {
        if (err) throw err;
        mongoose.disconnect();
      });
    });
  });
});