June 01, 2006
Merging RSS Feeds with Query of Queries
One of my all-time favorite features in ColdFusion is "query of queries". I have yet to see this functionality duplicated as thoroughly, or as easily, in any other language outside of SQL itself. Of course the limitation with SQL is that it generally operates on relational data stored in a relational database. The query of queries functionality changes that for ColdFusion developers.
I'm a pretty blog-centric fellow. I rely on blogs to keep me up to date on the rapidliy changing world of technology, and on what it is that Adobe's amazing community of developers is up to each week. I also publish a few blogs. I have this one for work, but I also have several personal ones. It’s a great way to distribute and share information across my family which is widely geographically distributed.
As you likely know, most blogs have an XML format called RSS that is often used to describe content in a generic form that can be read by other programs that understand RSS and/or XML in general. There are a few versions of RSS, and there's also the emerging ATOM format. XML handling is also another great feature of ColdFusion. By comparison, in some other mainstream languages this is still amazingly painful.
Recently I decided that I'd like to merge two blogs with related content into one blog. I was publishing content too slowly to really need them both, but by combining them I could give the impression that I was very active. In my case one blog was managed by Blogger and published to my personal domain. The other blog wasn't really even a blog at all, but my Flickr photostream. Flickr can make your photostream available as an RSS feed.
There was the basis for this little exercise - two blogs, both with RSS feeds, to be merged and sorted by date. Normally this would be quite the chore, but after some thought it occurred to me that RSS was giving me a neutral way of describing the content. The two feeds looked like identical relational database tables. If I could build a table of each feed, I could run a SQL statement on them to join them together (technically a union).
This is when the solution hit me smack in the face. Use CFHTTP to read the RSS feeds individually, parse the XML content, generate a ColdFusion query objects, fill the query objects with data and run the SQL to put them together and sort them. In the end, not only would I have merged XML structures from different sources and sorted them, but I would also have native CF query object which would be ideal for looping/output.
What I ended up with was something that looked like the following code snippet (non-pertinent information removed).
<!--- Grab and parse Flickr RSS feed --->
<cfhttp url="#flickr#" />
<cfset doc = XmlParse( CFHTTP.FileContent ) />
<!--- Build query object with RSS feed data --->
<cfset photos = QueryNew( "source, title, link, description, publish", "VarChar, VarChar, VarChar, VarChar, Date" ) />
<cfloop index="sub" from="1" to="#ArrayLen( doc.rss.channel.item )#" step="1">
<!--- Extract the publish date string as date/time --->
<cfset publish = ParseDateTime( doc.rss.channel.item[sub].pubDate.XmlText ) />
<!--- Insert new row into query --->
<cfset rows = QueryAddRow( photos ) />
<cfset success = QuerySetCell( photos, "source", "flickr" ) />
<cfset success = QuerySetCell( photos, "title", doc.rss.channel.item[sub].title.XmlText ) />
<cfset success = QuerySetCell( photos, "link", doc.rss.channel.item[sub].link.XmlText ) />
<cfset success = QuerySetCell( photos, "description", description ) />
<cfset success = QuerySetCell( photos, "publish", publish />
</cfloop>
<!--- Print results to screen --->
<cfdump var="#photos#" />
<!--- ****************************** --->
<!--- Grab and parse Blogger RSS feed --->
<cfhttp url="#blogger#" />
<cfset doc = XmlParse( CFHTTP.FileContent ) />
<!--- Build query object with ATOM feed data --->
<cfset posts = QueryNew( "source, title, link, description, publish", "VarChar, VarChar, VarChar, VarChar, Date" ) />
<cfloop index="sub" from="1" to="#ArrayLen( doc.rss.channel.item )#" step="1">
<!--- Extract the publish date string as date/time --->
<cfset publish = ParseDateTime( doc.rss.channel.item[sub].pubDate.XmlText ) />
<cfset updated = doc.rss.channel.item[sub].updated.XmlText />
<cfset offset = Mid( updated, Len( updated ) - 3, 1 ) />
<cfset publish = DateAdd( "h", offset, publish ) />
<!--- Insert new row into query --->
<cfset rows = QueryAddRow( posts ) />
<cfset success = QuerySetCell( posts, "source", "blogger" ) />
<cfset success = QuerySetCell( posts, "title", doc.rss.channel.item[sub].title.XmlText ) />
<cfset success = QuerySetCell( posts, "link", doc.rss.channel.item[sub].link.XmlText ) />
<cfset success = QuerySetCell( posts, "description", doc.rss.channel.item[sub].description.XmlText ) />
<cfset success = QuerySetCell( posts, "publish", publish ) />
</cfloop>
<!--- Print results to screen --->
<cfdump var="#posts#" />
<!--- Query of query to merge feed data --->
<cfquery dbtype="query" name="merger">
SELECT *
FROM photos
UNION
SELECT *
FROM posts
ORDER BY publish DESC
</cfquery>
<!--- Print merged data to screen --->
<cfdump var="#merger#" />
I don't know how particularly useful this is for anybody else, but I was proud of the little exercise, and felt it was worth sharing. If you do find it handy, please let me know. For me, it was simply amazing just how productive this actually was thanks to ColdFusion. The entire gathering, parsing, populating and merging of both feeds is less than 70 lines of code including comments and spacing.
Posted by khoyt at 07:31 PM | Comments (2)
June 27, 2005
Accessing Flickr with ColdFusion
I love it each and every time I see a company offer an API for their product/service. The list is growing quite long these days, including Amazon, eBay, of course the Macromedia News Aggregator itself, and my personal favorite, Flickr. The Flickr API is really very extensive, allowing access to just about every feature they have to offer. When I ran down the list of "API Kits" I noticed that ColdFusion was not represented among the list of examples. With this in mind, I set out to see exactly what it would take.
The result is that it's excessively simple to access and work with the Flickr API through ColdFusion.
I chose the REST option for my example. For this type of access there are two distinct parts. The first is an HTTP request to the Flickr services. The URL of the HTTP request is expected to have various query string parameters depending on what method you want to access and that method’s parameters. The second part is a response to the HTTP request, but rather than getting HTML in return, a REST service gets XML formatted data.
As a simple example, when accessing the "flickr.test.echo" method, the HTTP request URL might be:
http://www.flickr.com/services/rest/?method=flickr.test.echo&api_key=123&foo=bar
This is a test method provided by the nice folks at Flickr as a means to ... uh .. test your request and make sure things are working the way you expect. As previously discussed, the response is XML formatted data. In this case, the echo method responds by simply echoing the parameters you passed in the request. Formatted as XML the above request might get a response that looks something like the following:
<rsp stat="ok">
<method>flickr.test.echo</method>
<api_key>123</api_key>
<foo>bar</foo>
</rsp>
What does this mean from a ColdFusion perspective? Well, let's break it down. We need to make an HTTP request and pass query string parameters. Okay, that'd be CFHTTP and CFHTTPPARAM. We get back an XML formatted data that we'll likely want to work with to determine what to display. Okay, that's the XmlParse() function provided by CFML. We might also want to display something specific from those results, which would require CFOUTPUT. Seems pretty basic, right? It is!
Here's a complete example (minus my personal API key of course) that uses the echo method of the Flickr API, and in this case simply iterates through the return parameters in an HTML page.
<cfparam name="flickr" default="http://www.flickr.com/services/rest/" />
<cfparam name="key" default="123" />
<cfhttp url="#flickr#">
<cfhttpparam name="api_key" type="url" value="#key#" />
<cfhttpparam name="method" type="url" value="flickr.test.echo" />
<cfhttpparam name="message" type="url" value="ColdFusion" />
</cfhttp>
<cfset doc = XmlParse( CFHTTP.FileContent ) />
<html>
<head>
<title>Flickr Echo with ColdFusion</title>
</head>
<body>
<cfloop index="sub" from="1" to="#ArrayLen( doc.rsp.XmlChildren )#" step="1">
<cfoutput>#doc.rsp.XmlChildren[sub]#</cfoutput> <br />
</cfloop>
<!--- If you'd like to see the XML document... --->
<!--- <cfdump var="#doc#" /> --->
</body>
</html>
This is clearly far from a complete API kit for ColdFusion but, in many cases, it's so simple to access Flickr with ColdFusion that it almost doesn't even really need one.
Posted by khoyt at 12:10 PM | Comments (8) | TrackBack
February 07, 2005
Denver, CO CFMX7 Tech Day
I'll be presenting at a Denver, CO area ColdFusion MX 7 technology day on February 23, 2005. The event is being held at Jillians at the Colorado Mills mall (14500 W. Colfax, Lakewood, CO) and runs from 9:00 AM until 1:00 PM. If you live or work in the area and have an interest in ColdFusion, then you'll want to be sure to attend.
Topics include:
CFMX7 Overview (Jeff Dalke, Practice Manager, GT Alliance)
Printing and Reporting (Greg Rewis, Web Technologies Evangelist, Macromedia)
Event Gateway (Mike Nimer, Senior Software Engineer, Macromedia)
Rich Forms & Flex (Kevin Hoyt, Sales Engineer, Macromedia)
Update:
The meeting organizer has informed me that you will need to register if you plan on attending this event. The cost is free, but please RSVP. You can register on the Macromedia web site.
Posted by khoyt at 08:37 AM | Comments (0)
January 03, 2005
ColdFusion New Years Resolutions
I haven't read any official reports, and can't make any scientific claims, but one might imagine that the most common resolution for 2005 will be to lose weight. If that's a goal you have, you might want to check out SportBrain.
The SportBrain is a trusty and smart little pedometer that tracks the steps you take throughout the day. When you're done for the day, snap it into its dock and it makes a phone call to dump your data on SportBrain servers. You can then log into their web site and graphically view your progress.
Some other features include seeing how you compare as to other SportBrain users in various categories, and inviting "SportGuests" to view your progress and leave comments. You can also get together with other SportBrain users and compete for prizes which can help with motivation.
I've used my SportBrain off and on for a number of months now and it's certainly one of the more accurate pedometers I've used. I'm a tall fellow (6' 7") and my gait can be hard to measure.
If you're wondering, how many steps you need each day to maintain an "active" lifestyle, most research suggests about 10,000 steps per day. Once you strap on a SportBrain you might be shocked at just how little you move every day.
What’s all this got to do with Macromedia? The SportBrain web site is powered by ColdFusion of course!
Posted by khoyt at 02:52 PM | Comments (2)
November 19, 2004
ColdFusion Helpers for Halo 2 Contest
For those of you who are CF'ers and are looking to get more structured access to the Halo RSS feed data, I've made available the CF source I used to do so during some of my development trials. There's two files - feed.cfc, which is the CFC that handles the workload, and feed.cfm, which is an example of how to use the CFC. So why didn't I use this in my project? Read on...
Note: This code was developed to test out an idea and was not designed with performance in mind. It comes as-is, with no warranty (smile).
I started off with fairly lofty feature goals. One of those features had me getting into an algorithm that had three nested "for" loops, and leveraged a minor degree of recursion. Bottom line - it was some ugly (though rather well performing) ActionScript. It seemed to me that I may not be using the right tool for the job. Indeed, if I could throw some XPath at the problem, I could eliminate the recursion and simplify the loops. Since CF offers great XML manipulation features (including XPath), I figured I'd go that direction.
I thought I might also be able to add some additional performance gains by leveraging remoting from my Flex app to the CFC for data calls. The idea was that I could have the server retrieve the XML feed and store it locally for processing until a refresh was requested from the application (every 15 minutes by default). At that point in time, I'd grab the latest RSS data. In the meantime, the Flex application would only be accessing small data chunks from that local XML file at a time (via a CFC object model), based on what the user wanted to see, and thanks to remoting, it would be a binary payload.
This approach would also allow me to be somewhat protected from the otherwise unpredictable feed availability. If the server was down, CF would catch the error and use the local file instead (not implemented in my example).
The trade off of course is that Flex itself has some rather nice XML facilities. The HTTPService class represents the XML data as a well structured DOM. Data-binding UI elements to XML data nodes makes display a walk in the park. Label function properties allow me to dynamically customize the way the node(s) display. If only I could get over this feature hump!
In the end, I dropped the feature, and went with another UI metaphor. Without the feature, it was much easier to stay inside Flex to accomplish my [now] fairly limited data processing. It also allowed me to hit the short, one-week deadline with other "nice to have" features. But I still have this dang CF code lying around (wink)! Please feel free to help yourself.
Posted by khoyt at 04:11 PM | Comments (0)