Relationships in mgo - mongodb

I've written some simple program with golang and mgo. My question is how to properly to relationships in mgo.
1st Approach:
type User struct {
Id bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"`
Username string `json:"username" bson:"username"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
Friends []User `json:"friends" bson:"friends"`
}
"Friends" is a slice of Users. I can $push a pointer to a User and it just works fine. The thing is that I only want to store a reference to the user and not nesting it:
2nd Approach:
type User struct {
Id bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"`
Username string `json:"username" bson:"username"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
Friends []bson.ObjectId `json:"friends" bson:"friends"`
}
This gives me the output I want - but now it's not visible from the struct which nested structs are referenced. Does mgo provide some mechanism to deal with this?

mgo is a db driver library and not an ORM..
What I'd do is have the ids array as in the 2nd example (unexported, with lowercase) and have a Friends() method which queries the db by those ids and return a []User

Related

Aggregation and filter in Golang in MongoDB

i use https://github.com/Kamva/mgm for my Golang project with MongoDB as database. I´ve been searching a way to aggregate and filter a collection.
Here my working Aggregation of the Author (a user) of a article:
authorCollName := mgm.Coll(&Models.User{}).Name()
articles := []Models.Article{}
err = mgm.Coll(&Models.Article{}).SimpleAggregate(&articles, builder.Lookup(authorCollName, "authorid", "_id", "author"))
But i want filter it on a specific category like here in this simple find:
err = collection.SimpleFind(&articles, bson.D{{"category", objID}})
The Models:
type Article struct {
mgm.DefaultModel `bson:",inline"`
Name string `json:"name" bson:"name"`
AuthorId primitive.ObjectID `json:"authorId" bson:"authorid"`
Author []User `json:"author" form:"author"`
Category primitive.ObjectID `json:"category,omitempty"`
}
type User struct {
mgm.DefaultModel `bson:",inline"`
Forename string `json:"forename"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
Can someone give me a hint, how i can solve it that i can use aggregation and a filter together?
The solution:
err = mgm.Coll(&Models.Article{}).SimpleAggregate(&articles, builder.Lookup(authorCollName, "authorid", "_id", "author"), M{operator.Match: M{"category": objID}})

Golang official MongoDB driver ObjectID weird behavior

I am trying to use the official mongodb driver in golang and am seeing something unexpected.
If I have a struct like
type User struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
}
I create a new instance of this with Name and Email but omit ID expecting that the DB will fill this with its value. Instead it uses all zeroes and so the second and so on inserts fail with
multiple write errors: [{write errors: [{E11000 duplicate key error collection: collection.name index: _id_ dup key: { : ObjectId('000000000000000000000000') }}]}, {<nil>}]
If I use a *primitive.ObjectID I get the same class of error only on null instead of zeroes
multiple write errors: [{write errors: [{E11000 duplicate key error collection: collection.name index: _id_ dup key: { : null }}]}, {<nil>}]
It doesn't matter if I use the omitempty directive or not, same result.
If I omit the ID field entirely, it works, but then my struct doesn't have that data on it.
Is there a way to have the DB handle the ID? Or MUST I explicitly call the NewObjectID() function on the struct?
It doesn't matter if I use the omitempty directive or not, same result.
omitempty tag on ID should work. For example:
type User struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
}
collection.InsertOne(context.Background(), User{Name:"Foo", Email:"Baz"})
If you don't specify the omitepmty tag, then the behaviour that you observed is just Go structs behaviour; whereby if any of struct fields are omitted it will be zero-valued. In this case because you have specified the field type to be primitive.ObjectID, ObjectId('000000000000000000000000') is the zero value.
This is the reason why you need to generate a value first before inserting, i.e.:
collection.InsertOne(context.Background(),
User{ ID: primitive.NewObjectID(),
Name: "Foo",
Email: "Bar"})
Is there a way to have the DB handle the ID?
Technically, it's the MongoDB driver that automatically generates the ObjectId if it's not supplied before sending to server.
You can try to use bson.M instead of a struct when inserting to leave out the _id field, i.e.
collection.InsertOne(context.Background(),
bson.M{"name":"Foo", "email":"Bar"})
Code snippet above is written using mongo-go-driver v1.3.x
The omitempty struct tag should work:
type User struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
}
The primitive.ObjectID type implements the bsoncodec.Zeroer interface, so it should be omitted from the document if it's the empty object ID (all 0's) and the driver will generate a new one for you. Can you try this and post the output?

Why there is no "OR" operator in "...WhereInput" anymore? (prisma 1.25.4)

