feature: block for "people you might know"
| Project: | User Relationships |
| Version: | 6.x-1.x-dev |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | reviewed & tested by the community |
okay, this is the trend du jour - linkedin has been doing it for a while, facebook just started doing it...basic idea:
User A has friends E, D, H
User B has friend C, F
User E has friends A, B, H, M
User A looks at his friends and sees B E and D, looks at User E and sees friends in common (H and B) but when viewing that same profile (or content created by any 'friend' he also sees a BLOCK that displays 2nd degree connections through "all user A friends" (E, D, H) as "people you might know" (in this case it would include B, M, C, F)
it's simply running all users against %user's friend list, removing existing friends and displaying only those connected by one degree from a current friend (not already on %user friend list)
it's linear social networking, not really dynamic, even though it may feel dynamic.

#1
sorry, that very last line of 3rd paragraph: user A sees a block saying 'people you might know: user B and User M (not C or F, those are one more node out)
#2
Moving this to the more general -dev version.
I think more discussion will need to go in to this feature. For instance, how will relationship types weigh on how users know each other. I'm working on a site that uses UR for two differing relational ideas: friends and subscribers. Just cause someone is a subscriber doesn't mean they know each other in the way that I think you're talking about. So how do we provide the option to either forgo or at least devalue that relationship?
#3
interesting point. perhaps a top level stratification - when a user defines a "relationship type" it is either "intimate" or "removed" - in this case, a group is created like "family" or "friends" and a second level option is to note that the group type is "intimate" - user creates another group called "reviewers" (for a text for example) or "subscibers" (for a periodical, etc) and indicates "removed" (perhaps the wrong term for what i mean, but just not 'intimate' - at which point these other relationships (distal and on the connected nodes, etc) are either pegged to "all" or "intimate only" (by admin)
#4
As I think about this, I wonder if the Facebook approach might be more effective.
Users are given a list of people they may know because X number of their friends know them.
Alice: Bob, Cliff, Daniel
Bob: Alice, Frank, Gabe, Cliff
Cliff: Alice, Frank, Daniel
Daniel: Harold, Alice, Frank
Alice is friends with Bob, Cliff, and Daniel and all three of them are friends with Frank so it's likely Alice also knows Frank. Bob knows Alice and Cliff who both know Daniel, but the admin has required 3 connections before considering a match so the system wont display Daniel as a possible connection to Bob.
Relationship types can then be considered a weight. So if a set of connections are all of the same relationship type the number of needed connections would be lower than if they were all of a different type.
Alice:
Friends: Bob, Cliff
Coworkers: Daniel
Bob:
Friends: Alice, Cliff
Coworkers: Gabe, Frank
Cliff:
Friends: Alice, Bob
Coworkers: Daniel
Daniel:
Friends: Harold
Coworkers: Alice, Cliff, Frank
Here Daniel might know Bob through Alice and Cliff but because they're her Coworkers and Bob is a Friend of Alice and Cliff there's a lower likelihood of that connection than if Daniel was a Friend of Alice and Cliff.
We'd give the admin the ability to tune those weights. They could decided that 4 connections of the same type should be considered a possible match while 10 connections of differing types might be a match.
#5
good point - using a simple 1 degree with 2 or more points of intersection is easy and flexible - and giving admin controls might or might not be good - unfortunately most people don't really get into social network analysis and so may tend toward lower numbers in the hopes of 'growing a network' at the sacrifice of quality or integrity in implied relationships...but perhaps a simple ruleset exactly like what you describe: if user A has two or more friends (adjustable by admin, but no lower than 2 or 3) and those 2 friends have at least 1 (or more) friends in common - but not on user A list - then suggest to them...
#6
I think the purpose of this block is mainly for discoverability and not really deciding the strength of relationship a user need to establish with new users. That has to be determined by each user. e..g Alice just need to discover Bob has some relationship with Frank. What relationship Alice establishes with Frank is her decision and not based on the "type" of relationship between Bob and Frank. It might influence but is not the deciding factor.
So I think the weight etc is little overkill, it is nice to have but not necessary. All the block is saying " you might know" so if you find someone you already know, it is easier to add them to your network with whatver "level" of friendship you want to establish.
#7
i hear you - and i suppose the overarching theme here is that aside from popsugar, fastcompany a handful of other sites, most users won't be dealing with hundreds of thousands or millions of users ;)
#8
You may be right. I'm probably getting ahead of myself here.
I do think we need a way for admins to dissociate specific types from the "you may know" list. Going back to my previous example, when "subscriber" and "friend" are both types "subscribers" should not be included in the list.
@ajayg The example above wasn't meant to limit possible friends to a specific type when they decide to actually become friends. It was only meant to say that groups of relationships under the same type should hold more weight than relationships crossing over types.
So... anyone know anything about social graphs and how to represent them in an RDBMS?
#9
I agree certain types of relationships should be disassociated with this block. One prime example would be "one way" relations where you are "fan" of someone doesn't make you a real friend of whoever you are admiring. Just felt weight etc is little overkills.
One way would be just give a list of roles (types) this will search on and admin can choose certain roles that would be used to
create the query, ignoring rest.
More Trees & Hierarchies in SQL
http://www.sqlteam.com/article/more-trees-hierarchies-in-sql
Specially look at the lineage concept at the end. These ideas depend on data manipulation at the time of storage because if you don't do at storage, compleletely doing a lookup is not scalable for larger data sets.
I think this will be great addition to UR because
1. It is done at the time of insertion of records so no issue with scalability
2. Provides a shortest path
3. Will allow to easily implement the above block.
#10
So my only issue with that site is that it's talking about Trees and Hierarchies. Those'll work fine for familial or organizational relationships, but friends are more of a web. Any tips for representing that?
#11
Think of this as multiple hierarchies. Each user has his/her own tree starting from him/her and he/she is part of someone elses tree. At some level say beyond 4 or 5 you become part of a big web. But In real life relationship beyond 4 th level are seldome useful for meaninful reference. I am thinking of model used by a very popular social networking site linkedin.com. They used to have 5 levels and reduced to 4 levels.
So you already have implmented a single root user to whom everybody is connected. So any user can have a lineage
/root/level1/level2/level3/level4 I haven't done the math but I suspect this itself can represent a billion users. So for all users in your tree (below your level) you go and find just how many people they are connected.
If the lineage is represented as a string as mentioned in the algorithm, One simple search shortcut would be (and I haven't thought through all implications) search for records which match say /root/level1/level2/*/level4/*
* is wild card. First * is level you are at etc
I must say I haven't completely thought through yet just writing as I am thinking openly.
#12
I'm opposed to misrepresenting data. In this case the data isn't a hierarchy unless it is. It's really a graph and I think should be thought of and manipulated as such.
I found some interesting ideas here: http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html#nodes_...
The algorithms will need to be modified so that an update wont trigger an entire rewrite of the routes table. It's very late and my brain is very fried so I may need to go over all this info again tomorrow.
#13
I've been trying to do this without a module, just by creating a custom block; I think it can be done without creating extra tables.
[EDIT: I removed the rest of this post to make this page clearer, because there was a lot of (wrong) code.]
#14
Alright, that code was a little messy and none of it worked. Here's the first part, working, which displays a list of users with whom the current user and the user whose profile the current user is viewing have in common. I hard-coded the type of relationship in here: "Friends," and rtid = 2. I also hard-coded the website URL because I don't know enough about how to get it dynamically.
I'm working on the rest of it.
<?php
global $user;
$cur_user = $user->uid;
$view_user = arg(1); //assuming this block only shows up on user pages
echo "<strong>Friends in common:</strong>";
$view_user_rel = db_query("
SELECT u.name, u.uid FROM
(SELECT u.name, u.uid FROM {users} AS u
LEFT JOIN {user_relationships} AS ur ON (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
WHERE (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
AND (ur.requester_id = $cur_user OR ur.requestee_id = $cur_user)
AND (ur.approved = 1)
AND (ur.rtid = 2)
GROUP BY (u.name))
AS u
LEFT JOIN {user_relationships} AS ur ON (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
WHERE (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
AND (ur.requester_id = $view_user OR ur.requestee_id = $view_user)
AND (ur.approved = 1)
AND (ur.rtid = 2)
GROUP BY (u.name)
ORDER BY RAND()
");
echo "<ul>";
$i = 1;
while ($value = db_fetch_array($view_user_rel)) {
if ( $value['uid'] != $cur_user && $value['uid'] != $view_user ) {
echo "<li><a href=\"http://www.example.com/user/" . $value['uid'] . "\">" . $value['name'] . "</a></li>";
$i++;
}
if ( $i > 5 ) { break; }
}
echo "</ul>";
?>
#15
Okay, here's what we've all been waiting for. It displays a list of users the current user might relate to, Facebook style. There are a few hard-coded values in here--check the code comments to change them.
This is intended to go in a block, but as the final comment says I recommend putting it in a block and adding a "[More]" link to a page.
Enjoy!
<?php
global $user;
$cur_user = $user->uid;
$user_rel = db_query("
SELECT u.name, u.uid, u.picture
FROM {users} AS u
LEFT JOIN {user_relationships} AS ur ON (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
WHERE (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
AND (ur.requester_id = $cur_user OR ur.requestee_id = $cur_user)
AND (ur.approved = 1)
AND (ur.rtid = 2)
GROUP BY (u.name)
ORDER BY COUNT(ur.requester_id OR ur.requestee_id) DESC
"); //rtid is the relationship ID, in my case it represents "Friends"
if ( db_fetch_array($user_rel) ) { //skip the rest if the user has no friends
$user_rel_com = array();
mysql_data_seek($user_rel, 0); //I don't know if this is necessary...
//assigns the actual values into an array
while($value = db_fetch_array($user_rel)) {
$user_rel_com[] = $value;
}
echo "<h2>Users you might know:</h2>"; //needs to be themed, can be removed if you're putting this in a block
foreach($user_rel_com as $row){ //writes the sub-query for $user_rel_rel
$req_req_id_list .= " OR ur.requester_id = " . $row['uid'] . " OR ur.requestee_id = " . $row['uid'];
}
$len = strlen($req_req_id_list);
$req_req_id_list = substr($req_req_id_list, 4, $len); //trims the first " OR "
//gets all of the current user's friends' friends and orders by how many times they show up in the list
$user_rel_rel = db_query("
SELECT u.name, u.uid, u.picture
FROM {users} AS u
LEFT JOIN {user_relationships} AS ur ON (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
WHERE (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
AND ($req_req_id_list)
AND (ur.approved = 1)
AND (ur.rtid = 2)
GROUP BY (u.name)
ORDER BY COUNT(ur.requester_id OR ur.requestee_id) DESC
");
$user_rel_rel_com = array();
mysql_data_seek($user_rel_rel, 0);
//assigns the actual values into an array
while($value = db_fetch_array($user_rel_rel)) {
$user_rel_rel_com[] = $value;
}
//array_diff() wasn't working so I wrote my own; this just removes users who the current user already has a relationship with
$remaining = array();
foreach($user_rel_rel_com as $value){
if ( !array_search($value,$user_rel_com) ) {
$remaining[] = $value;
}
}
$i = 0;
echo "<ul>";
foreach ($remaining as $value) {
if ( $i < 5 ) { //change '5' to whatever you want; it limits the number of users shown
if ( $value['uid'] != $cur_user ) {
if ( !$value['picture'] ) { $value['picture'] = "/files/avatar_selection/BUp B 85.png"; } //change to whatever your default avatar is
echo "<li><a href=\"http://www.babelup.com/user/" . $value['uid'] . "\"><img src=\"http://www.babelup.com/" . $value['picture'] . "\" height = \"20px\" width = \"20px\" /> " . check_plain($value['name']) . "</a></li>"; //change according to your URL
}
}
$i++;
}
echo "</ul>";
//I recommend using this code in a block, and adding a [More] link here that goes to a page with all recommended users on it
}
?>
#16
One more thing: I wrote this on D5, and haven't tested it on D6. It only works on MySQL (should be okay on 4 and 5) because of mysql_data_seek() but I'm not entirely convinced that that needs to be in there at all.
I'm not aware of any changes from D5 to D6 that would affect this, except maybe something in the UR tables that I wouldn't know about because I'm not using D6.
If the maintainers of UR don't want to put this into the module itself, I can look into contributing it as a separate module.
#17
#18
I am getting following warning. I substituted ur.rtid with appropriate number.
warning: mysql_data_seek(): supplied argument is not a valid MySQL result resource in C:\xampp\htdocs\drupal-57\includes\common.inc(1352) : eval()'d code on line 19.warning: mysql_data_seek(): supplied argument is not a valid MySQL result resource in C:\xampp\htdocs\drupal-57\includes\common.inc(1352) : eval()'d code on line 48.
#19
Try removing the mysql_data_seek() lines (on line 19 and 48, as the error you got notes) and see if that helps. I had never heard of any code in Drupal needing to use mysql_data_seek() before, but I was looking through some PHP documentation and it mentioned that it was necessary. It worked for me, so I left it in, but it's likely it's not needed at all.
#20
#21
I removed the two mysql_data_seek() lines and it is working fine now as expected. I noticed a strange thing though. On user profile page it showed correct list of people I might know (friend of friends). But the same block shows a different list which were just direct friend on the other pages. So it is kind of odd to see direct friends as "people you might know". SO to work correctly the block needs to be disabled from pages other than profile pages.
#22
That should never be the case with the "friends of friends" block. The code works completely independently of anything in the URL and doesn't depend on anything being on the page. I can't duplicate the experience you're having; the code even works fine for me if I put it in a page instead of a block.
The "mutual friends" block, however, should ONLY show up on profile pages that are not the user's own. To accomplish that, use this PHP in the Block Visibility field:
<?phpglobal $user;
return $user->uid && arg(0) == 'user' && is_numeric(arg(1)) && arg(1) != $user->uid && !arg(2);
?>
For the websites I'm using this code, I have the "friends of friends" block show up on the user's own profile, and the "mutual friends" block show up on other people's profiles.
#23
I'm attaching some code I started for a submodule that adds this capability (both the "users you might know" and the "mutual friends" blocks) but I don't have time to finish it. There's a note at the very beginning of the file that should help anyone who wants to work on this.
Remember that when you download the file, you need to change the extension from .module.txt back to .module.
#24
Related (improved) code (though for FriendList, it uses essentially the same database structure; the only difference is the lack of an 'approved' column). May or may not work. Also lacks the benefits of the module, namely that there are no page views or settings.
#25
@IceCreamYou: First - thank you!
I'm trying to use your comment from comment #15 but get this error:
user warning: Invalid use of group functionquery:
SELECT u.name, u.uid, u.picture
FROM users AS u
LEFT JOIN user_relationships AS ur ON (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
WHERE (u.uid = ur.requester_id OR u.uid = ur.requestee_id)
AND (ur.requester_id = 1 OR ur.requestee_id = 1)
AND (ur.approved = 1)
AND (ur.rtid = 2)
GROUP BY (u.name)
ORDER BY COUNT(ur.requester_id OR ur.requestee_id) DESC"
Any idea?
I just went over the thread for FriendList and can see you guys have done some nice stuff.
Is there a chance you could backport your conclusions from there to the fix offered for UR here for all Drupal 5 users...?
Thanks again!
#26
I'm using this code on a production site. It's intended for a block, but (as you can tell from the final comment) it works just as well in a page.
#27
will this support d6 (@icecreamyou)? any changes that must be made?
#28
There is no db_num_rows() function in D6, so just take that out.
Also, there's a bug (for D5 and D6): one of the variables was accidentally written with two dollar signs ($). Just search for $$ and replace it with $.
Remember that you should always test this kind of thing in a page before you put it in a block (you can just preview it, you don't have to save) because blocks can break your site if they malfunction.
Also note that with a lot of users this code gets very slow.
#29
Tried the code you just posted but still getting the same error...
Should this work with UR 2.8/3-dev?
#30
I'm using 5.x-2.8. It's entirely possible that the DB structure changed in other versions, in which case I can't help you.
I know this has worked for other people though. System/install specs might help.
EDIT: I will say that it's probably better to group on u.uid than u.name.
EDIT 2: Just checked for sure, and that query is entirely valid MySQL. It should be valid on PGSQL as well.
#31
Ok i'm using 2.8 too...
MySQL 4.1.22, PHP 5.2.6, Apache 2.2.9 (Unix)
I'm going to try this whole thing again and see what happens...
#32
Ah. It probably doesn't work on MySQL 4. Try adding COUNT(uid) as a SELECT parameter - it may be that you have to explicitly declare a GROUP BY modifier function. If that doesn't work, I can't help you, unfortunately.
Try running the query directly from your database and see if you get a more specific error.
#33
I'll try to upgrade to mysql 5 ... it seems nothing is helping and i really need this feature!
Thanks for all the help!!!
EDIT: Well.. i'm afraid of upgrading mysql to 5.
Anyone know how to make this query work on mysql 4 ?
#34
how can I post the profile picture of any user im connected with, too?
#35
The given code does that. But the attached code is better (and should work in 5.x and 6.x). It does require a little CSS theming to get it to look nice, however.
#36
Hi,
sry it does not work. It shows always the guest user... (in Drupal 6, User Relationships 6.x-1.0-beta10)
perhaps you can fix it.
you can alsp contact me for further informations
thx
mathias
#37
I can't guarantee it will work in 6.x because I have no idea if the DB has changed. I do know that it works in 5.x-2.x...
#38
perhaps can you check it... and correct it ;-)
it will be nice if it works on d6
#39
Queries look good. There may be a number of issues, like Postgre vs MySQL, the type of relationship you are using (one way vs. two way), MySQL 4 vs. 5, as comments above discuss.
#40
Hi,
I use mysql 5.1 and two way relationship.
#41
I'm not the module maintainer and I have no interest in fixing it for a version that I know nothing about and don't use.
#42
#36
Do you mean you see the anonymous user being returned?
I noticed that too.
When the number of results returned from the initial query is less than the $max number.
$z[$i] does not have a value. Therefore the code is loading a 'uid' that does not exist.
Adding...
if(count($user_rel_rel_com) < $max) {
$max = count($user_rel_rel_com);
}
..before the line of code
for ($i = 0; $i < $max; $i++) {
will help get rid of the anonymous users.
#43
That's a valid solution for both branches actually--I didn't notice that bug before.
#44
Please reopen if further support is needed, just cleaning up the issue queue.
#45
alex.k - this is a feature request, not a support request. If this isn't in the module yet, this issue should stay open until either it's added to the module or it's marked "won't fix." I'm leaving it closed since I'm not the maintainer.
#46
Hi People,
We really want to look forward to add this feature as friendlist already has it however as compared to user relationships it is still not integrated with various modules like activity, author pane.
If this feature is available in user relationships then i think this module will be awesome. We may also consider adding other feature of friendlist like followers, following etc in user realtionships.
I really love the user relatioship modulle andd would like to see it as all in one relationship module.
Could anyone please think of adding those feature in this.
Regards
Sagar
#47
I also need this functionality.
I am trying to achieve it in Views, but have no luck with my combinations of filters / arguments / relationships.
Any help? Can Views_Or help me? Is the above code portable to d6?
EDIT : The functionality I am after is "Friends of this Friend"
#48
IceCreamYou, thank you.
Code in #15 worked for me in drupal 6. Also combination of #35 + #42 works.
It will be nice if this feature will be in the module.
#49
Sounds like #35 + #42 is the way to go
#50
We will be happy if it will be available soon.
#51
I feel kind of stupid (everybody seems to know what to do with that code),... but....
where do I have to put the php/uymn_0.txt code ? Views ? Some special place there ? Can't put it in a Block->Block Body, it only likes HTML. Or did you hard coded this stuff into a theme file ?!
#52
OK, I just found out, that I have to activate the PHP Filter Module to be able to add PHP code to a Block Body