2015-09-07 4 views
1

Моя коллекция мангуст выглядит примерно так:простой способ обновить массив с MongoDB

var followSchema = new Schema({ 
    facebookId: {type: String, required: true}, 
    players : [], 
    fans: [], 
}); 

Когда игрок хочет следовать другому пользователю, я добавляю, что идентификатор пользователя в игроков [] массива.

Для достижения этой цели я первый посмотреть запись игрока:

var myRecord = FollowModel.findOneAndUpdate(
     {facebookId: req.user.facebookId}, 
     {$setOnInsert: {}}, 
     {upsert: true, new : true} 
    ); 

выше гарантирует, что если игрок не существует, он будет создан.

Тогда я идти о проверке "игроков [] массив:

myRecord.exec(function(err, result) { 
    if (err) { 
    throw err; 
    } 

    if (result.players.indexOf(req.body.idToFollow) < 0) { 
    result.players.push(req.body.idToFollow); 
    result.save(); 
    res.status(200).send('Player added to Follow List'); 
    } else { 
    res.status(200).send('Already Following this player'); 
    } 
}); 

мне было просто интересно, если есть более прямолинейный и ясный способ написания этого запроса?

ответ

0

Если вы «не все равно» о добавлении немного больше функциональных возможностей здесь (очень рекомендуется) и ограничивая нагрузку обновлений, где вы на самом деле не делают никакой необходимости, чтобы вернуть измененный документ, или даже если вы делаете, то это будет всегда лучше использовать атомные операторы с массивами вроде $push и $addToSet.

«Дополнительная функциональность» также заключается в том, что при использовании массивов в хранилище очень разумной практикой является сохранение «длины» или «подсчета» элементов. Это становится полезным в запросах и может быть эффективно доступно с помощью «индекса», в отличие от других методов получения «подсчета» массива или использования этого «подсчета/длины» для целей фильтрации.

Лучшая конструкция здесь заключается в использовании "Bulk" operations, поскольку тестирование присутствующих элементов массива не очень хорошо сочетается с концепцией «upserts», поэтому, когда вы хотите повысить функциональность, тестирование массива лучше в двух операциях. Но так как операции «Bulk» могут быть отправлены на сервер с «одним запросом», и вы также получаете «один ответ», то это уменьшает любые реальные накладные расходы при этом.

var bulk = FollowModel.collection.initializeOrderedBulkOp(); 

// Try to add where not found in array 
bulk.find({ 
    "facebookId": req.user.facebookId, 
    "players": { "$ne": req.body.idToFollow } 
}).updateOne({ 
    "$push": { "players": req.body.idToFollow }, 
    "$inc": { "playerCount": 1 } 
}); 

// Otherwise create the document if not matched 
bulk.find({ 
    "facebookId": req.user.facebookId, 
}).upsert().updateOne({ 
    "$setOnInsert": { 
     "players": [req.body.idToFollow] 
     "playerCount": 1, 
     "fans": [], 
     "fanCount": 0 
    } 
}) 

bulk.execute(function(err,result) { 
    // Handling in here 
}); 

Как это работает в том, что первая попытка там пытается найти документ, в котором элемент массива будет добавлен нет уже в массиве. Мы не пытаемся создать «upsert» здесь, так как вы не хотите создавать новый документ, если единственная причина, по которой он не соответствует документу, заключается в том, что элемент массива отсутствует. Но если это согласовано, то новый член добавляется к массиву, а текущий «счет» «увеличивается» на 1 через $inc, который сохраняет общее количество или длину.

Таким образом, второй оператор будет соответствовать только документу и поэтому использует «upsert», поскольку, если документ не найден для поля ключа, он будет создан. Поскольку все операции находятся внутри $setOnInsert, тогда операция не будет выполняться, если документ уже существует.

Это всего лишь один запрос и ответ на сервер, поэтому нет никаких «повторений и четверга» для включения двух операций обновления, и это делает его эффективным.

Удаление записи массива в основном в обратном направлении, за исключением того, что на этот раз нет никакой необходимости «создать» новый документ, если он не был найден:

