Ask Ben: Redirecting Users To A Random Page

Posted October 28, 2008 at 10:19 AM by Ben Nadel

Tags: ColdFusion, Ask Ben

I am using cflocation to redirect all users to a temporary page. What I am wondering is if there is a way to have cflocation randomly or, even better, non-sequentially choose from a list of temporary pages to redirect users to. Is this possible?

This is actually a really easy task. ColdFusion provides very accessible randomization methods that we can leverage to get this done. What I'm gonna do it just put the list of temporary pages into an array and let ColdFusion randomly select from that array:

  • <!--- Create an array of URLs to which we are redirecting. --->
  • <cfset arrPages = ArrayNew( 1 ) />
  •  
  • <!--- Add the temporary pages to the array. --->
  • <cfset ArrayAppend( arrPages, "./temp/one.cfm" ) />
  • <cfset ArrayAppend( arrPages, "./temp/two.cfm" ) />
  • <cfset ArrayAppend( arrPages, "./temp/three.cfm" ) />
  • <cfset ArrayAppend( arrPages, "./temp/four.cfm" ) />
  •  
  •  
  • <!---
  • Because we are using this as a "Temporary Page", we can
  • use a standard CFLOcation tag. If we wanted this to be more
  • permanent, I would recomment using the CFHeader tag to
  • provide the propery status code (or CFLocation if you are
  • using CF8).
  •  
  • Let ColdFusion select a random page using RandRange() and the
  • size of the pages array.
  • --->
  • <cflocation
  • url="#arrPages[ RandRange( 1, ArrayLen( arrPages ) ) ]#"
  • addtoken="false"
  • />

Because the URL selection uses the actual array of pages to determine the random number assignment, you can update the array and it will automatically update the random redirects. RandRange() takes a lower and upper number range and will select a random integer from the range with the two ends inclusive.

I hope that helps.



Reader Comments

Oct 28, 2008 at 12:21 PM // reply »
6 Comments

Why would you want to redirect users to a random page??


Oct 28, 2008 at 12:23 PM // reply »
11,247 Comments

@Christopher,

I only answer the questions - I don't know why they're asked.


Oct 28, 2008 at 3:14 PM // reply »
132 Comments

@Christopher and Ben

It's not a totally unusual request in community sites. When you have thousands or millions of members, it's quite a cool feature to randomly be redirected to a random user page or submission to learn about users you wouldn't normally encounter.

deviantART for instance has Random Deviation (submission) and Random Deviant (user).

http://www.deviantart.com/random/deviant
http://www.deviantart.com/random/deviation

Of course Ben's example wouldn't scale for them since they have 2 million accounts and millions of submissions. Instead you'd need to generate a random number (or several) and select from the database the record with that id. By generating several record ids randomly you can avoid selecting a deleted row because the probability goes down dramatically.

In code:

<cfset user = queryNew("username")>
<cfloop condition="#not user.recordCount#">
<cfset numbers = RandomArray(10)>
<cfquery name="user">
select top 1 username
from users
where id in (<cfqueryparam value="#arrayToList(numbers)#" list="true">)
</cfquery>
</cfloop>

We can then cflocation like in Ben's code:

<cflocation url="http://#user.username#.riaforge.org" addtoken="false">

There is a possibility of an infinite loop in there, but the probability of never hitting a single record that's not deleted is so rare I doubt we'll ever come across that. You could add a check that if that loop ran N number of times to bail out too, if you were worried.

If hitting the database turns into a bottleneck, then cache a large number of random records (ex. 200), and select from them randomly without ever selecting duplicates, and that appears "random" to users, since most probably never hit it 200 times in a day. Getting clever you can avoid that issue too.


Oct 28, 2008 at 3:20 PM // reply »
110 Comments

Or hit the dB up for a random row and don't worry about getting back a deleted row

http://www.petefreitag.com/item/466.cfm


Oct 28, 2008 at 3:22 PM // reply »
11,247 Comments

@Elliott,

Good use case, thanks.


Oct 28, 2008 at 3:41 PM // reply »
6 Comments

I second Ben's "thanks". I've spent so much of my time working on boring (and lucrative) business/gov. apps that I couldn't think of a case where I'd want to apply randomness...other than the obvious "delete a random user" button that I just added to a system that I'm working on now ;)


Oct 28, 2008 at 3:48 PM // reply »
11,247 Comments

>delete a random user

Ha ha ha :) I try to make that an implicit part of my systems - that way it takes the thinking out of it for my users.


Oct 28, 2008 at 10:33 PM // reply »
132 Comments

@Gareth

If you read through the comments on that page you can see the various suggested methods don't scale well at all because they require generating random numbers for every row in the table.

If you have 62 million rows, that's no good.

Generating a finite key set and checking against that is O(k) where k is the key set size. This is constant. The index scan in the database is probably O(klog(n)). Since we never generate n keys (that'd be silly since it'd end up the same as the RAND() solution, we get O(klog(n)) type performance, which scales very well on large, partitioned, clustered indexed databases. Worst case we might have to do n key set generations, but honestly, that case is so rare...

On the other hand, the RAND() solution is always O(n) since it needs to generate that random number for every row in the database. Then we end up doing a O(nlog(n)) sort of the records in memory. So we end up with O(nlog(n)) as the runtime, worse though, is the fact that both the sort and the RAND() totally defeats the clustering and the indexing since we scan the entire table. :(

In any case, I suppose the right answer is "it depends". If your application is sufficiently modular you could refactor it later to handle the larger database by simply rewriting that one method.


Oct 28, 2008 at 11:13 PM // reply »
110 Comments

@Elliott,
Weird. I've used the SQL "NEWID()" on a table with a couple of million rows and did not have an issue with the data return. It seemed to chug along nicely. I guess if it was being hit with regularity (being on the front page for example), then it may bog the server down, but I hadn't had the issue when I ran it. I'll take your word for it as your knowledge seems to be O(K) on the subject :)


Oct 29, 2008 at 2:29 AM // reply »
132 Comments

@Gareth

Haha, fair enough. I've not benchmarked it, so outside the theory I'm not quite sure what the exact metrics are.

That'd certainly be something fun to look at.

Thanks!


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 24, 2013 at 5:39 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Adam Oops! My mistake! I hadn't gotten that far in my testing - I'm still baby stepping my way through the process. ... read »
May 24, 2013 at 5:13 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
Hi Jason, Thanks for checking up on that, but I still stand firm on my position. :) There are actually two listLast()'s in use, and you're right that the one using a space as a delimiter is fine. ... read »
May 24, 2013 at 4:45 PM
Ask Ben: Manually Enforcing Basic HTTP Authorization In ColdFusion
@Ben I have been lurking your site for quite some time, and haven't stepped up to comment until today. Thanks for all the great info - keep it up! @Adam I believe you are mistaken... as the commen ... read »
May 24, 2013 at 11:21 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, Ha ha, let's us never speak of justifying "##" notation again :P ... read »
May 24, 2013 at 11:18 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Ah, so it was indeed how I vaguely remembered it to be: A direct assignment value = users.id[ i ] causes value to retain the sticky datatype of the query column. Although unnecessary in ... read »
May 24, 2013 at 9:11 AM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Brandon, Hi, No, I haven't been able to do that. I have just kept it as it is. ... read »
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools