I'm currently working on a small application that allows users to rate certain objects, my database (MongoDB) structure looks like this
Movie {
Id int
Name string
}
Actor {
Id int
Name string
Age int
}
Movie_Actors {
Movie Movie
Actor Actor
}
User {
Id int
Username string
Password string
}
Rating {
Id int
User User
Actor Actor
Rating int
}
My problem begins when I want to select all Actors in a Movie where there are less than 5 Ratings
// db *mgo.Database
c := db.C('ratings')
err := c.Find(...)
For this problem, I would denormalize your schema a little to make querying in MongoDB more natural. Looking only at the types you need for this query, you might use the following:
type Movie struct {
Id bson.ObjectId `_id,omitempty`
Name string
Actors []bson.ObjectId
}
type Actor struct {
Id bson.ObjectId `_id,omitempty`
Name string
Age int
Ratings []Rating
NRatings int
}
Here the Movie directly embeds the Actor IDs, and the Actor keeps track of the number of ratings in a separate field from the ratings themselves. If you only wanted to run this single type of query, you could fully denormalize the data model by embedding the Movies directly into the Actors who played in them. This would simplify the query even more, but would also create a lot of duplication of information and prevent you from dealing with Movies as separate entities.
See the documentation on schema design for a discussion on these tradeoffs.
In this design, you can find the actors in the movie Fantastic Mr. Fox with fewer than 5 ratings using the following:
// Find the actors in the movie
var m Movie
db.C("movies").Find(bson.M{"name": "Fantastic Mr. Fox"}).One(&m)
// Filter the actors to find those with less than 5 ratings.
var results []Actor
db.C("actors").Find(bson.M{"_id": bson.M{"$in": m.Actors}, "nratings": bson.M{"$lt": 5}}).All(&results)
fmt.Println(results)
Related
We currently have the following schema;
type Reactions []*Reaction
type Post struct {
ID string `pg:",pk" json:",omitempty"`
CreatorID string `pg:",notnull"`
Creator *User `pg:",rel:has-one,fk:creator_id"`
Groups Groups
Reactions Reactions `pg:",rel:has-many" json:",omitempty"`
}
type Reaction struct {
ID string `pg:",pk" json:",omitempty"`
Type ReactionType `pg:",notnull"`
CreatorID string `pg:",notnull"`
Creator *User `pg:",rel:has-one,fk:creator_id"`
PostID string `pg:",notnull"`
Post *Post `pg:",rel:has-one,fk:post_id"`
}
When trying to query all Posts including their Reaction Relations using the following query, we recieve the following error message; pg: relation=\"Reactions\" does not have base model=Post with id=\"\" (check join conditions)
func (pm PGPostRepo) selectQuery(model ...interface{}) *orm.Query {
return pm.db.Model(model...).
Relation("Creator.id").
Relation("Creator.given_name").
Relation("Creator.family_name").
Relation("Reactions.type").
Column("post.*")
Where("post.id = ?", postID).
Select()
}
I'm actually quite lost on this one, as if we replace
Relation("Reaction.type") with Relation("Reaction.*") we do not get the error(Though both Creator & Post are null), but then we're retrieving more columns than we'd like.
I am not a pro but isn't it the Reactions in Post model. It should be Reactions Reaction ... instead of Reactions Reactions .... Because the model is Reaction, not Reactions. I hope it solves the problem and i am not a stupid.
I have the following prisma schema
model User {
id Int #id #default(autoincrement())
userName string #unique
complaints Complaint[]
}
model Complaint {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
creationTime DateTime #default(now())
userId Int
priority ComplaintPriority
}
enum ComplaintPriority {
HIGH
MEDIUM
LOW
}
I need to select the users that have last complaint (as last I mean the complaint with latest creationTime) with priority value HIGH.
In other words:
if an user has 3 complaints and the last of these complaints has high priority the user should be part of the result (ignoring previous complaints)
If an user has 8 complaints (maybe some of those with high priority) and the last one has low priority the user should not be part of the results
If the user has no complaints at all the user should not be part of the results
I didn't find the prisma syntax for this operation. Does anybody has any idea how to do it?
I looked into this a bit, unfortunately, I don't think there's a way to create a query exactly as you have in mind (as of version 3.5.0 of Prisma).
Here's a workaround that you could perhaps consider:
Fetch all user records that have at least one complaint with HIGH priority. Include complaint records and order them by creationTime.
Manually filter through the list in Node.js to keep appropriate user records.
let users = await prisma.user.findMany({
where: {
complaints: {
some: {
priority: "HIGH"
}
}
},
include: {
complaints: {
orderBy: {
creationTime: "desc"
}
}
}
})
users = users.filter(user => user.complaints[0].priority == "HIGH")
It should be noted though, this isn't perfectly optimal if there are really high number of user records. In such a case, I would consider creating a raw SQL query using rawQuery.
I have been reading a nice article about how to manage data type or model with id, sometime we need the same model with id and the same model without id and the following
article describe how to model types to handle this issue.
the below example is written in Scala and I want its Haskell representation for better understanding
case class WithId[A](id: Id, model: A)
// receive data for a new user from the client
val user: User = Json.parse[User](json)
// receive data from the database
val user: WithId[User] = UserService.findByIdOrFail(userId)
what matters for me is the type respresenation, suppose we have the function findByIdOrFail and parse ...
data WithId a = W { id :: Id, model :: a }
-- Receive data for a new user from the client
userId = parse json
-- Receive data from the database
user = findByIdOrFail userId
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.
I've got 2 structs to represent a ManyToMany relationship. User and Note
type User struct {
ID int
Name string
Notes []*Note
}
type Note struct {
TableName struct{} `sql:"user_notes"`
ID int
Text string
}
Now let's say I want to insert a new user and also at the same time add a few notes.
I would expect this to insert a user and its note(s):
note := Note{
Text: "alohaa dude",
}
user := User{
Name: "peter",
Notes: []Note{no},
}
s.DB.Insert(&user)
However this only saves the user and not the user and the note. In go-pg do I have to do this manually or is there an automated way through the ORM?
Rodrigo, same problem statement is being discussed here: https://github.com/go-pg/pg/issues/478
This functionality is not supported in go-pg at this time and you might want to try a db prepare approach to insert with relationships.