51

Quickie-Question:

To summarize, I'm a little confused as to how I would design such a database that allows indefinite badge-rule creation without requiring structural changes to the previously existing user-tables in the database.

Storing Badge Title, Criteria, etc. What would that Table look like?

  • badge_id (1)
  • badge_title (10K Badge)
  • badge_image (10k.jpg)
  • badge_criteria ([posts] >= 10000)
    ...

Winded-Question:

I would like to implement a badge-system on my own personal projects, but am seeking a bit of advice as to how one would best do such a thing. I've been reading some of the questions here about badge-systems, but don't see the database-architecture getting much attention.

Badges that are based on user-points (Hypothetical "10k Badge") would seem pretty straight forward. Any event that affects the users reputation (upvotes, downvotes, answer-accepted, etc) would invoke a method to review the users new reputation, and potentially award a badge.

That system sounds pretty straight forward, but what does that look like as a database for the administrator who wants to create innumerable amounts of badges with little-effort down the road - some of which may be based on different criteria, and not simply the user reputation.

User-reputation is likely a value within the user-record itself. But ideally, wouldn't you want to avoid having to add new fields to the user table when you create new badges? For instance the "Edited 100 Entries" badge - you wouldn't create a new column "entries_edited" within the Users Table, would you? And then increment that after each entry-edited...

Any hints?

Stackoverflow Archive:


Note: I'm NOT asking how to associate badges with users. I'm NOT asking how to award badges (that will be done programmatically)

Community
  • 1
  • 1
Sampson
  • 265,109
  • 74
  • 539
  • 565
  • 5
    +1 as I am looking for the same answer. – Robert S. Jun 26 '09 at 14:30
  • @Jonathan Sampson Can you please share your own very last answer: 1) In what methodology did you conclude? 2) Lessons learned from your methodology. regards and thanks for this wonderfully asked question. – Andre Chenier Jan 02 '14 at 13:47
  • Sampson I also am curious if you ever completed your project and had anything useful you could share? I found your post as I had the same idea of storing the criteria for a badge in a badge table. I am still researching the best way to do my system would love to hear from anyone who has done one! thanks – JasonDavis Dec 23 '15 at 03:04
  • 1
    @JasonDavis I did not. If I were to do this today, I would probably have a series of scripts (each representing a single badge/accomplishment) that run on a routine basis. A series of external scripts seems to me like the most flexible solution, since it allows for super-fine, ad hoc conditions. – Sampson Dec 23 '15 at 20:10

7 Answers7

23

Given that the badge criteria can be arbitrarily complex, I don't think you can store it in a database table broken down into "simple" data elements. Trying to write a "rules engine" that can handle arbitrarily complex criteria is going to take you down the path of basically re-writing all the tools that you have in your programming language.

If you know in advance you want the badges limited to only certain fields (i.e. badges are only based off reputation or number of edits or something), then you can store those in a simple table like:

ReputationBadgeCriteria
  BadgeId
  BadgeName
  MinReputation

Alternatively, you could use some kind of DSL to write your "rules" but you end up having to also create a parser to parse the rules when you read them as well as something to execute these rules. Depending on the complexity you want in your DSL, this may not be a trivial task. This looks like the path you are going in your question with having a Criteria column (presumably plain text) that has something like "[Reputation] > 1000" or "[Posts] > 5" in it. You still have to parse and execute those rules and the complexity of writing something to do so is dependent on how complex you want those rules to be.

I would recommend you read these Daily WTF articles for information on why this approach leads to pain.

David Archer
  • 2,121
  • 1
  • 15
  • 26
23

Depending on how far you want to go with it, your schema can get pretty complicated. It seems to me that the base elements you need to track are:

Badges awarded
Points earned

Pretty simple so far, but you want to be able to dynamically create new badges and new points categories. Badge awards would depend on earning points in one or more point categories that would add up to a certain amount. So you need to track the relationship between point categories (and points earned) and badges:

Point categories
Badge categories

So the key would be your user points table, which would link to point categories, which link to badges. Users earn points in a particular category, which would contribute to earning points towards one or more badges.

badges:
badge_id
badge_name
required_points
....

point_categories:
point_id
category_name
weighting (optional)
...

point_groups:
badge_id
point_id
weighting (optional)
...

user_points:
user_id
point_id
points
...

user_badges:
user_id
badge_id
points_earned
badge_awarded (yes/no)
...

Your "admin" interface would allow someone to create a new badge and pick which point categories are required to earn that badge (point_groups). Whenever a user earns points (user_points), you update the user_points table, then determine which badges those points would could contribute to (point_groups). You then recompile the points for the badges that were affected by the points earned and update the user_badges table with the point_earned. Then check the points_earned field in user_badges against the required_points in the badges table.

You can get much fancier by assigning different weights to different point categories, or even different weights for point categories for particular badges. But this setup would allow an unlimited amount of badges and point categories to be created and managed fairly easily without changing tables structures.

If that is completely not what you are looking for, then I think I should at least get a vote or two for a lot of typing.