var bulk = FollowModel.collection.initializeOrderedBulkOp(); 

// Try to remove where found in array 
bulk.find({ 
    "facebookId": req.user.facebookId, 
    "players": req.body.idToFollow 
}).updateOne({ 
    "$pull": { "players": req.body.idToFollow }, 
    "$inc": { "playerCount": -1 } 
}); 

bulk.execute(function(err,result) { 
    // Handling in here 
}); 

Так что теперь вам нужно только проверить, где элемент массива присутствует и где тогда находится $pull согласованный элемент из содержимого массива, в то же время, как «декрементирование» «счет» на 1, чтобы отразить удаление.

Теперь вы «можете» использовать здесь $addToSet, так как он просто посмотрит на содержимое массива, и если элемент не будет найден, он будет добавлен, и по тем же причинам нет необходимости тестировать массив элемент, существующий при использовании $pull, поскольку он просто ничего не сделает, если элемент отсутствует. Futhermore $addToSet в этом контексте может быть использован непосредственно в качестве «upsert», до тех пор, пока вы не «перекрестных путей», так как это не допускается, чтобы попытаться использовать несколько операторов обновления на том же пути с MongoDB:

FollowModel.update(
    { "facebookId": req.user.facebookId }, 
    { 
     "$setOnInsert": { 
      "fans": [] 
     }, 
     "$addToSet": { "players": req.body.idToFollow } 
    }, 
    { "upsert": true }, 
    function(err,numAffected) { 
     // handling in here 
    } 
); 

Но это было бы «неправильно»:

FollowModel.update(
    { "facebookId": req.user.facebookId }, 
    { 
     "$setOnInsert": { 
      "players": [],    // <-- This is a conflict 
      "fans": [] 
     }, 
     "$addToSet": { "players": req.body.idToFollow } 
    }, 
    { "upsert": true }, 
    function(err,numAffected) { 
     // handling in here 
    } 
); 

Однако, делая это, вы теряете «подсчета» функциональность, поскольку такие операции только завершая без учета того, что на самом деле там, или если что-то было «добавлено» или «удален».

Сохранение «счетчиков» - это действительно хорошо, и даже если у вас нет непосредственного использования для них прямо сейчас, то на каком-то этапе жизненного цикла вашего приложения вы, вероятно, захотите их. Поэтому имеет смысл понять логику и реализовать ее сейчас. Небольшая цена для оплаты теперь принесет большую пользу позже.


Быстрое оповещение здесь, как я обычно рекомендую, если возможно, «массовые» операции. При использовании этого с помощью аксессуара .collection в мангусте вам следует знать, что это родные методы драйвера и поэтому ведут себя иначе, чем методы «мангуста».

В частности, все методы «mongoose» имеют встроенную «проверку», чтобы увидеть, что соединение с базой данных в настоящее время активно. Там, где это не так, операция эффективно «ставится в очередь» до тех пор, пока соединение не будет выполнено. Используя собственные методы, эта «проверка» больше не присутствует. Поэтому вам нужно либо убедиться, что соединение уже присутствует из метода «mongoose», выполняющего «первый», либо поочередно обертывать всю логику приложения в конструкцию, которая «ждет» для соединения, которое должно быть выполнено:

mongoose.connection.on("open",function(err) { 
    // All app logic or start in here 
}); 

Таким образом, вы уверены, что существует соединение, и правильные объекты могут быть возвращены и использованы методами. Но никакое соединение, и операции «Массовые» не сработают.

0

Если вы не заботитесь о том, зная, что последующие уже создан, то вы можете использовать обозначение $ addToSet в исходном запросе:

var myRecord = FollowModel.findOneAndUpdate(
     {facebookId: req.user.facebookId}, 
     {$setOnInsert: {}, 
     $addToSet: {players: req.body.idToFollow}}, 
     {upsert: true, new : true} 
    ); 

Я не уверен, что вы делаете с этим пустой $ setOnInsert.

Смежные вопросы