Загрузить документ со значением из фильтра - PullRequest
0 голосов
/ 19 февраля 2020

У меня есть коллекция со следующей структурой в MongoDB:

{
  "userId": String,
  "refs": Set<String>
}

Мне нужно обновить коллекции этими документами. Я хочу добавить к refs новую строку для пользователей, которые находятся в фильтре $in. Но, если пользователь не существует, мне нужно его "отшить".

В коде (golang) это выглядит так:

filter := bson.M{
    "userId": bson.M{
        "$in:": tokens // tokens is []string
    }
}

update := bson.M{
    "$addToSet": bson.M{
        "refs": newReference
    }
}

ctx, _ := newDbOperationContext()
_, err := driver.UpdateMany(ctx, filter, update)

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

Я установил в driver.UpdateMany(bson, bson, opts...) опцию options.UpdateOptions.SetUpsert(true)", но в результате я получил документ без userId:

{
  "_id": ObjectId("..."),
  "refs": ["new_reference"]
}

Итак, у меня вопрос, как сохранить новые значения с помощью поля userId.

Масштаб равен 2 * 10 ^ 6 пользователям для обновления, поэтому я хотел бы сделать это с помощью пакетного запроса. Я думаю, что создавать с использованием «один за другим» и обновлять его здесь не вариант.

Спасибо за вашу поддержку!

Ответы [ 3 ]

0 голосов
/ 19 февраля 2020

Рассматривая ваш вариант использования, я думаю, что лучшим решением будет следующее:

Поскольку у вас есть высокий масштаб и wi sh для выполнения пакетных запросов, лучше всего использовать BulkWrite: db Метод .collection.bulkWrite () предоставляет возможность выполнять массовые операции вставки, обновления и удаления. Пример: https://godoc.org/go.mongodb.org/mongo-driver/mongo#example -Collection-BulkWrite

При этом используется модель UpdateOne, но также поддерживается модель UpdateMany. Это также функция SetUpsert (true) Теперь для поля _id: ваш обновленный / вставленный документ должен иметь поле _id, чтобы новый документ имел это поле _id, иначе mongoDb автоматически генерирует поле _id при вставке документа, если ваш документ upsert делает не имеет поля _id

Я думаю, что не будет большой проблемой иметь поле _id в ваших документах, так что таким образом ваша проблема будет решена.

Что касается шкалы, я предлагаю использовать BulkWrite с моделями UpdateOne или UpdateMany.

Надеюсь, это поможет.

0 голосов
/ 23 февраля 2020

Согласно предыдущим вопросам в SO, таких как , этот и , этот другой не представляется возможным выполнить несколько upserts, используя только оператор $in, потому что он вставит только один документ (тот, который соответствует фильтру):

Если ни один документ не соответствует критериям запроса, db.collection.update () вставляет один документ .

Так что, как упомянул @Kartavya, лучше всего выполнять несколько операций записи, используя BulkWrite.

Для этого вам нужно добавить опцию upsert (= WriteModel ) для каждого пользователя в tokens в качестве фильтра, и для всех вы можете использовать одно и то же обновление $addToSet операция:

tokens := [...]string{"userId1", "userId3"}
newRef := "refXXXXXXX"

// all docs can use the same $addToSet update operation
updateOp := bson.D{{"$addToSet", bson.D{{"refs", newRef}}}}

// we'll append one update for each userId in tokens as a filter
upserts := []mongo.WriteModel{}
for _, t := range tokens {
    upserts = append(
        upserts,
        mongo.NewUpdateOneModel().SetFilter(bson.D{{"userId", t}}).SetUpdate(updateOp).SetUpsert(true))
}

opts := options.BulkWrite().SetOrdered(false)
res, err := col.BulkWrite(context.TODO(), upserts, opts)
if err != nil {
    log.Fatal(err)
}

fmt.Println(res)
0 голосов
/ 19 февраля 2020

В случае upsert, если документа нет, то в базу данных будет вставлена ​​только часть запроса на обновление. Вот почему ваш вывод такой. Вы можете увидеть здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...