Suggest users based on common interest - MySql query - mysql

Basically what i am trying to do is to suggest people based on common interests.
I have a table of Users(id, username, firstname, lastname, etc)
I have a table of Interested_People where UserID + Interested_in is stored.
I have a table of Contactlist where people who are added with each other is stored.(user1, user2, accepted [1,0])
What I want is to select * users table who are not my friend and they have same interest with me as well.
I searched a lot in internet but couldn't find something like so.
Here i do have created a query and it does exactly what I want. But it is very slow. Even it takes 16 to 20 second to output in PHPMyAdmin in my local machine. Now I Kindly request you guys if you can edit my query a bit and make it bandwidth & time efficient.
SELECT *
FROM users
WHERE id IN(SELECT userid
FROM interested_people
WHERE interested_in IN(SELECT interested_in
FROM interested_people
WHERE userid = [userid])
AND id NOT IN(SELECT user1 AS my_friends_userid
FROM contactlist f
WHERE f.user2 = [userid]
AND accepted = 1
UNION
SELECT user2 AS my_friends_userid
FROM contactlist f
WHERE f.user1 = [userid]
AND accepted = 1))
AND id != [userid]
ORDER BY Rand ()
LIMIT 0, 10;
[Userid] in this query is the ID of the user who is online. Like if i m online my ID will be 1.
This query suggest 10 random users who are not my friends and have same interests as me. But very slooow.
Thanks in advance!

Your problem suggests a self-join to get the users with common interests. Then, not exists to avoid the contact list. The following gets the list of users with common interests, ordered by the number of common interests:
select ip2.userid, count(*) as numInCommon
from interested_People ipme join
interested_People ip2
on ipme.interested_in = ip2.interested_in and
ipme.userid = $UserId and -- Your user id goes here
ip2.userid <> ipme.userid
where not exists (select 1
from contactlist cl
where cl.user1 = ipme.userid and cl.user2 = ip2.userid and
cl.accepted = 1
) and
not exists (select 1
from contactlist cl
where cl.user1 = ip2.userid and cl.user2 = ipme.userid and
cl.accepted = 1
)
group by ip2.userid;

Related

Mysql query to select if a user's friends list is the logged in user's friend

I am trying to get the friend list of a user profile. That's easy because of the schema I did.
Here is the table I did and demo data http://sqlfiddle.com/#!9/f78cf7/2
If I want to get the friend of user id 1, I would run select * from friends where user_id = 1 and I will get friends of this user. This is will return only first row with friend_id = 21.
Now how can I know if user id 30 is also a friend with id 21 in same query? How can I get the status column in same query?
Thank you.
You can try the below way -
DEMO
select f.user_id,f.friend_id,f.status
from friends f join friends f1 on f.id=f1.id+1
where f.status='Friend' and f1.status='Friend'
You can search both sides. For example:
select *
from friends where user_id = 30 and friend_id = 21 and status = 'Friend'
or user_id = 21 and friend_id = 30 and status = 'Friend'

Two tables and join and left join together

