Mongoose обновляет множественный геопространственный индекс без ограничений - PullRequest
3 голосов
/ 28 марта 2012

У меня есть несколько моделей Mongoose с геопространственными индексами:

var User = new Schema({
  "name" : String,
  "location" : {
     "id" : String,
     "name" : String,
     "loc" : { type : Array, index : '2d'}
  }
});

Я пытаюсь обновить все элементы, находящиеся в области, например:

User.update({ "location.loc" : { "$near" : [ -122.4192, 37.7793 ], "$maxDistance" : 0.4 } }, { "foo" : "bar" },{ "multi" : true }, function(err){
    console.log("done!");
});

Однако этоПохоже, чтобы обновить только первые 100 записей.Глядя на документы, кажется, что есть собственный предел для find s для геопространственных индексов, который применяется, когда вы не устанавливаете ограничение.

(из документов : Используйте limit (), чтобы указать максимальное количество возвращаемых точек (по умолчанию применяется ограничение 100, если оно не указано) )

Это также относится к обновлениям независимо от флага multi,который является гигантским сопротивлением.Если я применяю обновление, оно обновляет только первые 100.

Сейчас единственный способ обойти это - сделать что-то отвратительное:

Model.find({"location.loc" : { "$near" : [ -122.4192, 37.7793 ], "$maxDistance" : 0.4 } },{limit:0},function(err,results){
   var ids = results.map(function(r){ return r._id; });
   Model.update({"_id" : { $in : ids }},{"foo":"bar"},{multi:true},function(){
      console.log("I have enjoyed crippling your server.");
   });
});

Хотя я даже не совсем уверен, что это сработает (и его можно было бы слегка оптимизировать, выбрав только _id), я бы очень хотел избежать хранения в памяти массива n идентификаторов, так какчисло может стать очень большим.

Редактировать: Вышеуказанный хак даже не работает, похоже, что find с {limit:0} все еще возвращает 100 результатов.Итак, в отчаянии и разочаровании я написал рекурсивный метод разбивки на страницы с помощью идентификаторов, а затем вернул их, чтобы я мог обновить их, используя описанный выше метод.Я добавил метод в качестве ответа ниже, но не принял его в надежде, что кто-то найдет лучший способ.

Насколько я могу судить, это проблема в ядре сервера Монготак что мангуст и нод-монгодб-натив не виноваты.Тем не менее, это действительно глупо, поскольку геопространственные индексы являются одной из немногих причин использования монго над некоторыми более надежными хранилищами NoSQL.

Есть ли способ достичь этого?Даже в node-mongodb-native или в оболочке mongo я не могу найти способ установить (или в этом случае удалить, установив в 0) ограничение на обновление.

1 Ответ

0 голосов
/ 29 марта 2012

Мне бы очень хотелось, чтобы эта проблема была исправлена, но я не могу придумать, как установить ограничение на обновление, и после тщательного изучения это не представляется возможным.Кроме того, взлом в вопросе даже не работает, я все еще получаю только 100 записей с находкой и limit, установленным на 0.

Пока это не исправлено в монго, вот как я могу обойти это: (!! ПРЕДУПРЕЖДЕНИЕ: УЖАСНЫЕ ХАКИ ВПЕРЕД: !!)

var getIdsPaginated = function(query,batch,callback){
  // set a default batch if it isn't passed.
  if(!callback){
    callback = batch;
    batch = 10000;
  }
  // define our array and a find method we can call recursively.
  var all = [],
      find = function(skip){
        // skip defaults to 0
        skip = skip || 0;
        this.find(query,['_id'],{limit:batch,skip:skip},function(err,items){
          if(err){
            // if an error is thrown, call back with it and how far we got in the array.
            callback(err,all);
          } else if(items && items.length){
            // if we returned any items, grab their ids and put them in the 'all' array
            var ids = items.map(function(i){ return i._id.toString(); });
            all = all.concat(ids);
            // recurse
            find.call(this,skip+batch);
          } else {
            // we have recursed and not returned any ids. This means we have them all.
            callback(err,all);
          }
        }.bind(this));
      };
  // start the recursion
  find.call(this);
}

Этот метод вернет гигантский массивиз _ids.Поскольку они уже проиндексированы, на самом деле это довольно быстро, но он все еще вызывает db гораздо больше, чем необходимо.Когда этот метод перезванивает, вы можете выполнить обновление с идентификаторами, например:

Model.update(ids,{'foo':'bar'},{multi:true},function(err){ console.log('hooray, more than 100 records updated.'); });

Это не самый элегантный способ решения этой проблемы, вы можете настроить его эффективность, установив пакет на основена ожидаемые результаты, но очевидно, что возможность просто вызывать update (или находить в этом отношении) по $ ближайшим запросам без ограничения действительно помогла бы.

...