Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Likes
« on January 19th, 2012, 02:23 AM »
So, we've talked about this one but all the time up to now I've been putting off implementing it because I was never thoroughly sure how to efficiently handle doing likes.

The idea of doing a big (and potentially expensive) query on thread display never appealed to me but after sitting down and examining XenForo (which does it as a core feature) and seeing how they did it, I find myself unable to find a better way of doing it.

Specifically, their method is to embed an array of entries into the posts table, in a blob[1] and pull that out as display time.

Oddly enough, that actually contains the username as well so there's no extra performance hit there, but I imagine the change of display name is expensive if that person did a lot of likes.

So, for the programmers out there, what do you reckon?

As I see it, there are four options:
1. Just store the likes/post relationship and query it at display time (optionally with caching). Smallest in the DB, no additional maintenance, but it does mean there's an extra query - and we do have to run through that query in its entirety, it's not like we can stick a limit in, because you can't do a limit per criteria: you can't get "3 rows of that criteria, 10 rows of that", so we have to evaluate all the likes on the page at once.

2. Store the list of ids who like a post into the post table, and add that list of people into the main loadMemberData call (or do a second, smaller one just for minimal since we only need the names at that point). Much quicker and more efficient, but there's still extra work being done to handle the overhead of gathering names etc. (though of course we only get the right number of names, the first 3 or so actual names and just bundle the rest together)

3. Store the list of ids and names in the post table. Consumes a lot more space than either of the other two options, however it means there's no extra effort involved other than parsing the extra item (which need only be an unserialize call, or even a json_decode call, bearing in mind that we only need the first 3 or so rows, and can discard the rest after), and of course adds to the maintenance work if a user changes their display name.

4. We store just the count of likes in the post table, and fetch the list (using the same table as in 1) AJAXively if someone asks. Much lighter than the others, but far less nice IMHO.


Thoughts?
 1. Eh, could be a text, can't think of any overwhelming differences, other than that it's harder to get a blob in a table, you have to prance around doing CAST() to get at it.
When we unite against a common enemy that attacks our ethos, it nurtures group solidarity. Trolls are sensational, yes, but we keep everyone honest. | Game Memorial

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Likes
« Reply #1, on January 20th, 2012, 12:16 AM »
Code: [Select]
Solution #3 would make more sense.

Suggestion? Instead of storing the user name, just store the ID... Then, at display time, instead of [nobbc][tt]<a href="?action=profile;u=$context[user_id]">name</a>[/tt], link to [tt]<we:$uid:$context[user_id]>[/tt]
. The idea is that PrettyURLs could search for these special cases, add the user ID to the list of searched names, and then print a link with the user name, instead of just a URL to the user page. Of course, that would require PrettyURLs to be enabled, and modified a lot... :-/ Perhaps instead display the regular link, and $name as the user name, then just before outputting, search for all $name and replace with the user name corresponding to the profile link stored right before it, or something... Then we just need to retrieve user names in addition to logins, and at prettyURL time, make a new array where profile links are associated to user name.

Or simply, don't give a damn about name changes...

Oh, bugger. Just go with solution 2. We only have to run that extra query if any names are found... (And if you don't have any friends in the like list, don't bother to show any name.)

I would personally go for solution 4, for my own taste, but I understand that it'd be seen as disappointing.

live627

  • Should five per cent appear too small / Be thankful I don't take it all / 'Cause I'm the taxman, yeah I'm the taxman
  • Posts: 1,670
Re: Likes
« Reply #2, on January 20th, 2012, 12:44 AM »
I choose e: none of the above :P

Seriously, though, I'd go for a tasteful combination of 2 and 4. Maybe I'm a bit biased on this decision, because that's how I did it in a SMF mod once. I had a separate table to log all the members' likes and put a field in the messages table to aid with the number (so I could say, "Spoogs, Arantor, and 5,397 others like this").
A confident man keeps quiet.whereas a frightened man keeps talking, hiding his fear.

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #3, on January 20th, 2012, 12:59 AM »
It does make me wonder about an option F, actually: don't have any of it in the core and make it a plugin. That way we immediately stop worrying about some of the performance issues (then I'd primarily look at option 2 with a dash of 4 anyway)