I have a users table and user_followings table. The tables have the basic structure:
users: id, name, email
users_followings: following_user_id, follower_user_id
follower_user_id is someone who is following some other person.
following_user_id is someone who is being followed by some other
person
I want that one can click on a particular user to see all the information like who are following him/her and who are the people that he/she is follwing.
SELECT
users.id,
users.name,
users.email
from users
JOIN user_followings ON
user_followings.follower_user_id = users.id
WHERE user_followings.following_user_id = 1
This query basically joins two table and fetches desired result.
Now suppose a user named 'A' is logged in and he is looking at user X's profile. There are many people who have followed user X.
Let's say John, Mike, Rusev, Jack etc
How can write a query that tells whether logged in User 'A' is following John, Mike, Rusev, Jack etc or not along with the query that is above there.
So user A should be able to know whether he is following John, Mike, Rusev, Jack etc or not
My understanding is that OP wants to see what users are following the current user (A) that also follows the user A is viewing (X)
In my example A is id = 1 and X is id = 6
SELECT fu.id, fu.name, fu.email
FROM users u
JOIN users_followings f ON f.userId = u.id
JOIN users fu on fu.id = f.follower
WHERE f.userId = 1
AND f.follower IN (SELECT follower
FROM users_followings
WHERE userId = 6)
I changed follower_user_id to follower and following_user_id to userId to not confuse myself
Supposed the user with id=1 is viewing the details of the user with id=2 and you want to the user with id=1 to know if the followings or followers of user with id=2 are related with user with id=1 in any way. Try this:
SELECT C.*,
(SELECT 1 FROM user_followings D WHERE D.following_user_id=1 AND
C.id=D.follower_user_id LIMIT 1) flwx_viewing_user,
(SELECT 1 FROM user_followings E WHERE E.follower_user_id=1 AND
C.id=E.following_user_id LIMIT 1) viewing_user_flwx
FROM
(SELECT A.id, A.name, A.email, 'following' relation
FROM users
WHERE EXIST (SELECT 1
FROM user_followings B
WHERE B.following_user_id=2)
UNION ALL
SELECT A.id, A.name, A.email, 'followers' relation
FROM users
WHERE EXIST (SELECT 1
FROM user_followings B
WHERE B.follower_user_id=2)) C;
I'm not sure I get it right but given ID=1 for A and ID=5 for X.
This query returns for every user that follows X the info if it is followed by A
SELECT
*,
CASE WHEN exists(
SELECT *
FROM following AFOLLOW
WHERE AFOLLOW.follower_user_id = 1
AND XFOLLOWED.follower_user_id = AFOLLOW.following_user_id)
THEN 'FOLLOWING'
ELSE 'NOTFOLLOWING' END
FROM following XFOLLOWED
WHERE following_user_id = 5
AND follower_user_id <> 1;

Optimisation of subqueries

I have a relation between users and groups. Users can be in a group or not.
EDIT : Added some stuff to the model to make it more convenient.
Let's say I have a rule to add users in a group considering it has a specific town, and a custom metadata like age 18).
Curently, I do that to know which users I have to add in the group of the people living in Paris who are 18:
SELECT user.id AS 'id'
FROM user
LEFT JOIN
(
SELECT user_id
FROM user_has_role_group
WHERE role_group_id = 1 -- Group for Paris
)
AS T1
ON user.id = T1.user_id
WHERE
(
user.town = 'Paris' AND JSON_EXTRACT('custom_metadata', '$.age') = 18
)
AND T1.user_id IS NULL
It works & gives me the IDs of the users to insert in group.
But when I have 50 groups to proceed, like for 50 town or various ages, it forces me to do 50 requests, it's very slow and not efficient for my Database.
How could I generate a result for each group ?
Something like :
role_group_id user_to_add
1 1
1 2
2 1
2 3
The only way I know to do that for now is to do an UNION on several sub queries like the one above, but of course it's very slow.
Note that the custom_metadata field is a user defined field. I can't create specific columns or tables.
Thanks a lot for your help.
if I good understood you:
select user.id, grp.id
from user, role_group grp
where (user.id, grp.id) not in (select user_id, role_group_id from user_has_role_group) and user.town in ('Paris', 'Warsav')
that code give list of users and group which they not belong from one of towns..
To add the missing entries to user_has_role_group, you might want to have some mapping between those town names and their group_id's.
The example below is just using a subquery with unions for that.
But you could replace that with a select from a table.
Maybe even from role_group, if those names correlate with the user town names.
insert into user_has_role_group (user_id, group_id)
select u.user_id, g.group_id
from user u
join (
select 'Paris' as name, 1 as group_id union all
select 'Rome', 2
-- add more towns here
) g on (u.town = g.name)
left join user_has_role_group ug
on (ug.user_id = u.user_id and ug.role_group_id = g.group_id)
where u.town in ('Paris','Rome') -- add more towns here
and json_extract(u.custom_metadata, '$.age') = 18
and ug.id is null;