Brent Baisley
  • 770
  • 4
  • 4
  • 1
    upvoted for lots of typing. also, i think this is the right path to go down... tho how far you go down that path is up to you. – ifatree Aug 26 '09 at 18:20
  • hi can you write down simple query to test above table..? – d3bug3r Aug 14 '12 at 16:34
  • I couldn't figure out that why this answer is not the accepted and most upvoted one. For hours I am thinking what can not I see that other people saw. For me, this is the best logic but lots of gurus absolutely know better than me :-( – Andre Chenier Jan 02 '14 at 13:49
  • +1 This is the best answer. It is the most thought out and thoroughly explained. – jtate Apr 22 '14 at 04:44
  • This works in the simple case where the badges are strictly based on reaching some number of points which the OP specifically called out as straightforward in the question. The real question is how do you do it for "innumerable" amount of badges that aren't just simple "user has > X points" type ones. – David Archer Feb 19 '16 at 03:45
5

You'd track your unique users in one table and unique badges in another then create a cross reference table to relate them.

A user can have many badges and a badge can have many users.

create table users (
id int,
name varchar
)

create table badges (
id int,
badge_name varchar
)


create table user_badges_xref (
user_id int,
badge_id int
)

Statistics that might affect whether a user earns a badge are tracked as a part of the administration of the site. so something like an answer being accepted would be in a schema that relates questions & answers. in order to display the answer and the owner of the answer, there would be a relationship to the user table and triggers that would check for badge conditions whenever a change was made.

I'm not asking how to award badges. I'm asking how to store criteria within the database.

So you want to store the logical operation required to determine if a badge is earned in a field somewhere?

I think agree with the other poster that criteria should be a part of the business logic. That logic can be on the app side or within a trigger. I think that's a matter of style.

If you were really married to the idea of storing the criteria in a field, I'd store it as parameterized SQL and run it dynamically.

So this sort of thing would be in your criteria field:

select "Badge Earned"
from all_posts 
where user_id = @user_id
having count(*) > 10000
Bob Probst
  • 9,533
  • 8
  • 32
  • 41
  • I'm not asking how to associate badges to users - I'm asking how to create and store criteria for badges in the future. – Sampson Jun 26 '09 at 13:52
  • I'm not asking how to award badges. I'm asking how to store criteria within the database. – Sampson Jun 26 '09 at 13:59
5

I'm approaching it like this: Create a table to store all the badges, and have one column reference a function that is run to see if the badge is awarded. That way the table remains simple and the logic to determine the badges can be kept in the code, where it is best suited.

With this method, badge requirements could also be linked together, to form more complex dependencies. For example, user must receive three separate, specific badges w/in a certain timeframe in order to get this badge.

Tapefreak
  • 982
  • 1
  • 13
  • 16
3

I would not create an increment for the edit badge. I think you should have a job running in the background and count() the numbero of post edited for members that haven't the badge of editing yet. When you see that the count is over the range you want, you add an entry in the database that tell that the user have the badge.

I think it's about the same for other badges. Try to limit the number of writting and do not write directly the count of the badge information in the user table. Use a table that will contain badge information and link it to the user table.

Patrick Desjardins
  • 136,852
  • 88
  • 292
  • 341
3

I apologize for being brief.

To implement a system such as this, I might create a table which stores either stored procedure names or actual queries that would be used to determine whether a particular user has earned a badge.

badge_criteria
badge_key int
badge_criteria varchar(max)

You can extract and execute the queries for badges which the user has not earned from your middle tier, but you would not have to make any code or structural changes to add new badges going forward.

Christian Pena
  • 183
  • 1
  • 5
2

This is going to be next to impossible to do in the database - awarding badges should be done in your business logic within the application. That way, you have all the existing data you need (edits, visits, reputation, etc.) and it can be dealt with as you see fit.

Update:

If by criteria you mean rules that determine if and how the badge is awarded, then that is not something that should be stored in the database. This would be almost impossible to test and maintain.

If you mean, for example, storing the "number of edits", there's no way getting around modifying a table or stored procedure to include that data if you need it.

Community
  • 1
  • 1
John Rasch
  • 62,489
  • 19
  • 106
  • 139
  • 1
    I'm awarding the badge with Code, yes. But the criteria for the badge will be stored in the database ideally, if I'm not mistaken. – Sampson Jun 26 '09 at 13:55
  • 1
    So Badge-Criteria should be stored in a config-file? Or what? The thing I'm trying to avoid is having to enter actual logic for the addition of a new badge. – Sampson Jun 26 '09 at 14:04
  • I never thought of a config file, but that could work. I was thinking more along the lines of storing constants in a "Badge" class - which is essentially the same thing. It really depends on the PHP framework you're using. – John Rasch Jun 26 '09 at 14:11
  • I am trying o do a badge system now and 1 idea I had is to possibly store a class method in my badge table that would process the logic for that badge, Any thoughts on his? – JasonDavis Dec 23 '15 at 03:07