Документация Upsert / UpsertId для драйвера golang mongodb mgo

В документации mongodb говорится:

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

И в документации mgo говорится:

Upsert находит единственный документ, соответствующий предоставленному документу селектора, и изменяет его в соответствии с документом обновления. Если документ, соответствующий селектору, не найден, документ обновления применяется к документу селектора, и результат вставляется в коллекцию.

Но если я сделаю подобное:

session.UpsertId(data.Code, data)

В итоге я получаю запись, в которой mongodb автоматически генерирует ObjectID вместо data.Code.

это означает, что UpsertId ожидает, что данные будут сформированы с помощью операторов обновления, и вы не можете использовать произвольную структуру? Или что мне здесь не хватает?

Pd. Mongo 2.4.9 mgo v2 golang go версия devel + f613443bb13a

РЕДАКТИРОВАТЬ:

Это пример того, что я имею в виду, используя образец кода от Нила Ланна:

package main

import (
  "fmt"
  "gopkg.in/mgo.v2"
  // "gopkg.in/mgo.v2/bson"
)

type Person struct {
  Code string
  Name  string
}

func main() {
  session, err := mgo.Dial("admin:admin@localhost");

  if err != nil {
        fmt.Println("Error: ", err)
        return
    // panic(err)
  }

  defer session.Close()

  session.SetMode(mgo.Monotonic, true)

  c := session.DB("test").C("people")

  var p = Person{
    Code: "1234",
    Name: "Bill",
  }

  _, err = c.UpsertId( p.Code, &p )

  result := Person{}
  err = c.FindId(p.Code).One(&result)
  if err != nil {
        fmt.Println("FindId Error: ", err)
        return
    // panic(err)
  }

  fmt.Println("Person", result)

}

person Gabriel Díaz    schedule 23.07.2014    source источник


Ответы (2)


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

Пример кода, предоставленный Нилом Ланном, будет выглядеть так:

package main

import (
  "fmt"
  "gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
)

type Person struct {
  Code string
  Name  string
}

func main() {
  session, err := mgo.Dial("admin:admin@localhost");

  if err != nil {
        fmt.Println("Error: ", err)
        return
  }

  defer session.Close()

  session.SetMode(mgo.Monotonic, true)

  c := session.DB("test").C("people")

  var p = Person{
    Code: "1234",
    Name: "Bill",
  }
    upsertdata := bson.M{ "$set": p}

    info , err2 := c.UpsertId( p.Code, upsertdata )
    fmt.Println("UpsertId -> ", info, err2)
  result := Person{}
  err = c.FindId(p.Code).One(&result)
  if err != nil {
        fmt.Println("FindId Error: ", err)
        return
  }

  fmt.Println("Person", result)

}

Большое спасибо за проявленный интерес и помощь Нилу.

person Gabriel Díaz    schedule 23.07.2014

Кажется, вы говорите здесь о назначении структуры с настраиваемым полем _id. Это действительно сводится к тому, как вы определяете свою структуру. Вот краткий пример:

package main

import (
  "fmt"
  "gopkg.in/mgo.v2"
  "gopkg.in/mgo.v2/bson"
)

type Person struct {
  ID    string `bson:"_id"`
  Name  string
}

func main() {
  session, err := mgo.Dial("127.0.0.1");

  if err != nil {
    panic(err)
  }

  defer session.Close()

  session.SetMode(mgo.Monotonic, true)

  c := session.DB("test").C("people")

  var p = Person{
    ID: "1",
    Name: "Bill",
  }

  _, err = c.UpsertId( p.ID, &p )

  result := Person{}
  err = c.Find(bson.M{"_id": p.ID}).One(&result)
  if err != nil {
    panic(err)
  }

  fmt.Println("Person", result)

}

Итак, в настраиваемом определении здесь я сопоставляю поле идентификатора с bson _id и определяю его тип как строку. Как показано в примере, это именно то, что происходит при сериализации через UpsertId и последующем извлечении.


Теперь, когда вы уточнили, я укажу на разницу в определении struct.