mysql with subquery on same table

i have a single mysql table with at minimum userId, wager.
Data can be
userId = 1 wager = 10.00, userId = 2 wager = 5.00, userId = 3 wager = 1.00.
I want to run a select with a userId param that returns all userId's that are <= the wager for that user
So if userId = 2 I get back all other users with a wager of 5.00 or less.
Query does a lot more than this with one join but just need some help or best approach with the above
You can do it with a self-join:
SELECT u1.*
FROM user AS u1
INNER JOIN user AS u2 ON u1.wager <= u2.wager
WHERE u2.userId = ?
SELECT userA.*
FROM user AS userA,user AS userB
WHERE userA.wager <= userB.wager
AND userA.userId=userB.userId
AND userB.userId = 2
It's a good question and hope it helps you. I think most people forgot to link the userId together and it may need to link the userId together.

Cakephp reciprocal friendship system

I've been trying to figure out for a week or so, how I can make a proper friendship system in CakePHP. I've read this and this thread but I can't get it to work.
I've read a lot more threads regarding this, but nobody seems to have a proper example.
I currently have a table users (id, username, password, e-mail etc.) and a table friendships (id, user_to, user_from, status).
Step 1 - Friendship request
If a user does a friendship request, then a row is inserted with the requesting user_id and the user_id of the user from whom the friendship is request, so it could look like:
id | user_from | user_to| status
1 | 1 | 2 | 0
This way I can easily show pending friends of user_id = 2, by selecting all records where user_to = 2
Step 2 - Confirm friendship
I've set it up so that user_id 2 now sees that user_id 1 wants to become friends, if he clicks the confirmation link, the status will be changed to 1, see below
id | user_from | user_to| status
1 | 1 | 2 | 1
I created all kinds of checks so the row stays unique.
Step 3 - Show friends
I thought this would be easy, if I want to show the friends of user_id = 1 then I just do a select with user_from = 1 OR user_to = 1, however this doesn't work.
User_id 1 can be a requester but can also be requested, so a JOIN will show strange results.
Does anyone know a solution? I'm happy to rebuild the entire system if I'm not doing the entire thing right! Any hints in the right direction are welcome as well...
Here is my solution : the difficulty lies in the correct request because friend requests can be crossed (if A asks B or B asks A will be stored the opposite way in the "to" and "from" fields of the table). Lets do it like that and user UNION and aliases to get friends from any user independently of the relation table bellow.
The [friends] table (relation): to|from|statut(pending,confirmed)
"to" and "from" > foreign_keys constraint to a [users] table
The request below always gives the wanted results ! (replace %d by the user ID or user Id in the SESSION
SELECT
users.userNickname,
friends.to AS friendUser,
friends.from AS currentUser,
friends.statut
FROM
users
INNER JOIN
friends
ON
users.userId = friends.to
WHERE
friends.from = '%d'
UNION
SELECT
users.userNickname,
friends.from AS friendUser,
friends.to AS currentUser,
friends.statut
FROM
users
INNER JOIN
friends
ON
users.userId = friends.from
WHERE
friends.to = '%d'
You can find friend requests to ID = 1 this way:
select * from Users u1 where u1.user_to = 1 and u1.user_from not in (select u2.user_to
from Users u2 where u2.user_from = u1.user_to)
You can find friend requests from ID = 1 this way:
select * from Users u1 where u1.user_from = 1 and u1.user_to not in (select u2.user_from
from Users u2 where u2.user_to = u1.user_from)
You can find mutual friendships of ID = 1 this way:
select * from Users u1 where ((u1.from = 1) or (u1.to = 1)) and 0 < (select count(*) from
Users u2 where u1.from = u2.to and u1.to = u2.from)
This code was not tested, but you get the idea.