Disclaimer: this is my first time using an ORM, and my SQL might be a bit rusty.
I have an application with users, songs, and I want to count and display how many times each song has been played by a given user. I know how to do it with SQL, but I'm failing at translating this to GORM. It goes like this:
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type User struct {
ID int `gorm:"primary_key"`
Listened []Listen
}
type Song struct {
ID int `gorm:"primary_key"`
Title string
Listened []Listen
}
type Listen struct {
UserID int `gorm:"primary_key;auto_increment:false"`
SongID int `gorm:"primary_key;auto_increment:false"`
Count int
}
Then, let's add some data
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic(err)
}
defer db.Close()
db.DropTableIfExists(&User{}, &Song{}, &Listen{})
db.CreateTable(&User{}, &Song{}, &Listen{})
db.Save(&User{ID: 1})
db.Save(&User{ID: 2})
db.Save(&Song{ID: 1, Title: "oh yeah"})
db.Save(&Song{ID: 2, Title: "greatest song"})
db.Save(&Song{ID: 3, Title: "bestest song"})
db.Save(&Listen{UserID: 1, SongID: 1, Count: 7})
db.Save(&Listen{UserID: 1, SongID: 2, Count: 23})
db.Save(&Listen{UserID: 2, SongID: 2, Count: 13})
db.Save(&Listen{UserID: 2, SongID: 3, Count: 10})
// do something here
}
Now, as I said I want to list how many times a user has played each song of the database. After scratching my head a little I came to this:
sqlite> select songs.id, l.count from songs
...> left join (select song_id, count from listens where user_id = 1) as l
...> on songs.id = l.song_id;
id count
---------- ----------
1 7
2 23
3
... and here I am. I just don't understand how I can express that with GORM method chains. Or should I just give up and run a raw sql query?
In the end I found how to do it:
db.Table("songs").
Select("songs.id, listens.count").
Joins("left join (select * from listens where user_id = ?) as listens on songs.id = listens.song_id", 1).
Find(&rs)
This query probably will work also. I hope it's helpful.
if err := db.Table("listens").Select("songs.id, listens.count").
Joins("JOIN songs on listens.song_id = songs.id").Where("user_id = ?",
1).Find(&rs).Error; err != nil {
fmt.Prinln(err)
}
Related
I have method in my product service like the one below. I'm filtering the data and using nestjs-typeorm-paginate to paginate, but it doesn't work properly (with a page size of 10 it returns 4-5 records depending on the number of related rooms. Without using room relation everything works great)
async findAllPaginated(
options: IPaginationOptions,
filters?: any,
): Promise<any> {
const queryBuilder = this.productRepository.createQueryBuilder('product');
queryBuilder.leftJoinAndSelect('product.category', 'category');
/* Room uses many to many relation */
queryBuilder.leftJoinAndSelect('product.room', 'room');
if (filters.category) {
queryBuilder.andWhere(`category.id like '${filters.category}'`);
}
if (filters.room) {
queryBuilder.andWhere(`room.id like '${filters.room}'`);
}
if (filters.priceMin) {
queryBuilder.andWhere(`product.price >= ${filters.priceMin}`);
}
if (filters.priceMax) {
queryBuilder.andWhere(`product.price <= ${filters.priceMax}`);
}
if (filters.promoPriceMin) {
queryBuilder.andWhere(`product.promoPrice >= ${filters.promoPriceMin}`);
}
if (filters.promoPriceMax) {
queryBuilder.andWhere(`product.promoPrice <= ${filters.promoPriceMax}`);
}
if (filters.sortField) {
queryBuilder.orderBy(filters.sortField, filters.sortDirection);
}
return paginate<Product>(queryBuilder, options);
}
Generated SQL looks like this:
SELECT
`product`.`id` AS `product_id`,
`product`.`shortenUrl` AS `product_shortenUrl`,
`product`.`name` AS `product_name`,
`product`.`price` AS `product_price`,
`product`.`promoPrice` AS `product_promoPrice`,
`product`.`deliveryCost` AS `product_deliveryCost`,
`product`.`promoEndDate` AS `product_promoEndDate`,
`product`.`description` AS `product_description`,
`product`.`amount` AS `product_amount`,
`product`.`photo` AS `product_photo`,
`product`.`width` AS `product_width`,
`product`.`height` AS `product_height`,
`product`.`depth` AS `product_depth`,
`product`.`colorCode` AS `product_colorCode`,
`product`.`created` AS `product_created`,
`product`.`updated` AS `product_updated`,
`product`.`deletedAt` AS `product_deletedAt`,
`product`.`categoryId` AS `product_categoryId`,
`product`.`ordersId` AS `product_ordersId`,
`category`.`id` AS `category_id`,
`category`.`name` AS `category_name`,
`category`.`icon` AS `category_icon`,
`category`.`created` AS `category_created`,
`category`.`updated` AS `category_updated`,
`category`.`deletedAt` AS `category_deletedAt`,
`category`.`groupId` AS `category_groupId`,
`room`.`id` AS `room_id`,
`room`.`shortenUrl` AS `room_shortenUrl`,
`room`.`name` AS `room_name`,
`room`.`icon` AS `room_icon`,
`room`.`created` AS `room_created`,
`room`.`updated` AS `room_updated`,
`room`.`deletedAt` AS `room_deletedAt`
FROM
`product` `product`
LEFT JOIN `category` `category` ON
`category`.`id` = `product`.`categoryId` AND(`category`.`deletedAt` IS NULL)
LEFT JOIN `room_products_product` `room_product` ON
`room_product`.`productId` = `product`.`id`
LEFT JOIN `room` `room` ON
`room`.`id` = `room_product`.`roomId` AND(`room`.`deletedAt` IS NULL)
WHERE
`product`.`deletedAt` IS NULL
ORDER BY
`product`.`name` ASC
And it returns duplicated data with diffrent rooms data:
The data returned to the client looks fine, with no duplicates with the correct link to the rooms, but with incorrect pagination.
How can I paginate correctly in this case? Is this at all possible?
I would appreciate any help :)
This is the data I have in the jobs table
id, title, requirements
1, software engineer, { qualifications: ["masters", "bachelors"], skills: ["react", "rails"] }
2, product engineer, { qualifications: ["masters", "associate"], skills: ["trello", "excel"] }
Let's say I have candidates table:
id, name, resume
1, Mark, { qualifications: "masters", skills: ["react", "rails"] }
2, Temeka, { qualifications: "associate", skills: ["powerpoint", "excel"] }
I'm building a query for Mark that will return the jobs that he does NOT have the requirements for, like this:
where_clause = { qualifications: "masters", skills: ["react", "rails"] }.map do |key, value|
<<~SQL
(
NOT jobs.requirements #> '{"#{key}": ["#{value}"]}'
OR
NOT jobs.requirements #> '{"#{key}": "#{value}"}'
)
SQL
end
I'm doing the above two checks because value can either be an array ["react", "rails"] or it can be just a string masters.
It doesn't seem to work. Mark is getting disqualified for job#1 for which has the requirements. What am I doing wrong here?
I believe this is primarily a POSTGRESQL issue I'm having, so don't worry about the ruby code here.
To find all jobs that Mark is not qualified for, you can use this:
select *
from jobs j
where not exists (select *
from candidates c
where c.name = 'Mark'
and (j.requirements -> 'qualifications') #> (c.resume -> 'qualifications')
and (j.requirements -> 'skills') #> (c.resume -> 'skills'))
Online example
If you want to hardcode the parameters rather then taking them from the table directly, you can do it like this:
select *
from jobs j
where not ( j.requirements -> 'qualifications' #> '["masters"]'
and j.requirements -> 'skills' #> '["react", "rails"]')
My scenario is i have a grid with search option where user can select the column and can do the search, the grid data is coming from various tables. I have attached a sample screen of grid.
User Screen
So i'm trying to create a dynamic query for search but the problem is i can able to search only in main table (schema.Robot) not in Preload tables. whenever i trying to search data data from Preload tables let say from RobotModel table that time getting below error
pq: missing FROM-clause entry for table "robot_models"
Here is my go code
func (r *RobotsRepository) GetRobotsSummary(listParams viewmodel.ListParams, companyID uint) ([]*schema.Robot, int, error) {
mrobots := []*schema.Robot{}
var count int
var order string
if listParams.SortColumn == "" {
listParams.SortColumn = "id"
listParams.SortOrder = 1
} else {
listParams.SortColumn = util.Underscore(listParams.SortColumn)
}
if listParams.SortOrder == 0 {
order = "ASC"
} else {
order = "DESC"
}
var searchQuery string
if listParams.SearchText != "" {
switch listParams.SearchColumn {
case "Robot":
listParams.SearchColumn = "name"
case "Model":
listParams.SearchColumn = "robot_models.name"
}
searchQuery = listParams.SearchColumn +" LIKE '%"+ listParams.SearchText +"%' and Company_ID = " + fmt.Sprint(companyID)
}else{
searchQuery = "Company_ID = " + fmt.Sprint(companyID)
}
orderBy := fmt.Sprintf("%s %s", listParams.SortColumn, order)
err := r.Conn.
Preload("RobotModel", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Name")
}).
Preload("Task", func(db *gorm.DB) *gorm.DB {
return db.Where("Task_Status in ('In-Progress','Pending')").Select("ID, Task_Status")
}).
Preload("CreatedUser", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Display_Name")
}).
Preload("UpdatedUser", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Display_Name")
}).
Where(searchQuery).
Order(orderBy).
Offset(listParams.PageSize * (listParams.PageNo - 1)).
Limit(listParams.PageSize).
Find(&mrobots).Error
r.Conn.Model(&schema.Robot{}).Where(searchQuery).Count(&count)
return mrobots, count, err
}
In searchQuery variable i'm storing my dynamic query.
My question is how can i search data for preload table columns
Here is the sql query which i'm trying to achieve using gorm
SELECT robots.id,robots.name,robot_models.name as
model_name,count(tasks.task_status) as task_on_hand,
robots.updated_at,users.user_name as updated_by
FROM rfm.robots as robots
left join rfm.tasks as tasks on tasks.robot_id = robots.id and
tasks.task_status in ('In-Progress','Pending')
left join rfm.robot_models as robot_models on robot_models.id =
robots.robot_model_id
left join rfm.users as users on users.id = robots.updated_by
WHERE robot_models.name::varchar like '%RNR%' and robots.deleted_at is null
GROUP BY robots.id,robot_models.name,users.user_name
ORDER BY task_on_hand DESC LIMIT 2 OFFSET 0
and sorry for bad English!
Even though you are preloading, you are still required to explicitly use joins when filtering and ordering on columns on other tables. Preloading is used to eagerly load the data to map into your models, not to join tables.
Chain on something like this:
.Joins("LEFT JOIN rfm.robot_models AS robot_models ON robot_models.id = robots.robot_model_id")
I'm not positive if you can use the AS keyword using this technique, but if not, it should be easy enough to adjust your query accordingly.
I have a model structure as following:
Group -> Many Parties -> Many Participants
In on of the API calls I need to get single groups with parties and it's participants attached.
This whole structure is built on 4 tables:
group
party
party_participant
participant
Naturally, with SQL it's a pretty straight forward join that combines all of them. And this is exactly what I am trying to do with slick.
Mu method is dao class looks something like this:
def findOneByKeyAndAccountIdWithPartiesAndParticipants(key: UUID, accountId: Int): Future[Option[JourneyGroup]] = {
val joins = JourneyGroups.groups join
Parties.parties on (_.id === _.journeyGroupId) joinLeft
PartiesParticipants.relations on (_._2.id === _.partyId) joinLeft
Participants.participants on (_._2.map(_.participantId) === _.id)
val query = joins.filter(_._1._1._1.accountId === accountId).filter(_._1._1._1.key === key)
val q = for {
(((journeyGroup, party), partyParticipant), participant) <- query
} yield (journeyGroup, party, participant)
val result = db.run(q.result)
result ????
}
The problem here, is that the result is type of Future[Seq[(JourneyGroup, Party, Participant)]]
However, what I really need is Future[Option[JourneyGroup]]
Note: case classes of JourneyGroup and Party have sequences for there children defined:
case class Party(id: Option[Int] = None,
partyType: Parties.Type.Value,
journeyGroupId: Int,
accountId: Int,
participants: Seq[Participant] = Seq.empty[Participant])
and
case class JourneyGroup(id: Option[Int] = None,
key: UUID,
name: String,
data: Option[JsValue],
accountId: Int,
parties: Seq[Party] = Seq.empty[Party])
So they both can hold the descendants.
What is the correct way to convert to the result I need? Or am I completely in a wrong direction?
Also, is this statement is correct:
Participants.participants on (_._2.map(_.participantId) === _.id) ?
I ended up doing something like this:
journeyGroupDao.findOneByKeyAndAccountIdWithPartiesAndParticipants(key, account.id.get) map { data =>
val groupedByJourneyGroup = data.groupBy(_._1)
groupedByJourneyGroup.map { case (group, rows) =>
val parties = rows.map(_._2).distinct map { party =>
val participants = rows.filter(r => r._2.id == party.id).flatMap(_._3)
party.copy(participants = participants)
}
group.copy(parties = parties)
}.headOption
}
where DAO method's signature is:
def findOneByKeyAndAccountIdWithPartiesAndParticipants(key: UUID, accountId: Int): Future[Seq[(JourneyGroup, Party, Option[Participant])]]
In RavenDB
I need to get the latest insert for a document based on it's ID and filter by IDs from a list
ie:
List<Entity> GetLastByIds(List<int> ids);
The Entity is similar to:
class Entity
{
int id; //Unique identifier for the category the price applies to.
int Price; //Changes with time
DateTime Timestamp; //DateTime.UtcNow
}
so if I insert the following:
session.Store(new Entity{Id = 1, Price = 12.5});
session.Store(new Entity{Id = 1, Price = 7.2});
session.Store(new Entity{Id = 1, Price = 10.3});
session.Store(new Entity{Id = 2, Price = 50});
session.Store(new Entity{Id = 3, Price = 34});
...
How can I get the latest price for IDs 1 and 3 ??
I have the Map/Reduce working fine, giving me the latest for each ID, it's the filtering that I am struggling with.
I'd like to do the filtering in Raven, because if there are over 1024 price-points in total for all IDs, doing filtering on the client side is useless.
Much appreciate any help I can get.
Thank you very much in advance :)
If the Id is supposed to represent the category, then you should call it CategoryId. By calling the property Id, you are picking up on Raven's convention that it should be treated as the primary key for that document. You can't save multiple versions of the same document. It will just overwrite the last version.
Assuming you've built your index correctly, you would just query it like so:
using Raven.Client.Linq;
...
var categoryIds = new[] {1, 3}; // whatever
var results = session.Query<Entity, YourIndex>()
.Where(x=> x.CategoryId.In(categoryIds));
(The .In extension method is in the Raven.Client.Linq namespace.)