То, что у меня есть, производит это:

{ "_id": 1, "name": "Bill" }

То, что у вас есть (без того же сопоставления в структуре), делает следующее:

{ "_id": ObjectId("53cfa557e248860d16e1f7e0"), "code": 1, "name": "Bill" }

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

type Person struct {
    Code string `bson:"_id"`
    Name string
}

Это сопоставляет поле с обязательным полем _id, в противном случае оно создается автоматически.

person Neil Lunn    schedule 23.07.2014
comment
В структуре нет поля ID, поэтому UpsertId пригодится. Но он не делает того, что говорится в документации mgo. Вот почему я попал в документацию mondogdb. Я хочу, чтобы Data.Code был _id, но в структуре нет bson:"_id", как из внешнего пакета. - person Gabriel Díaz; 23.07.2014
comment
@ GabrielDíaz В остальном я не совсем понимаю, о чем вы спрашиваете. Похоже, вы хотите предоставить что-то другое, например _id. Вот что делает и работает этот код. Если вы имеете в виду другое, то, возможно, вы сможете прояснить свой вопрос. Upsert будет использовать заданное значение _id, если нет другой причины, по которой он этого не делает. - person Neil Lunn; 23.07.2014
comment
Я отредактировал ваш образец тем, что делаю. Надеюсь, это поможет понять вопрос. Спасибо! - person Gabriel Díaz; 23.07.2014
comment
@ GabrielDíaz Если я понимаю тон вашего вопроса, вы говорите, что upsert добавляет свое собственное _id значение. Из различий между моим списком и вашим, причина ясна. Посмотрите на отображение на структуре. Это часть сериализации BSON, о которой я сказал. - person Neil Lunn; 23.07.2014
comment
Я понимаю, о чем вы говорите, но у меня другой вопрос. Чтение документации по драйверу mgo.Если документ, соответствующий селектору, не найден, документ обновления применяется к документу селектора, и результат вставляется в коллекцию, я ожидаю, что Upsert объединит предоставленный идентификатор и предоставленную структуру как объект, а затем вставьте это в базу данных. Это не работает, так что это означает в документации? - person Gabriel Díaz; 23.07.2014
comment
@ GabrielDíaz Ничего особенного. Я добавил информацию, чтобы показать вам, где вы делаете это неправильно. Взглянем. - person Neil Lunn; 23.07.2014
comment
Я это понимаю. Я не могу этого сделать, потому что структура взята из стороннего пакета, не так ли? Насколько я понимаю, либо документация написана неправильно, либо UpsertId не выполняет то, что говорится в документации. Я знаю, как заставить его работать, я просто хочу быть уверенным, что это документация, которая не так ясна, как должна быть, или что есть проблема с Upsert. Спасибо! - person Gabriel Díaz; 23.07.2014
comment
@ GabrielDíaz В документации нет ничего плохого. Для справки в будущем вы почти наверняка будете сбиты с толку, если предположите, что это не работает так, как вы говорите. Но теперь вы, кажется, указываете на реальный вопрос вроде «Как создать подкласс структуры?». Я думаю, вы можете согласиться с тем, чего хотите, а не с вопросом, который вы задали. Я прав? - person Neil Lunn; 23.07.2014
comment
Если вы посмотрите на последний образец кода, я не делаю никаких подклассов, чтобы заставить его работать, похоже, что документация mongodb также применима к драйверу mgo, и что для применения требуется предложение, если документ обновления указан операторы обновления. Если я ошибаюсь, то снова начну брать уроки английского :) - person Gabriel Díaz; 23.07.2014
comment
@ GabrielDíaz По моим подсчетам, у вас все еще есть возможность проголосовать за полезные ответы. Вы цитировали меня достаточно раз, чтобы убедиться, что это правда. Я также считаю, что вы используете неправильный подход и что вы должны делать то, что я говорю, вы должны делать. И это было бы не только голосованием за полезную помощь, но и принятием ответа, на который иначе вы бы никогда не пришли. - person Neil Lunn; 23.07.2014