Kohana 3 ORM: Getting most repeated values, ranked, and inserting into new object / array - orm

So, another in my series of Kohana 3 ORM questions :)
I have, essentially, a pivot table, called connections. The connections table connects a song to a keyword. That's all great and working (thanks to my last two questions!)
I want to output the most connected songs by keyword. So, to somehow query my connections table and output an object (with an arbitrarily limited number of iterations $n) that ranks songs by the number of times they have been connected, ie. the number of times that particular song_id appears for that particular keyword_id.
I have literally no idea how to achieve this, without querying every single row (!!!) and then counting those individual results in an array.... There must be a more elegant way to achieve this?

I believe this is more of an SQL question. Using the DB query builder:
DB::select('songs.*')->select(array('COUNT("keywords.id")', 'nconnections'))
->from('songs')
->join('connections', 'LEFT')->on('connections.song_id', '=', 'songs.id')
->join('keywords', 'LEFT')->on('connections.keyword_id', '=', 'keywords.id')
->group_by('songs.id')
->order_by('nconnections')
->as_object('Model_Song')
->execute();
or in SQL
SELECT `songs`.*, COUNT(`keywords`.`id`) AS `nconnections` FROM songs
LEFT JOIN `connections` ON `connections`.`song_id` = `songs`.`id`
LEFT JOIN `keywords` ON `connections`.`keyword_id` = `keywords`.`id`
GROUP BY `songs`.`id` ORDER BY `nconnections`
should return the result you want.
You'll want to have an accessible property called nconnections in your song model. The simplest way to do that is to add a public member so you don't tamper with ORM's inner workings.
I'm assuming you're using a model called 'Song', linked to a 'songs' table, a 'Keyword' model linked to a 'keywords' table and in the 'connections' table foreign keys 'song_id' and 'keyword_id' for each model respectively.

Related

Triple relation QueryBuilder symfony (what the hell am I doing?)

