Подтвердить что ты не робот

Могут ли mongo обновить данные массива?

У меня есть mongo-документ, подобный этому.

{
    "_id" : ObjectId("50b429ba0e27b508d854483e"),
    "array" : [
        {
            "id" : "1",
            "letter" : "a"
        },
        {
            "id" : "2",
            "letter" : "b"
        }
    ],
    "tester" : "tom"
}

Я хочу иметь возможность вставлять и обновлять array с помощью одной команды mongo и не использовать условное выражение в find(), а затем запускать insert() и update() в зависимости от наличия объекта.

id - это элемент, который я хочу быть селектором. Поэтому, если я обновляю массив следующим образом:

{
    "id" : "2",
    "letter" : "c"
}

Мне нужно использовать оператор $set

db.soup.update({
    "tester":"tom",
    'array.id': '2'
}, {
    $set: {
        'array.$.letter': 'c'
    }
})

И если я хочу вставить новый объект в массив

{
    "id" : "3",
    "letter" : "d"
}

Мне нужно использовать оператор $push

db.soup.update({
    "tester":"tom"
}, {
    $push: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

Мне нужен вид upsert для элемента массива.

Должен ли я делать это программно или я могу сделать это с помощью одного вызова манго?

4b9b3361

Ответ 1

Я не знаю, какой вариант будет включен во встроенный массив, как в MongoDB 2.2, поэтому вам, вероятно, придется обработать это в коде приложения.

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

Вы не можете выполнить upsert на основе значения поля во встроенном массиве, но вы можете использовать $addToSet для вставки встроенного документа, если он не" t уже существует:

db.soup.update({
    "tester":"tom"
}, {
    $addToSet: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

Это не соответствует вашему конкретному варианту использования соответствия id элемента массива, но может оказаться полезным, если вы знаете ожидаемое текущее значение.

Ответ 2

Я сам столкнулся с этой проблемой. Я не смог найти решение с одним вызовом, но нашел решение с двумя вызовами, которое работает, когда у вас есть уникальное значение в ваших элементах массива. Сначала используйте команду $pull, которая удаляет элементы из массива, а затем $push.

db.soup.update({
    "tester":"tom"
}, {
    $pull: {
        'array': {
            "id": "3"
        }
    }
})
db.soup.update({
    "tester":"tom"
}, {
    $push: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

Это должно работать, когда документ не существует, когда документ существует, но запись в массиве не существует и когда запись существует.

Опять же, это работает, только если у вас есть что-то вроде поля id в этом примере, которое должно быть уникальным для элементов массива.

Ответ 3

В некоторых случаях вы можете увеличить количество встроенных наборов документов, если вы можете реструктурировать документы для использования объектов вместо массивов. Я только что ответил на еще один вопрос, но пригодится здесь для удобства.


Пример документа будет

{
    tester: 'tom',
    items: {
        '1': 'a',
        '2': 'b'
    }
}

Чтобы обновить элемент с помощью id 3 и значения 'c', вы сделали бы

db.coll.update(
    { tester: 'tom' }, 
    { $set: { 'items.3': 'c' } }
)

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

db.coll.update(
    { tester: 'tom' }, 
    { 
        $set: { 'items.3': 'c' },
        $addToSet: { itemNames: 3 }
    }
)

(Помните, что $addToSet - это O (n).) При удалении свойства вы также должны извлекать его из массива.

db.widgets.update(
    { tester: 'tom' }, 
    { 
        $unset: { 'items.3': true },
        $pull: { itemNames: 3 }
    }
)