Prisma 1.23 had "OR,AND" in "...WhereInput", but in version 1.25.4 there is no "OR" operator, it's just "AND"
The database is mongodb
input ProvinceWhereInput {
AND: [ProvinceWhereInput!]
id: ID
id_not: ID
id_in: [ID!]
id_not_in: [ID!]
id_lt: ID
id_lte: ID
id_gt: ID
id_gte: ID
id_contains: ID
id_not_contains: ID
id_starts_with: ID
id_not_starts_with: ID
id_ends_with: ID
id_not_ends_with: ID
name: String
name_not: String
name_in: [String!]
name_not_in: [String!]
name_lt: String
name_lte: String
name_gt: String
name_gte: String
name_contains: String
name_not_contains: String
name_starts_with: String
name_not_starts_with: String
name_ends_with: String
name_not_ends_with: String
cities_some: CityWhereInput
}
These were disabled to allow for a quicker implementation of relational filters towards non-embedded types (cities_some in your case if it is a non-embedded type would not have been available in 1.23 for example).
You can follow this issue to get notified once they get reenabled.
https://github.com/prisma/prisma/issues/3897

How to work with multiple types in single collection with mgo

I'm very new to golang and try to write a simple event-sourcing user-management webapi using mongodb as backing database. Now i have User, which looks something like this:
type User struct {
Id bson.ObjectId `json:"id" bson:"_id"`
UserName string `json:"username" bson:"username"`
Email string `json:"email" bson:"email"`
PwdHash string `json:"pwd_hash" bson:"pwd_hash"`
FullName string `json:"fullname" bson:"fullname"`
}
and three events, happening to user, when somebody uses api:
type UserCreatedEvent struct {
UserId bson.ObjectId `json:"id" bson:"_id"`
//time when event was issued
CreatedEpoch time.Time `json:"created_epoch" bson:"created_epoch"`
//id of user that made a change
IssuedByUserId bson.ObjectId `json:"issuedby_userid" bson:"issuedby_userid"`
}
type UserDeletedEvent struct {
UserId bson.ObjectId `json:"id" bson:"_id"`
CreatedEpoch time.Time `json:"created_epoch" bson:"created_epoch"`
//id of user that made a change
IssuedByUserId bson.ObjectId `json:"issuedby_userid" bson:"issuedby_userid"`
}
type UserUpdatedEvent struct {
UserId bson.ObjectId `json:"id" bson:"_id"`
CreatedEpoch time.Time `json:"created_epoch" bson:"created_epoch"`
//id of user that made a change
IssuedByUserId bson.ObjectId `json:"issuedby_userid" bson:"issuedby_userid"`
ChangedFieldName string `json:"changed_field_name" bson:"changed_field_name"`
NewChangedFieldValue string `json:"new_changed_field_value" bson:"new_changed_field_value"`
}
Now i'm stuck on saving and retrieving events from db. The problem is i want to store them in a single collection, so that i have a full plain history of user modifications. But i can't find how to correctly store event type name as a mongo document field and then use it in searches. What is the idiomatic go-way to do this?
I'll be gratefull for any help.
What's nice about a non-relational database is that a little redundancy is OK and is faster for retrieval since you're not joining things together. You could just add your bottom three objects as properties on your User in whatever fashion makes sense for you and your data. Then you wouldn't need to store the UserId on those objects either.
If you need to search over the Events quickly, you could create another collection to hold them. You'd be inserting to two collections, but retrieval times/logic should be pretty good/easy to write.
Your new Event would be something like:
type UserEventType int
const (
Created UserEventType = iota
Deleted
Updated
)
type UserEvent struct {
UserId bson.ObjectId `json:"id" bson:"_id"`
CreatedEpoch time.Time `json:"created_epoch" bson:"created_epoch"`
//id of user that made a change
IssuedByUserId bson.ObjectId `json:"issuedby_userid" bson:"issuedby_userid"`
ChangedFieldName string `json:"changed_field_name,omitempty" bson:"changed_field_name,omitempty"`
NewChangedFieldValue string `json:"new_changed_field_value,omitempty" bson:"new_changed_field_value,omitempty"`
EventType UserEventType `json:"user_event_type" bson:"user_event_type"`
}
Notice the omitempty on the fields that are optional depending on the type of event.
You really aren't supposed to store different objects in the same collection. Here's a line from the Mongo documentation:
MongoDB stores documents in collections. Collections are analogous to tables in relational databases.
In case you aren't familiar with relational databases, a table would essentially represent one type of object.

Matching structure to mgo result

I have the following document in my local mongodb:
_id 25dd9d29-efd5-4b4e-8af0-360c49fdba31
name Reykjavik
initialDiseaseColouring blue
In my code I set up a city structure as the following:
type City struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string
InitialDiseaseColouring string
}
I'm querying it using
result := City{}
collection.Find(bson.M{"name":"Reykjavik"}).One(&result)
When I try to access the initialDiseaseColouring attribute it's not there
This is result when I print it:
{ObjectIdHex("32356464396432392d656664352d346234652d386166302d333630633439666462613331") Reykjavik }
Does anyone know why?
I was following the example on https://gist.github.com/border/3489566
By default, the bson codec uses the lowercased field name as the key. Use the field tag to specify a different key:
type City struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Name string
InitialDiseaseColouring string `bson:"initialDiseaseColouring"`
}
The addition of the field tag changes the key from "initialdiseasecolouring" to "initialDiseaseColouring".