MongoDB - $, установленный для обновления или нажатия элемента Array

В коллекции продуктов у меня есть массив недавних просмотров, в котором есть 2 поля, которые смотрелиBy и смотрелиDate.

В сценарии, если у меня уже есть запись с viewedby, мне нужно ее обновить. Например, например, если у меня есть массив: -

"recentviews" : [ 
        {
            "viewedby" : "abc",
            "vieweddate" : ISODate("2014-05-08T04:12:47.907Z")
        }
    ]

И пользователь abc, поэтому мне нужно обновить выше, а если нет записи для abc, я должен $push.

Я пробовал $set следующим образом: -

db.products.update( { _id: ObjectId("536c55bf9c8fb24c21000095") },
                    { $set: 
                        { "recentviews": 
                            { 
                                viewedby: 'abc',
                                vieweddate: ISODate("2014-05-09T04:12:47.907Z") 
                            }
                        } 
                     }
            )

Вышеуказанный запрос уничтожает все мои другие элементы в массиве.

Ответ 1

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

То, что вы ищете, является частью оператора positional $. Вам потребуется часть вашего запроса, чтобы также "найти" элемент требуемого массива.

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$.vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

Таким образом, $ обозначает совпадающую позицию в массиве, поэтому часть обновления знает, какой элемент в массиве обновляется. Вы можете получить доступ к отдельным полям документа в массиве или просто указать весь документ для обновления в этой позиции.

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

Если поля фактически не изменяются, и вы просто хотите вставить новый элемент массива, если он не существует точно так же, вы можете использовать $addToSet

db.products.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        $addToSet:{ 
            "recentviews": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    }
)

Однако, если вы просто ищете "толкание" к массиву с помощью значения исключительного ключа, если этого не существует, вам нужно сделать еще одну ручную обработку, сначала увидев, существует ли элемент в массиве, а затем $push, где это не так.

Вы получаете некоторую помощь от методов mongoose при этом, отслеживая количество документов, на которые влияет обновление:

Product.update(
    { 
        "_id": ObjectId("536c55bf9c8fb24c21000095"),
        "recentviews.viewedby": "abc"
    },
    { 
        "$set": { 
            "recentviews.$": {
                 "viewedby": "abc",
                 "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
        }
    },
    function(err,numAffected) {

        if (numAffected == 0) {
            // Document not updated so you can push onto the array
            Product.update(
                { 
                    "_id": ObjectId("536c55bf9c8fb24c21000095")
                },
                { 
                    "$push": { 
                        "recentviews": {
                            "viewedby": "abc",
                            "vieweddate": ISODate("2014-05-09T04:12:47.907Z")
                        }
                    }
                },
                function(err,numAffected) {

                }
            );
        }            

    }
);

Единственное слово предостережения здесь заключается в том, что в сообщениях writeConcern от MongoDB 2.6 до более ранних версий есть немного изменений в реализации. Непонятно, как теперь API-интерфейс mongoose реализует возврат аргумента numAffected в обратном вызове, что разница может означать что-то.

В предыдущих версиях, даже если данные, отправленные в исходном обновлении, точно соответствовали существующему элементу, и не было никаких реальных изменений, тогда "измененная" сумма будет возвращена как 1, хотя ничего не было фактически обновлено.

От MongoDB 2.6 ответ на реакцию на запись состоит из двух частей. Одна часть показывает измененный документ, а другой показывает совпадение. Поэтому, пока совпадение будет возвращено частью запроса, соответствующей существующему элементу, фактический измененный счетчик документов будет возвращаться как 0, если на самом деле не было никаких изменений.

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