Let me explain how I am making things (maybe not the best way by the way).
I want to join my StoreSchedule entity (which contains a triple relation : store (which is where I stock all informations about adress, name, picture of my stores), days (the 7 days of the week) and Schedules (only strings like '09:00-22:00').
To combine thoses 3 entities, I made StoreSchedule, that has a triple relation where I cross the 3 informations. Maybe I am not explaining well, let me show you some screens.
What I already tried to do with my QueryBuilder in my repository:
https://imgur.com/a/bE52iBH
How I structured things in my StoreSchedule table :
https://imgur.com/a/6m7YkhI
My Schedule table :
https://imgur.com/a/75bRriS
Days table contains the 7 days of the week. Maybe that's obvious, maybe not.
So here's the thing, I can't figure out how to make the I need : a query that gets all content from Store and joins StoreSchedule where ID = Store.ID
I want to get days and Schedule with the ID from store.
Can I do that with query builder?
Do I have to modifiy my database? Are my relations good?
Best regards!
ps : hope I made myself clear enough..
You make it too complicated. You can purge the days table AND StoreSchedule. Just make a many-to-one relation between Store and (Store)Schedule. Add a column "day" in your schedule table. A short integer will do if you store the number of the day. See php's date function.
For your query (the first link) you won't need that WHERE clause. Doctrine will join the related data automatically for you.
public function getAllContentStoreAndSchedule()
{
return $this->createQueryBuilder('st')
->leftJoin('st.schedule', 'sc')
->addSelect('sc')
->orderBy('st.name', 'ASC')
->addOrderBy('sc.day', 'ASC')
->getQuery()
;
}

Active Record where join table record doesn't exist

I am trying to get a list of all records that don't exist in a join table.
The models are User, Game and MarkedGame, where users can mark games as played. It's a many to many relationship:
User > MarkedGame < Game
What I want is a list of all games that haven't been marked by the user.
I know that I could do two separate queries and subtract them:
Game.all - current_user.games
But I don't like that this leaves me with an array rather than an Active Record relation object. Plus it seems like there should be a more performant way of doing it.
If there is no Active Record way of handling this, is there perhaps a SQL way? My raw SQL is not particularly strong so any help on that would be appreciated.
Thanks.
You can try the following:
Game.where.not(id: MarkedGame.where(user_id: current_user.id).pluck(:game_id))
That should do it. Returns all games that have not been marked by the current user.
Game.where('id not in (select game_id from marked_games where user_id = ?)', current_user.id)

SQL complicated query with joins

I have problem with one query.
Let me explain what I want:
For the sake of bravity let's say that I have three tables:
-Offers
-Ratings
-Users
Now what I want to do is to create SQL query:
I want Offers to be listed with all its fields and additional temporary column that IS NOT storred anywhere called AverageUserScore.
This AverageUserScore is product of grabbing all offers, belonging to particular user and then grabbing all ratings belonging to these offers and then evaluating those ratings average - this average score is AverageUserScore.
To explain it even further, I need this query for Ruby on Rails application. In the browser inside application you can see all offers of other users , with AverageUserScore at the very end, as the last column.
Associations:
Offer has many ratings
Offer belongs to user
Rating belongs to offer
User has many offers
Assumptions made:
You actually have a numeric column (of any type that SQL's AVG is fine with) in your Rating model. I'm using a column ratings.rating in my examples.
AverageUserScore is unconventional, so average_user_score is better.
You don't mind not getting users that have no offers: average rating is not clearly defined for them anyway.
You don't deviate from Rails' conventions far enough to have a primary key other than id.
Displaying offers for each user is a straightforward task: in a loop of #users.each do |user|, you can do user.offers.each do |offer| and be set. The only problem here is that it will execute a separate query for every user. Not good.
The "fetching offers" part is a standard N+1 counter seen even in the guides.
#users = User.includes(:offers).all
The interesting part here is only getting the averages.
For that I'm going to use Arel. It's already part of Rails, ActiveRecord is built on top of it, so you don't need to install anything extra.
You should be able to do a join like this:
User.joins(offers: :ratings)
And this won't get you anything interesting (apart from filtering users that have no offers). Inside though, you'll get a huge set of every rating joined with its corresponding offer and that offer's user. Since we're taking averages per-user we need to group by users.id, effectively making one entry per one users.id value. That is, one per user. A list of users, yes!
Let's stop for a second and make some assignments to make Arel-related code prettier. In fact, we only need two:
users = User.arel_table
ratings = Rating.arel_table
Okay. So. We need to get a list of users (all fields), and for each user fetch an average value seen on his offers' ratings' rating field. So let's compose these SQL expressions:
# users.*
user_fields = users[Arel.star] # Arel.star is a portable SQL "wildcard"
# AVG(ratings.rating) AS average_user_score
average_user_score = ratings[:rating].average.as('average_user_score')
All set. Ready for the final query:
User.includes(:offers) # N+1 counteraction
.joins(offers: :ratings) # dat join
.select(user_fields, average_user_score) # fields we need
.group(users[:id]) # grouping to only get one row per user

Error in SQL SELECT with INNER JOIN

So, basically, I have two tables called "dadoscatalogo" and "palavras_chave", with a common field, "patrimonio" which is the primary key of "dadoscatalogo".
I'm using a servlet to connect to the database with these tables, and passing a query to search for entries based on some search criteria that's defined by the user.
Now, since the user can search for entries based on information present in both tables, I need to do an INNER JOIN, and then use WHERE to search for that info. I'm also using LIKE, because the user may pass just part of the information, and not all of it.
So, to test it all out, I tried passing it a few parameters to work with, and see how it went. After some debugging, I found out that there was some mistake in the query. But I can't seem to be able to point out exactly what it is.
Here's the test query:
SELECT dadoscatalogo.patrimonio
FROM dadoscatalogo
INNER JOIN palavras_chave
ON dadoscatalogo.patrimonio=palavras_chave.patrimonio
WHERE dadoscatalogo.patrimonio LIKE '%'
AND dadoscatalogo.titulo LIKE '%tons%'
OR palavras_chave.palchave LIKE '%programming%';
So, basically, what I'm trying to do with this query is, get all the primary keys from "dadoscatalogo" that are linked to a record with a "titulo" containing "tons", or a "palchave" containing "programming".
PS. Sorry for the names not being in English, hopefully it won't be too much of a distraction.
EDIT: Right now, the tables don't have much:
This is the dadoscatalogo table:
http://gyazo.com/fdc848da7496cea4ea2bcb6fbe81cb25
And this is the palavras_chave table:
http://gyazo.com/6bb82f844caebe819f380e515b1f504e
When they join, I'm expecting it to have 4 records, and it would get the one with patrimonio=2 in dadoscatalogo (which has "tons" in titulo), and the one with palchave=programming (which would have patrimonio=1)
As per my understanding run below query:
SELECT dadoscatalogo.patrimonio
FROM dadoscatalogo
INNER JOIN palavras_chave
ON dadoscatalogo.patrimonio=palavras_chave.patrimonio
WHERE dadoscatalogo.titulo LIKE '%tons%'
OR palavras_chave.palchave LIKE '%programming%';

Search through Users with dynamic attributes with SQL

.Hi i'm working with Asp and SQL-Server and i have no problem with writing dynamic query
I'm trying to Write a search page for searching people.
I have 3 related tables:
See my table diagram in : http://tinypic.com/r/21159go/5
What i'm trying to do is to design a search page that a person can search users with a dynamic number of attributes.
Example:
think that a username called "User1" has 3 attributes named "Attr1", "Attr2" and "Attr3" related to him in "UserAttributes" table and "User2" has 3 attributes named "Attr1", "Attr2" and "Attr4".
Attribute names and other bunch of items unrelated to search function saved in "Attributes" Table. This is because i want to relate an attribute between multiple users. and their values are stored in "UserAttributes" table.
Well someone wants to search upon "Attr1" and "Attr2" and wants to return all users that have "Attr1" and "Attr2" with specific value.
I need a query to know how to implement this. I can write a dynamic query with asp.net so if someone please give me a query for this one example i have brought, i would be thankful
P.S. This is not my real database. my real database is much more complex and has more fields and tables but i just cut it and brought only necessary items. and because attributes are very dynamic they can't be embedded in table columns.
Thanks in advance
Based on your DB diagram your code would be something like this
update:
SELECT u.*
FROM users AS u
LEFT OUTER JOIN UserAttributes AS ua1
ON u.USER_ID = ua1.USER_ID
LEFT OUTER JOIN UserAttributes AS ua2
ON u.USER_ID = ua2.USER_ID
WHERE (
ua1.attribute_id = 'att1'
AND ua.attribute_value = 'MyValue' )
AND (
ua2.attribute_id = 'att2'
AND ua.attribute_value = 'MyValue2' )
In where clause you would specify the attirbute_Id and what value you are expecting out of it. Than just decide if you want to restrict users to have all values match or just one of them, in that case modify AND between statements to be OR
if you just want to do this quick and dirty you can create your own class library that can create adhoc sql that will pass to the database.
if you want more organized matter, create SP that will bring back users and accepts any left of id and value. Do a lot of that by passing list separated by comma, colon or semicolon. Than split it up in SP and filter results based on those values
there are many other alternatives like EntityFramework, LINQ-to-SQL and other options, just need to figure out what works best for you, how much time you want to spend on it and how easy will it be to support later.