The other thing is that option 4 is kind of implied in option 2 anyway, though not directly. You still have a master log table of likes so that you can query for 'the number of times my stuff has been liked'[1] but other than that you have a list of ids - nothing says you have to pull all the ids in question, just the first ones that need to be queried, and everything else can just be totalled up with count().

Means I think I might be able to tackle this one tomorrow (implementing option 2 primarily)


The other thing that occurs to me is that when content is liked, we need to return the new like content AJAXively so that we can transform 'X and Y like this' to 'You, X and Y like this'. I still don't think there's a nice way to trigger the colouriser buffer change, is there?
 1. And if we have aspirations about displaying that on post view, we should similarly cache that in the members table.

live627

  • Should five per cent appear too small / Be thankful I don't take it all / 'Cause I'm the taxman, yeah I'm the taxman
  • Posts: 1,670
Re: Likes
« Reply #4, on January 20th, 2012, 02:54 AM »
Wait, a color for the word "you"?

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #5, on January 20th, 2012, 03:10 AM »
No, not for 'you' but for the other names.

Unless you're going to assume that no-one else has liked it in the interim between you opening the page and you pressing like, in which case you only need return the new number of likes, put 'You' in front of it and lop off the last name on the list (which should be achievable client-side). Of course, when you unlike a post, you presumably want to return back to having three names that aren't your own... so you HAVE to return the names with colours.

live627

  • Should five per cent appear too small / Be thankful I don't take it all / 'Cause I'm the taxman, yeah I'm the taxman
  • Posts: 1,670
Re: Likes
« Reply #6, on January 20th, 2012, 05:36 AM »
What about reusing the DOM and just updating it?

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #7, on January 20th, 2012, 08:50 AM »
It doesn't matter. You can't just magically wave your hand at the DOM and deal with data you simply do not have.

As standard, let's say you display 3 names of likes plus a total for everyone else. For things you haven't liked yet, that means you have 'A, B, C and X others like this'.

Now, we can handle liking this content in the DOM and convert it to 'You, A, B and (X+n) others like this' where we fix the X+n part to be a live count.

The problem comes when we try to unlike the content. 'You, A, B and X others like this' needs to become 'A, B, C and X others like this' - but you don't have C in the DOM!

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Likes
« Reply #8, on January 20th, 2012, 11:52 AM »
You would think :P

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #9, on January 20th, 2012, 02:25 PM »
So, what you're saying is that I should always sjove A, B, C though even if the current user is on the list anyway, just on the off chance that they unlike it, as opposed to just updating the list on return?

Eh, no thanks. Don't like shoving more content through than necessary.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Likes
« Reply #10, on January 20th, 2012, 06:00 PM »
I just don't think that it's of any importance.

Yeah, it's cool to have your friends listed in the Like section (only your friends though...), and have a link to get a list of everyone who liked something. But in the end, what happens is that people don't care much about who liked something... They'll be likely to ignore the notice, and instead rely on positioning (i.e. they'll think that if some post is better liked, then it'll be positioned to its advantage in the list.)
That's mainly for topics, though, not for individual posts...

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #11, on January 20th, 2012, 06:17 PM »
I wasn't even going for 'friends' in the like section, simply listing the names of people who'd liked it. That way it feels less sterile than "3 people liked this".

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Likes
« Reply #12, on January 20th, 2012, 08:03 PM »
Sure, but if there are many people, you would put the emphasis on your friends, wouldn't you...?

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Likes
« Reply #13, on January 20th, 2012, 08:05 PM »
I only wish I could think this through clearly as to whether that's actually important or not. It is in FB for example but it isn't elsewhere.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Likes
« Reply #14, on January 20th, 2012, 08:12 PM »
Becomes critical when you have plenty of members...