<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;AkcCSH09cCp7ImA9WhRbE00.&quot;"><id>tag:blogger.com,1999:blog-18508356</id><updated>2012-02-03T17:27:49.368-05:00</updated><category term="linux" /><category term="hygiene" /><category term="cascade" /><category term="python programming" /><category term="javascript" /><category term="flot" /><category term="wxwindows" /><category term="REST" /><category term="dynamic" /><category term="ajax" /><category term="programming" /><category term="macros" /><category term="sqlalchemy" /><category term="pymongo" /><category term="sourceforge" /><category term="decorator" /><category term="10gen" /><category term="wxpython" /><category term="lisp" /><category term="websockets" /><category term="mongodb" /><category term="pycon" /><category term="mapreduce" /><category term="metapython" /><category term="posgresql" /><category term="descriptor" /><category term="grok" /><category term="zeromq" /><category term="gevent" /><category term="python" /><category term="cherrypy" /><category term="zope" /><category term="turbogears" /><category term="aggregation" /><category term="ming" /><category term="zarkov" /><category term="datalog" /><category term="json" /><category term="socket.io" /><category term="compiler" /><title>Just a little Python</title><subtitle type="html">Blog about all things Python that intersect my work and hobbies</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.pythonisito.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>63</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/JustALittlePython" /><feedburner:info uri="justalittlepython" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;DE4FRH84fCp7ImA9WhRbEE4.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-580764718919725833</id><published>2012-01-31T14:08:00.000-05:00</published><updated>2012-01-31T14:08:35.134-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-31T14:08:35.134-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="pymongo" /><title>Moving along with PyMongo</title><content type="html">&lt;p&gt;In a &lt;a href="http://blog.pythonisito.com/2012/01/getting-started-with-mongodb-and-python.html"&gt;previous post&lt;/a&gt;, I introduced &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; and its Python
driver &lt;a href="http://api.mongodb.org/python/current/"&gt;pymongo&lt;/a&gt;. Here, I continue in the same vein, describing in more
detail how you can become productive using pymongo.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;Connecting to the Database&lt;/h2&gt;
&lt;p&gt;In the last post, I gave a brief overview of how to connect to a database hosted
on the &lt;a href="http://mongolab.com"&gt;MongoLab&lt;/a&gt; service using pymongo, so I won't go into detail on
that here. Instead, I will mention a few options you'll want to be aware of. &lt;/p&gt;
&lt;p&gt;Some of the connection options set the default policy for the safety of your
data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;safe&lt;/strong&gt; MongoDB by default operates in a "fire-and-forget" mode where all
data-modifying operations are optimistically assumed to succeed. Turning on safe
mode changes this, waiting for a response from the database server indicating
that an operation has succeeded or failed before returning from a data-modifying
operation. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;journal&lt;/strong&gt; In version 1.8, MongoDB introduced journaling to provide
single-server durability. MongoDB's flexible approach to balancing safety and
performance, however, means that if your application wants to make sure its data
has &lt;em&gt;really&lt;/em&gt; been saved, you need to wait for the journal to be written.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fsync&lt;/strong&gt; This is the really, really safe option. With or without a journal,
this will wait until your data is on the physical disk before it returns from
update operations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;w&lt;/strong&gt; Before journaling, MongoDB used (and still uses) replication to ensure
the durability of your data. The w parameter allows you to control how many
servers (or which set of servers) your update has been replicated to before
returning from a data-modifying operation. Be aware that this parameter &lt;em&gt;can&lt;/em&gt;
significantly slow down your updates, particularly if you are requiring them to
be replicated to different datacenters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;read_preference&lt;/strong&gt; This option allows you to specify how you'd like to handle
queries. By default, even in a replica set configuration, all your queries will
be routed to the primary server in the replica set to ensure strong
consistency. Using read_preference you can change this policy, allowing your
queries to be distributed across the secondaries of your replica set for
increased performance at the cost of moving to "eventual consistency."&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One thing that's nice about the pymongo connection is that it's automatically
pooled. What this means is that pymongo maintains a pool of connections to the
mongodb server that it reuses over the lifetime of your application. This is good
for performance since it means pymongo doesn't need to go through the overhead of
establishing a connection each time it does an operation. Mostly, this happens
automatically. you &lt;em&gt;do&lt;/em&gt;, however, need to be aware of the connection pooling,
however, since you need to manually notify pymongo that you're "done" with a
connection in the pool so it can be reused.&lt;/p&gt;
&lt;p&gt;Now that all that is out of the way, the easiest way to connecto to a MongoDB
database from python is below (assuming you are running a MongoDB server
locally, and that you have installed ipython and pymongo):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Inserting Documents&lt;/h2&gt;
&lt;p&gt;Inserting documents begins by selecting a database. To create a database, you
do... well, nothing, actually. The first time you refer to a database, the
MongoDB server creates it for you automatically. So once you have your database,
you need to decide which "collection" in which to store your documents. To create
a collection, you do... right - nothing. So here we go:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tutorial&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27017&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tutorial&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25bcffeb033049af000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;One thing to note here is that the insert command returned us an ObjectId
value. This is the value that pymongo generated for the &lt;code&gt;_id&lt;/code&gt; property, the
"primary key" of a MongoDB document. We can also manually specify the _id if we
want (and we don't have to use ObjectIds):&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h3&gt;A note on document structure&lt;/h3&gt;
&lt;p&gt;A note here is order about the kind of documents you can insert into a
collection. Technically, the type of document is described by the &lt;a href="http://bsonspec.org/"&gt;BSON&lt;/a&gt;
spec, but practically you can think of it as JSON plus a few extra types. Some of
the types you should be aware of are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;primitive types&lt;/strong&gt; Python ints, strings, and floats are automatically handled
  appropriately by pymongo. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;object&lt;/strong&gt; Objects are represented by pymongo as regular python
  dicts. Technically, in BSON, objects are &lt;em&gt;ordered&lt;/em&gt; dictionaries, so if you need
  or want to rely on the ordering you can use the &lt;code&gt;bson&lt;/code&gt; module, included with
  pymongo, to create such objects. Objects have strings as their keys and can have
  any valid BSON type as their values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;array&lt;/strong&gt; Arrays are represented by pymongo as Python lists. Again, any valid
  BSON type can be used as the values, and the values in an array do not need
  to be of the same type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ObjectId&lt;/strong&gt; ObjectIds can be thought of as globally unique identifiers. They
   are often used to generate "safe" primary keys for collections without the
   overhead of using a sequence generator as is often done in relational
   databases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binary&lt;/strong&gt; Strings in BSON are stored as UTF-8 - encoded unicode. To store
    non-unicode data you must use the &lt;code&gt;bson.Binary&lt;/code&gt; wrapper around a Python
    string. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Documents in MongoDB also have a size limit (16MB in the latest version), which
is enough for many use-cases, but is something you'll need to be aware of when
designing your schemas.&lt;/p&gt;
&lt;h3&gt;Batch inserts&lt;/h3&gt;
&lt;p&gt;MongoDB and pymongo also allow you to insert multiple documents with a single API
call (and a single trip to the server). This can significantly speed up your
inserts, and is useful for things like batch loads. To perform a multi-insert,
you simply pass a list of documents to the &lt;code&gt;insert()&lt;/code&gt; method rather than a single
document:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c0aceb033049af000001&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c0aceb033049af000002&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You may have noticed that the structure of the documents inserted in this snippet
differ (significantly!) from the documents inserted before. MongoDB does not make
any requirements that documents share structure with one another. Analogous to
Python's dynamic typing, MongoDB is a dynamically typed database, where the
structure of the document is stored along with the document itself. Practically,
I've found it useful to group similarly structured documents into collections,
but it's definitely not a hard-and-fast rule.&lt;/p&gt;
&lt;h2&gt;Querying&lt;/h2&gt;
&lt;p&gt;OK, now that you've got your data into the database, how do you get it back out
again? That's the function of the &lt;code&gt;find()&lt;/code&gt; method on collection objects. With no
parameters, it will return all the documents in a collection as a Python
iterator:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7f2910068b90&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25bce9eb033049a0000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25bcffeb033049af000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c0aceb033049af000001&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c0aceb033049af000002&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Most of the time, however, you'll want to select particular documents to return.
You do this by providing a query as the first parameter to find. Queries are
represented as BSON objects as well, and are similar to query-by-example that you
may have seen in other database technologies. For instance, to retrieve all
documents that have the name 'My Document', you would use the following query:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25bce9eb033049a0000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25bcffeb033049af000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;ids&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;My Document&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;subdocument&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;MongoDB also allows you to 'reach inside' embedded documents using the dot
notation. Here are some examples of how you can use that:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
   &lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
   &lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
   &lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
   &lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="c1"&gt;# reach inside objects&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="c1"&gt;# find objects where *any* value in an array matches &lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="c1"&gt;# reach into an array&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c.d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c.1.d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;One other thing that's important to be aware of is that you can return only a
subset of the fields in a query. (By default, &lt;code&gt;_id&lt;/code&gt; is &lt;em&gt;always&lt;/em&gt; returned unless
you explicitly suppress it.) Here is an example:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c.1.d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="c1"&gt;# we can also reach inside when specifying which fields to return&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c.1.d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We can also restrict the &lt;em&gt;number&lt;/em&gt; of results returned from a query by &lt;code&gt;skip&lt;/code&gt;ping
some documents and &lt;code&gt;limit&lt;/code&gt;ing the total number returned:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;a href="http://www.mongodb.org/display/DOCS/Querying"&gt;query&lt;/a&gt; and &lt;a href="http://www.mongodb.org/display/DOCS/Advanced+Queries"&gt;advanced query&lt;/a&gt; pages in
the MongoDB docs provide the full query syntax, which includes inequalities, size
and type operations, and more.&lt;/p&gt;
&lt;h2&gt;Indexing&lt;/h2&gt;
&lt;p&gt;Like any database, MongoDB can only perform so well by scanning collections for
matches. To provide better performance, MongoDB can use indexes on its
collections. The normal method of specifying an index in MongoDB is by "ensuring"
its existence, in keeping with the pattern of having things spring into existence
when they're needed. To create an index on our 'test' collection, for instance,
we would do the following:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ensure_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a_1&amp;#39;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
   &lt;span class="o"&gt;....&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f28261deb033053bc000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f28261deb033053bc000001&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f28261deb033053bc000002&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x24f7b90&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_one&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f28261deb033053bc000001&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Ok, well that's not actually all that exciting. However, MongoDB provides an
&lt;code&gt;explain()&lt;/code&gt; method that lets us see whether our index is getting used:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;allPlans&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BtreeCursor a_1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]}}],&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BtreeCursor a_1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]},&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexOnly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;isMultiKey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;millis&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nChunkSkips&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nYields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nscanned&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nscannedObjects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are several important things to note about the result here. The most
important is the cursor type, &lt;code&gt;BtreeCursor a_1&lt;/code&gt;. This means that it's using an
index, and in particular the index named &lt;code&gt;a_1&lt;/code&gt; that we created above. If the
field is &lt;em&gt;not&lt;/em&gt; indexed, we get a different query plan from MongoDB:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;allPlans&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}}],&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexOnly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;isMultiKey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;millis&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nChunkSkips&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nYields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nscanned&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;nscannedObjects&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Here, MongoDB is using a &lt;code&gt;BasicCursor&lt;/code&gt;. For you SQL experts out there, this is
equivalent to a full table scan, and is very inefficient. Note also that when we
queried the indexed field, &lt;code&gt;nscanned&lt;/code&gt; and &lt;code&gt;nscannedObjects&lt;/code&gt; were both one,
meaning that it only had to check one object to satisfy the query, whereas in the
case of our unindexed field, we had to check every document in the collection.&lt;/p&gt;
&lt;p&gt;MongoDB has an &lt;em&gt;extremely&lt;/em&gt; fast query that it can use in some cases where it
doesn't have to scan &lt;em&gt;any&lt;/em&gt; objects, only the index entries. This happens when the
only data you're returning from a query is part of the index:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="o"&gt;...&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]]},&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexOnly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;isMultiKey&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note here the &lt;code&gt;indexOnly&lt;/code&gt; field is true, specifying that MongoDB only had to
inspect the index (and not the actual collection data) to satisfy the query.&lt;/p&gt;
&lt;p&gt;Another thing that's nice about the MongoDB index system is that it can use
compound indexes (indexes that include more than one field) to satisfy some
queries. In this case, you should specify the &lt;em&gt;direction&lt;/em&gt; of each field since
MongoDB stores indexes as B-trees. An illustration is probably best. First, we'll
drop our &lt;code&gt;a_1&lt;/code&gt; index and ensure a new index on &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, both ascending:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a_1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ensure_index&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a_1_b_1&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, let's see what happens when we query by just &lt;code&gt;a&lt;/code&gt;:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="o"&gt;...&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BtreeCursor a_1_b_1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$minElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$maxElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]]},&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;MongoDB's optimizer here is "smart" enough to use the compound (a,b) index to
query just the &lt;code&gt;a&lt;/code&gt; value. What if we query just the &lt;code&gt;b&lt;/code&gt; value?&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;allPlans&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}}],&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Oops! That doesn't work because the index is sorted with keys (a,b). Key order
also becomes important when we want to sort our results:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="o"&gt;...&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BtreeCursor a_1_b_1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$minElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$maxElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]],&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$minElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$maxElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]]},&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;allPlans&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;indexBounds&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}}],&lt;/span&gt;
 &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;cursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;BasicCursor&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So if we sort in the same order as our index, we can use the B-tree index to sort
the results. If we sort in a different order, we can't, so MongoDB has to
actually load the entire result set into RAM and sort it there. (MongoDB can
actually use an index for the exact reverse of our sort order as well, so 
&lt;code&gt;[(a, -1), (b, -1)]&lt;/code&gt; would have worked just fine.) In a collection
of 3 documents, this isn't important, but as your data grows, this can become
quite slow, in some cases actually returning an error because the result set is
too large to sort in RAM.&lt;/p&gt;
&lt;h2&gt;Deleting data&lt;/h2&gt;
&lt;p&gt;Deleting data in MongoDB is fairly straightforward. All you need to do is to pass
a query to the &lt;code&gt;remove()&lt;/code&gt; method on the collection, and MongoDB will delete all
documents from the collection that match the query. (Note that deletes can still
be slow if you specify the query inefficiently, as they use indexes just like
queries do.)&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;}]},&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af00000b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;73&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$gt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h2&gt;Updating data&lt;/h2&gt;
&lt;p&gt;In many cases, an update in MongoDB is as straightforward as calling &lt;code&gt;save()&lt;/code&gt; on
a python dict:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_one&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;code&gt;save()&lt;/code&gt; can also be used to insert documents if they don't exist yet (this check
is done by checking the dict to be saved for the &lt;code&gt;_id&lt;/code&gt; key).&lt;/p&gt;
&lt;p&gt;Unlike some other NoSQL &lt;a href="http://couchdb.apache.org/"&gt;solutions&lt;/a&gt;, MongoDB allows you to do quick,
in-place &lt;a href="http://www.mongodb.org/display/DOCS/Updating"&gt;updates&lt;/a&gt; of documents using special 
[modifier operations][mongodb-modifier]. For instance, to set a field to a
particular value, you can do the following:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a.b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;testq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;82&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f25c89beb033049af000009&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;b&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;}]}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;MongoDB provides several different modifiers you can use to update documents in
place, including the following (for more details see  &lt;a href="http://www.mongodb.org/display/DOCS/Updating"&gt;updates&lt;/a&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;$inc&lt;/strong&gt; Increment a numeric field (generalized; can increment by any number)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$set&lt;/strong&gt; Set certain fields to new values&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$unset&lt;/strong&gt; Remove a field from the document&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$push&lt;/strong&gt; Append a value onto an array in the document&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$pushAll&lt;/strong&gt; Append several values onto an array&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$addToSet&lt;/strong&gt; Add a value to an array if and only if it does not already exist&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$pop&lt;/strong&gt; Remove the last (or first) value of an array&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$pull&lt;/strong&gt; Remove all occurrences of a value from an array&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$pullAll&lt;/strong&gt; Remove all occurrences of any of a set of values from an array&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$rename&lt;/strong&gt; Rename a field&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$bit&lt;/strong&gt; Bitwise updates &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There's a lot more that I could  cover, but hopefully that whets your appetite to
learn more about MongoDB. In future posts, I'll discuss how to use GridFS (a
"filesystem" on top of MongoDB) and MongoDB's various aggregation options, as
well as how you can use &lt;a href="http://merciless.sourceforge.net/"&gt;Ming&lt;/a&gt; to simplify certain operations.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-580764718919725833?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/DeY4IbrS9uylG5lmHSktbgTcgGE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DeY4IbrS9uylG5lmHSktbgTcgGE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/DeY4IbrS9uylG5lmHSktbgTcgGE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/DeY4IbrS9uylG5lmHSktbgTcgGE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=l1wiZoCCPTw:yoFM-BW-jp8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=l1wiZoCCPTw:yoFM-BW-jp8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=l1wiZoCCPTw:yoFM-BW-jp8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=l1wiZoCCPTw:yoFM-BW-jp8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=l1wiZoCCPTw:yoFM-BW-jp8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/l1wiZoCCPTw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/580764718919725833/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2012/01/moving-along-with-pymongo.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/580764718919725833?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/580764718919725833?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/l1wiZoCCPTw/moving-along-with-pymongo.html" title="Moving along with PyMongo" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.pythonisito.com/2012/01/moving-along-with-pymongo.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcHSHg4eyp7ImA9WhRVGUw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-2960147252637298718</id><published>2012-01-18T13:07:00.000-05:00</published><updated>2012-01-18T13:07:19.633-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-18T13:07:19.633-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="pymongo" /><title>Getting Started with MongoDB and Python</title><content type="html">&lt;p&gt;If you've been following this blog for a while, you've seen me mention MongoDB
more than once. One exciting thing for me is that I'll be co-teaching a tutorial
at PyCon this year on &lt;a href="http://us.pycon.org/2012/schedule/presentation/186/)"&gt;Python and MongoDB&lt;/a&gt; that will cover
MongoDB, PyMongo, and Ming. So to hopefully whet your appetite for learning more at the tutorial, I
thought I'd write a few posts covering MongoDB, PyMongo, and Ming from a
beginner's perspective. &lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;What is MongoDB?&lt;/h2&gt;
&lt;p&gt;From &lt;a href="http://www.mongodb.org/"&gt;MongoDB.org&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;MongoDB (from "humongous") is a scalable, high-performance, open source NoSQL
database. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well, that's not all that enlightening, so I'll expand a bit here on MongoDB's features...&lt;/p&gt;
&lt;h3&gt;MongoDB is a document database&lt;/h3&gt;
&lt;p&gt;MongoDB is a document database, which means that instead of storing "rows" in
"tables" like you do in a relational database, you store "documents" in
"collections." Documents are basically &lt;a href="http://json.org/"&gt;JSON&lt;/a&gt; objects
(technically &lt;a href="http://bsonspec.org/"&gt;BSON&lt;/a&gt;. This is to be distinguished from other
NoSQL-type databases such as key-value stores (e.g. &lt;a href="http://fallabs.com/tokyocabinet/"&gt;Tokyo
Cabinet&lt;/a&gt;), column family stores (e.g. &lt;a href="http://cassandra.apache.org/"&gt;Cassandra&lt;/a&gt;) or
column stores (e.g. &lt;a href="http://www.monetdb.org/"&gt;MonetDB&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;MongoDB has a flexible query language&lt;/h3&gt;
&lt;p&gt;This is one thing that makes MongoDB a pleasure to work with, particularly if you
come from another NoSQL database where querying is either restrictive (key-value
stores which can only be queried by key) or cumbersome (something like
&lt;a href="http://couchdb.apache.org/"&gt;CouchDB&lt;/a&gt; that requires you to write a &lt;a href="http://howfuckedismydatabase.com/nosql/"&gt;map-reduce query&lt;/a&gt;). MongoDB
has a BSON-based query language that's a bit more restrictive than SQL, that you
can still use to get a lot done.&lt;/p&gt;
&lt;p&gt;Here's an example of a simple MongoDB query that we use at SourceForge to find
all the blog posts for a project:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;blog_post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;state&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;published&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;app_config_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;$in&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;app_config_ids&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;There are also several other operators like '$lt', '$nin', '$not', and '$or' that
allow you to construct quite complex queries, though you are somewhat restricted
from what you can do in SQL (even with a single table).&lt;/p&gt;
&lt;h3&gt;MongoDB is fast and scalable&lt;/h3&gt;
&lt;p&gt;A single MongoDB node is able to comfortably serve 1000s of requests per second
on cheap hardware. When you need to scale beyond that, you can use either
replication (keeping several copies of the data on different servers) or sharding
(partitioning the data across servers). MongoDB even includes logic to automatically
load-balance your shards as your database and load increase.&lt;/p&gt;
&lt;h2&gt;Getting Started with MongoDB&lt;/h2&gt;
&lt;p&gt;While MongoDB is fairly straightforward to install on (64-bit) systems, there are
also a couple of companies that provide a free tier of MongoDB hosting, &lt;a href="http://mongolab.com"&gt;MongoLab&lt;/a&gt;
and &lt;a href="http://mongohq.com"&gt;MongoHQ&lt;/a&gt; that are great for getting started. I've been using, for
no particular reason, MongoLab for my own things and I can recommend them, and
it's what I have experience with, so that's what I'll cover here. &lt;/p&gt;
&lt;p&gt;Let's assume you sign up for a MongoLab account. Once you've done this, you can
create a database using their web-based control panel and click on it, you'll
note the connection info at the top of the page:&lt;/p&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://4.bp.blogspot.com/-6_JhBQkaegg/TxcJl5dvVfI/AAAAAAAAAnI/TfsGHQ0Ad84/s1600/mongolab-connection.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="84" width="400" src="http://4.bp.blogspot.com/-6_JhBQkaegg/TxcJl5dvVfI/AAAAAAAAAnI/TfsGHQ0Ad84/s400/mongolab-connection.png" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;(Your server name and port number may be different.) At this point, most
tutorials would tell you to install and launch the 'mongo' command-line tool to
begin exploring your database. We'll skip that here and use the python driver
PyMongo directly. I like to use &lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;virtualenv&lt;/a&gt; myself and
&lt;a href="http://ipython.org"&gt;ipython&lt;/a&gt;, so that's the approach I'll take here:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;virtualenv&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;source&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="sr"&gt;/bin/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt; &lt;span class="n"&gt;ipython&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;ipython&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;banner&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now that we're in ipython, we'll go ahead and connect to the database and create
a document.&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;mongodb://tutorial-test:u3ZYh136@ds029187.mongolab.com:29187/tutorial-test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;tutorial-test&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f16f5c7eb03306a92000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;pymongo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x7fbb9006f350&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;4f16f5c7eb03306a92000000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)}]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Well, that's it for now. I'll be posting several followup articles in this series
that will go into more detail on how to do various queries and updates using
&lt;a href="http://api.mongodb.org/python/current/"&gt;PyMongo&lt;/a&gt;, the MongoDB python driver, as well as how to effectively use
&lt;a href="http://merciless.sourceforge.net/"&gt;Ming&lt;/a&gt;, so stay tuned!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-2960147252637298718?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/-sCyIuV97QkqB91f4FQylixEHU0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-sCyIuV97QkqB91f4FQylixEHU0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/-sCyIuV97QkqB91f4FQylixEHU0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/-sCyIuV97QkqB91f4FQylixEHU0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=r1eSdclvp9I:nweb_ZKHM8U:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=r1eSdclvp9I:nweb_ZKHM8U:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=r1eSdclvp9I:nweb_ZKHM8U:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=r1eSdclvp9I:nweb_ZKHM8U:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=r1eSdclvp9I:nweb_ZKHM8U:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/r1eSdclvp9I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/2960147252637298718/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2012/01/getting-started-with-mongodb-and-python.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/2960147252637298718?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/2960147252637298718?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/r1eSdclvp9I/getting-started-with-mongodb-and-python.html" title="Getting Started with MongoDB and Python" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-6_JhBQkaegg/TxcJl5dvVfI/AAAAAAAAAnI/TfsGHQ0Ad84/s72-c/mongolab-connection.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://blog.pythonisito.com/2012/01/getting-started-with-mongodb-and-python.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU8ERHoycSp7ImA9WhRWE0s.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-9089049144420251503</id><published>2011-12-31T15:22:00.008-05:00</published><updated>2011-12-31T15:36:45.499-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-31T15:36:45.499-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><title>MongoDB's Write Lock</title><content type="html">&lt;p&gt;MongoDB, as some of you may know, has a process-wide write lock. This has caused some degree of ridicule from database purists when they discover such a primitive locking model. Now per-database and per-collection locking is on the roadmap for MongoDB, but it's not here yet. What was announced in MongoDB version 2.0 was locking-with-yield. I was curious about the performance impact of the write lock and the improvement of lock-with-yield, so I decided to do a little benchmark, MongoDB 1.8 versus MongoDB 2.0.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Page Faults and Working Sets&lt;/h2&gt;&lt;p&gt;MongoDB uses the operating system's virtual memory system to handle writing data back to disk. The way this works is that MongoDB maps the entire database into the OS's virtual memory and lets the OS decide which pages are 'hot' and need to be in &lt;em&gt;physical&lt;/em&gt; memory, as well as which pages are 'cold' and can be swapped out to disk. MongoDB also has a periodic process that makes sure that the database on disk is sync'd up with the database in memory. (MongoDB also includes a journalling system to make sure you don't lose data, but it's beyond the scope of this article.)&lt;/p&gt;&lt;p&gt;What this means is that you have &lt;em&gt;very&lt;/em&gt; different behavior depending on whether the particular page you're reading or writing is in memory or not. If it is not in memory, this is called a page fault and the query or update has to wait for the &lt;em&gt;very&lt;/em&gt; slow operation of loading the necessary data from disk. If the data &lt;em&gt;is&lt;/em&gt; in memory (the OS has decided it's a 'hot' page), then an update is literally just writing to system RAM, a &lt;em&gt;very&lt;/em&gt; fast operation. This is why 10gen (the makers of MongoDB) recommend that, if at all possible, you have sufficient RAM on your server to keep the entire working set (the set of data you access frequently) in memory at all times. &lt;/p&gt;&lt;p&gt;If you are able to do this, it turns out that the global write lock really doesn't affect you. Blocking reads for a few nanoseconds while a write completes turns out to be a non-issue. (I have not measured this, but I suspect that the acquisition of the global write lock takes significantly longer than the actual write.) But this is all kind of hand-waving at this point. I did end up measuring what happens to your query rate as you do more and more (non-faulting) writes and found very little effect.&lt;/p&gt;&lt;h2&gt;2.0 to the Rescue?&lt;/h2&gt;&lt;p&gt;So the &lt;em&gt;real&lt;/em&gt; problem happens when you're doing a write, you acquire the lock, and then you try to write and &lt;strong&gt;fault&lt;/strong&gt; while holding the lock. Since a page fault can take around 40,000 times longer than a nonfaulting memory operation, this obviously causes problems. In MongoDB version 2.0 and higher, this is addressed by detecting the likelihood of a page fault and releasing the lock before faulting. This allows other reads and writes to proceed while the data is brought in from disk. This is what I'm really trying to measure here; what is the effect of a so-called "write lock with yield" as opposed to a plain old "write lock?"&lt;/p&gt;&lt;h2&gt;Benchmark Setup&lt;/h2&gt;&lt;p&gt;First off, I didn't want to worry about all the stuff I normally run on my laptop to get in the way, and I'd like to make the benchmark as reproducible as possible, so I decided to run it on Amazon EC2. (I have made the code I used available &lt;a href="https://sourceforge.net/u/rick446/random/"&gt;here&lt;/a&gt; - particularly fill_data.py, lib.py, and combined_benchmark.py if you'd like to try to replicate the results. Or you can just grab my image ami-63ca1e0a off AWS.) I used an m1.large instance size instance running Ubuntu server with ephemeral storage used for the database store. I also reformatted the ephemeral disk from ext3 to ext4 and mounted it with noatime for performance purposes. MongoDB is installed from the &lt;a href="http://www.mongodb.org/display/DOCS/Ubuntu+and+Debian+packages"&gt;10gen .deb repositories&lt;/a&gt; as version 1.8.4 (mongodb18-10gen) and 2.0.2 (mongodb-10gen).&lt;/p&gt;&lt;p&gt;The database used in the test contains one collection of 150 million 112-byte documents. This was chosen to give a total size significantly in excess of the 7.5 GB of physical RAM allocated to the m1.large instances. (The collection size with its index takes up 21.67 GB in virtual memory). Journalling has been turned *off* in both 1.8 and 2.0.&lt;/p&gt;&lt;p&gt;What I wanted to measure is the effect of writes on reads that are occurring simultaneously. In order to simulate such a load, the Python benchmark code uses the gevent library to create two greenlets. One is reading randomly from its working set of 10,000 documents as fast as possible, intended to show non-faulting read performance. The second greenlet attempts to write to a random document of the 150 million total documents at a specified rate. I then measure the achieved write and read rate and create a scatter plot of the results.&lt;/p&gt;&lt;h2&gt;Results&lt;/h2&gt;&lt;p&gt;First, I wanted to show the effect of &lt;em&gt;non&lt;/em&gt;-faulting writes on non-faulting reads. For this, I restrict writes to occur only in the first 10,000 documents (the same set that's being read). As expected, the write lock doesn't degrade performance much, and there's little difference between 1.8 and 2.0 in this case.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-oxAkPmpZ9VU/Tv9vq-L7klI/AAAAAAAAAms/hGIeMPk7diM/s1600/NonfaultingWrites.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="272" width="400" src="http://4.bp.blogspot.com/-oxAkPmpZ9VU/Tv9vq-L7klI/AAAAAAAAAms/hGIeMPk7diM/s400/NonfaultingWrites.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;The next test shows the difference between 1.8 and 2.0 in fairly stark contrast. We can see an immediate drop-off in read performance in the presence of faulting writes, with virtually no reads getting through when we have 48 (!) writes per second. In contrast, 2.0 is able to maintain around 60% of its peak performance at that write level. (Going much beyond 48 faulting writes per second makes things difficult to measure, as you're using a significant percentage of the disk's total capacity just to service your page faults).&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-UL0MRxvhJvY/Tv9vx5vKD2I/AAAAAAAAAm4/4LwRzI5rSq4/s1600/FaultingWrites.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="272" width="400" src="http://3.bp.blogspot.com/-UL0MRxvhJvY/Tv9vx5vKD2I/AAAAAAAAAm4/4LwRzI5rSq4/s400/FaultingWrites.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;p&gt;The global write lock in MongoDB will certainly not be mourned when it's gone, but as I've shown here, its effect on MongoDB's performance is significantly less than you might expect. In the case of non-faulting reads and writes (the working set fits in RAM), the write lock degrades performance only slightly as the number of writes increases, and in the presence of faulting writes, the lock-with-yield approach of 2.0 mitigates most of the performance impact of the occasional faulting write.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-9089049144420251503?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/PdXhXfoyB6Y5yNbptnpCv8HvO_0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PdXhXfoyB6Y5yNbptnpCv8HvO_0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/PdXhXfoyB6Y5yNbptnpCv8HvO_0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PdXhXfoyB6Y5yNbptnpCv8HvO_0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=X4KGjfajQEw:conYn18dRtg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=X4KGjfajQEw:conYn18dRtg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=X4KGjfajQEw:conYn18dRtg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=X4KGjfajQEw:conYn18dRtg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=X4KGjfajQEw:conYn18dRtg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/X4KGjfajQEw" height="1" width="1"/&gt;</content><link rel="enclosure" type="image/png" href="https://sourceforge.net/u/rick446/blog/_discuss/thread/2a7ae045/5285/attachment/FaultingWrites.png" length="0" /><link rel="enclosure" type="image/png" href="https://sourceforge.net/u/rick446/blog/_discuss/thread/2a7ae045/5285/attachment/NonfaultingWrites.png" length="0" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/9089049144420251503/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/12/mongodbs-write-lock.html#comment-form" title="17 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/9089049144420251503?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/9089049144420251503?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/X4KGjfajQEw/mongodbs-write-lock.html" title="MongoDB's Write Lock" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-oxAkPmpZ9VU/Tv9vq-L7klI/AAAAAAAAAms/hGIeMPk7diM/s72-c/NonfaultingWrites.png" height="72" width="72" /><thr:total>17</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/12/mongodbs-write-lock.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkEBRHc6eyp7ImA9WhRTEEo.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-5309032346838518989</id><published>2011-10-31T12:30:00.000-04:00</published><updated>2011-10-31T12:30:55.913-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-31T12:30:55.913-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="10gen" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><title>MongoDB Rocks My World</title><content type="html">I am delighted to announce that I'll be speaking at several MongoDB-related events over the next couple of months. So if you're in or near Dallas, Seattle, or Silicon Valley, I'd love it if you could make it to one of my talks. 10gen (the makers of MongoDB) do a great job of putting on regional conferences that are &lt;b&gt;extremely&lt;/b&gt; reasonably priced (typically $30 student and $50 early bird), and there's usually a nice after-party for each one, so there's no excuse not to go. &lt;br /&gt;
&lt;br /&gt;
So anyway, here are the conferences I'll be speaking at:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;November 17: &lt;a href="http://www.10gen.com/events/mongo-dallas-2011"&gt;MongoDallas&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;December 1: &lt;a href="http://www.10gen.com/events/mongo-seattle-2011"&gt;MongoSeattle&lt;/a&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;December 9: &lt;a href="http://www.10gen.com/events/mongosv-2011"&gt;MongoSV&lt;/a&gt; (Santa Clara, CA)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;I also wanted to say a few words on why I enjoy using MongoDB and why it is such a good fit for a lot of the problems I face...&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h1&gt;MongoDB is Flexible&lt;/h1&gt;You probably expected me to say something about MongoDB being &lt;a href="http://www.youtube.com/watch?v=b2F-DItXtZs"&gt;web-scale&lt;/a&gt;, and while that's true, it's not the biggest reason I use it. What I like the most is that you can organize your data the way you want to without a lot of restrictions forced on you by the DBMS you're using. For those who don't know, MongoDB is what's called a 'document-oriented' database. Rather than storing "rows" in "tables" like you do in a relational database, you store "documents" in "collections." &lt;br /&gt;
&lt;br /&gt;
What is a document? Well, in MongoDB is basically a JSON object. (I'm actually lying. It's technically called BSON because it's a binary format and you get a couple of extra data types for 'free' in MongoDB including datetimes, ObjectIds, and regular expressions, but it's basically JSON.) So if your RDBMS-based application is doing some queries, and &lt;em&gt;every query&lt;/em&gt; has a one-to-many type join (or worse yet you're doing several queries to grab all the data you need to render a page), in MongoDB you would just store all the data you need in a single document, which can be retrieved by a single query. Fast and flexible.&lt;br /&gt;
&lt;br /&gt;
Another way MongoDB is flexible is that it allows you to use ad-hoc queries and indexes. In some NoSQL solutions, you need to define views or map-reduce jobs to get at your data. Rather than forcing you to define all that up-front (or pay a penalty at runtime), MongoDB includes a BSON-based query language and indexes. What this means is that you don't have to clutter your data model with every possible path someone might want to use someday to access your data. For me, MongoDB strikes a sweet spot between explicit query models like Hadoop or CouchDB and a full query compiler like SQL databases. &lt;br /&gt;
&lt;h1&gt;MongoDB is Fast&lt;/h1&gt;Ok, so there is a little bit about "web-scale." MongoDB supports both replication and sharding, which means that it scales really well over several servers. But that's not really what I'm talking about. What I'm talking about it single-server speed. MongoDB uses a nifty little trick (mmap) to map its entire database into memory, where it can treat reads and writes to the database as reads and writes to RAM. For many common operations, this is &lt;em&gt;really fast&lt;/em&gt;. Like tens of thousands of writes per second fast. This means that for a site like SourceForge, which is getting millions of hits a day, we're comfortable serving the majority of requests off a single MongoDB server. And by setting up replication (which we have now) and journalling (which we plan on adding Real Soon Now) with MongoDB, you can be quite safe as well. Replication also means that backups are a snap and don't require our server to go through any downtime.&lt;br /&gt;
&lt;br /&gt;
So great, it's fast in production. But it's also fast (and easy to set up) for development. The MongoDB server is just a single binary application that you can run yourself from the command line, and it keeps all its data in a single directory. This is really nice for a development setup like mine, where I'm running everything needed for an Allura (the SF.net platform) on a VMWare virtual machine. It's just plain simple to set up a single node, and setting up larger systems with replication and sharding isn't *that* much harder.&lt;br /&gt;
&lt;h1&gt;MongoDB is Simple&lt;/h1&gt;While there are many parts of MongoDB that I'm sure took a good bit of complexity in the server to get right (shard migration, journalling, and the new aggregation framework come to mind), the basic ideas are easy to get your mind around. Some of the other NoSQL solutions do some really mind-stretchingly awesome stuff (Cassandra's write scaling and CouchDB's multi-master replication come to mind). MongoDB has some nice and interesting features, as well, but its real strength is that it does what it does really well. It's fast (performance wise and development wise), flexible, stable, and scalable. And because a) the 10gen engineers are awesome and b) it's open source, when there is a feature or technique enough people want/need, MongoDB grows that feature. But historically, it has grown that feature in a way that fits in really well with the existing feature set and doesn't require an enormous cognitive leap to grasp.&lt;br /&gt;
&lt;h1&gt;10gen&lt;/h1&gt;MongoDB's creators are an awesome group of folks. They're throwing conferences every 6 weeks or more frequently &lt;em&gt;somewhere&lt;/em&gt;, they're incredibly responsive to support questions, they're actually &lt;em&gt;really smart&lt;/em&gt;, and they've developed an incredible community around MongoDB. And just for fun, they've decided to single-handedly support the coffee mug industry. Seriously, though, the support they provide for MongoDB is nothing short of stellar, from n00b to Mongo master, and that's probably the biggest reason MongoDB has seen the kind of uptake it has.&lt;br /&gt;
&lt;br /&gt;
So if you haven't ever had the chance to use MongoDB, I'd urge you to grab a &lt;a href="http://www.mongodb.org/downloads"&gt;copy&lt;/a&gt; and start playing around with it. And if you're curious, try to make it out to one of the MongoDB conferences or user groups. All the folks I've met at these are quite friendly and love to help newer users along. So yeah, I guess I'm a MongoDB fanboy. Think I've gone off the deep end? Any other features of MongoDB you love? Please let me know in the comments. I'd love to hear from you!&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-5309032346838518989?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/QAOSHgHS40kBGjGBu7vXRHtzgZk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QAOSHgHS40kBGjGBu7vXRHtzgZk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/QAOSHgHS40kBGjGBu7vXRHtzgZk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/QAOSHgHS40kBGjGBu7vXRHtzgZk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=89EeaqYZGhc:iHUvGnJUQgk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=89EeaqYZGhc:iHUvGnJUQgk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=89EeaqYZGhc:iHUvGnJUQgk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=89EeaqYZGhc:iHUvGnJUQgk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=89EeaqYZGhc:iHUvGnJUQgk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/89EeaqYZGhc" height="1" width="1"/&gt;</content><link rel="related" href="http://www.10gen.com/events" title="MongoDB Rocks My World" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/5309032346838518989/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/10/mongodb-rocks-my-world.html#comment-form" title="13 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5309032346838518989?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5309032346838518989?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/89EeaqYZGhc/mongodb-rocks-my-world.html" title="MongoDB Rocks My World" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>13</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/10/mongodb-rocks-my-world.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEYNR345fCp7ImA9WhdQEE8.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-6623941383940655926</id><published>2011-08-10T21:25:00.003-04:00</published><updated>2011-08-10T21:29:56.024-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-10T21:29:56.024-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="socket.io" /><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="gevent" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><category scheme="http://www.blogger.com/atom/ns#" term="zeromq" /><title>MongoDB Atlanta User Group</title><content type="html">So last night I had the opportunity to speak about Zarkov at the Atlanta MongoDB user group (not to be confused with the MongoATL conference). It was a good crowd, and we had a good time munching on pizza beforehand and talking about MongoDB afterward. Anyway, I did a little realtime demo combining some stuff I've been doing with Socket.io, Zarkov, and Flot and promised I'd make the code available. A mostly-functional version is available at &lt;a href="http://sf.net/u/rick446/mongodb-atl/"&gt;http://sf.net/u/rick446/mongodb-atl/&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
Also, I figured I'd use this post to collect all the presentation paraphernalia from last night, so below are the slides and somewhat poor quality video I recorded. Hope you manage to enjoy it anyway!&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style="width:425px" id="__ss_8818234"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/rick446/realtime-analytics-using-mongodb-python-gevent-and-zeromq" title="Realtime Analytics Using MongoDB, Python, Gevent, and ZeroMQ" target="_blank"&gt;Realtime Analytics Using MongoDB, Python, Gevent, and ZeroMQ&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8818234" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/" target="_blank"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/rick446" target="_blank"&gt;Rick Copeland&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div&gt;&lt;strong&gt;&lt;a href="http://blip.tv/rick-copeland/realtime-analytics-using-mongodb-python-gevent-and-zeromq-5457174"&gt;Realtime Analytics Using MongoDB, Python, Gevent, and ZeroMQ&lt;/strong&gt;&lt;br /&gt;
&lt;embed src="http://blip.tv/play/AYLNinoA" type="application/x-shockwave-flash" width="425" height="338" wmode="transparent" allowscriptaccess="always" allowfullscreen="true" &gt;&lt;/embed&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-6623941383940655926?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4l9PXUQRV_Dpv9oZTfobGFxOqUY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4l9PXUQRV_Dpv9oZTfobGFxOqUY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4l9PXUQRV_Dpv9oZTfobGFxOqUY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4l9PXUQRV_Dpv9oZTfobGFxOqUY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=cAkpZ4MhpQM:hS5M16fKb5g:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=cAkpZ4MhpQM:hS5M16fKb5g:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=cAkpZ4MhpQM:hS5M16fKb5g:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=cAkpZ4MhpQM:hS5M16fKb5g:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=cAkpZ4MhpQM:hS5M16fKb5g:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/cAkpZ4MhpQM" height="1" width="1"/&gt;</content><link rel="related" href="http://www.meetup.com/Atlanta-MongoDB-User-Group/" title="MongoDB Atlanta User Group" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/6623941383940655926/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/08/mongodb-atlanta-user-group.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6623941383940655926?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6623941383940655926?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/cAkpZ4MhpQM/mongodb-atlanta-user-group.html" title="MongoDB Atlanta User Group" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/08/mongodb-atlanta-user-group.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04NQXk9eSp7ImA9WhdRF0k.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-6599315650212761166</id><published>2011-08-07T16:46:00.000-04:00</published><updated>2011-08-07T16:46:30.761-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-07T16:46:30.761-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="socket.io" /><category scheme="http://www.blogger.com/atom/ns#" term="flot" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="gevent" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><category scheme="http://www.blogger.com/atom/ns#" term="zeromq" /><title>WebSockets to Socket.io</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;In a &lt;a href="http://blog.pythonisito.com/2011/07/gevent-zeromq-websockets-and-flot-ftw.html"&gt;previous blog post&lt;/a&gt;, I showed how you can use Gevent, ZeroMQ,  WebSockets, and Flot to create a nice asynchronous server that graphs events in real time to multiple web consumers. Unfortunately, Chrome is just about the only browser that my demo worked in due to some issues with the WebSockets standard. So I've updated the example to use Socket.io, a WebSockets-like Javascript library that will work across multiple browsers.&lt;/p&gt;&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;The best place to start is my last post; you'll need all the libraries I mentioned there. In addition, you need to install gevent-socketio for Socket.io support in gevent.&lt;/p&gt;&lt;h2&gt;Changes from WebSockets server&lt;/h2&gt;&lt;p&gt;The first thing we need to change is we need to create a &lt;code&gt;SocketIOServer&lt;/code&gt; to replace our &lt;code&gt;WSGIServer&lt;/code&gt; with a &lt;code&gt;WebSocketHandler&lt;/code&gt;. While we're at it, we'll go ahead and merge our little static file server with the &lt;code&gt;SocketIOServer&lt;/code&gt;. Our new main method now looks like this:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Set up zmq context and greenlets for all the servers, then launch the web&lt;/span&gt; 
&lt;span class="sd"&gt;    browser and run the data producer'''&lt;/span&gt; 
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
 
    &lt;span class="c"&gt;# zeromq: tcp to inproc gateway&lt;/span&gt; 
    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# WSGI server: copies inproc zmq messages to websocket&lt;/span&gt; 
    &lt;span class="n"&gt;sio_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SocketIOServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;SocketIOApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;socket.io&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# Start the server greenlets&lt;/span&gt; 
    &lt;span class="n"&gt;sio_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="c"&gt;# Open a couple of webbrowsers&lt;/span&gt; 
    &lt;span class="n"&gt;webbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://localhost:8000/graph.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;webbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://localhost:8000/graph.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# Kick off the producer&lt;/span&gt; 
    &lt;span class="n"&gt;zmq_producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let's take a look at our SocketIOApp:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SocketIOApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Funnel messages coming from an inproc zmq socket to the socket.io'''&lt;/span&gt; 
 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paste&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StaticURLParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'PATH_INFO'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/socket.io'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;socketio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'socketio'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'inproc://queue'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
            &lt;span class="n"&gt;socketio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What we've done here is overlay the &lt;code&gt;StaticURLParser&lt;/code&gt; provided by &lt;code&gt;paste&lt;/code&gt; with our own little ZeroMQ-to-Socket.io gateway. So if a request hits any path starting with &lt;code&gt;/socket.io&lt;/code&gt;, it gets routed to our gateway. Otherwise it gets handled as a request for a static resource. We could have had a separate &lt;code&gt;WSGIServer&lt;/code&gt; dedicated to handling static requests, but it seemed simpler (to me) to go ahead and combine them here.&lt;/p&gt;&lt;h2&gt;Client Updates&lt;/h2&gt;&lt;p&gt;Now, for our client, we've made a couple of changes as well. For one, we need to include the &lt;code&gt;socket.io.js&lt;/code&gt; library. I've included the one from the socket.io CDN to simplify development:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;http://cdn.socket.io/stable/socket.io.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In our javascript, I've also made a few changes to handle the Socket.io protocol better as well as make the graph update a little less choppy. First, we'll set up our socket and some basic variables we'll use later:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'localhost'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#placeholder'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;datalen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;series&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
    &lt;span class="p"&gt;};&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to handle the protocol events from &lt;code&gt;socket.io&lt;/code&gt; to update our status:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'connect'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Connected: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt; 
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Error&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt; 
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'disconnect'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Closed&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, we will handle the actual messages coming from the server:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;datalen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="nx"&gt;xaxis&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;timeformat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt; 
                &lt;span class="p"&gt;},&lt;/span&gt; 
                &lt;span class="nx"&gt;yaxis&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; 
                &lt;span class="nx"&gt;hooks&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="nx"&gt;draw&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canvascontext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                        &lt;span class="c1"&gt;// Redraw the graph in 50ms&lt;/span&gt; 
                        &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
                            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setupGrid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
                            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();},&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
                    &lt;span class="p"&gt;}&lt;/span&gt; 
                &lt;span class="p"&gt;}&lt;/span&gt; 
            &lt;span class="p"&gt;});&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What we're doing here is pushing the value from the server on an array, shifting off the old values. Then we set up a hook for &lt;code&gt;Flot&lt;/code&gt; to redraw the graph every 50ms when drawing completes. Altogether, there aren't too many changes from our WebSockets demo, and that demo itself wasn't too complex. If you'd like to see all the code, you can at my &lt;a href="http://sf.net/u/rick446/gevent-socketio/"&gt;SourceForge Repo&lt;/a&gt;. Hope you've enjoed these posts as much as I've enjoyed playing around with these libraries. Happy hacking!&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-6599315650212761166?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/XLeS-yjMouKxG_kky95ZCAoSbSs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XLeS-yjMouKxG_kky95ZCAoSbSs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/XLeS-yjMouKxG_kky95ZCAoSbSs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XLeS-yjMouKxG_kky95ZCAoSbSs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=KVgcHI7g7Vg:7KXXJwYGgOk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=KVgcHI7g7Vg:7KXXJwYGgOk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=KVgcHI7g7Vg:7KXXJwYGgOk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=KVgcHI7g7Vg:7KXXJwYGgOk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=KVgcHI7g7Vg:7KXXJwYGgOk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/KVgcHI7g7Vg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/6599315650212761166/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/08/websockets-to-socketio.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6599315650212761166?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6599315650212761166?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/KVgcHI7g7Vg/websockets-to-socketio.html" title="WebSockets to Socket.io" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/08/websockets-to-socketio.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQFQXg-fCp7ImA9WhdRF0s.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-9060752972438346337</id><published>2011-07-26T15:04:00.001-04:00</published><updated>2011-08-07T21:18:30.654-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-07T21:18:30.654-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><title>Extending Zarkov's Map/Reduce</title><content type="html">&lt;div class="markdown_content"&gt;Since I've been working on &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt;, I've been writing a few&lt;br /&gt;
map/reduce jobs. One of the things I noticed about map/reduce, at&lt;br /&gt;
least as I've implemented it in Zarkov, is that it's pretty&lt;br /&gt;
inefficient if you want t o generate several aggregate views of the&lt;br /&gt;
same data (like say an event stream). In order to meet Zarkov's&lt;br /&gt;
performance requirements without making my operations team&lt;br /&gt;
&lt;em&gt;too&lt;/em&gt; angry, I decided to "extend" map/reduce, as it's&lt;br /&gt;
implemented in Zarkov. I'm not terribly creative, so I called this&lt;br /&gt;
command &lt;code&gt;xmapreduce&lt;/code&gt;. Here' I'll briefly describe&lt;br /&gt;
map/reduce, show the problem with it, and explain the solution&lt;br /&gt;
implemented in Zarkov.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Map/Reduce&lt;/h2&gt;The &lt;a href="http://labs.google.com/papers/mapreduce.html" rel="nofollow"&gt;map/reduce algorithm&lt;/a&gt; is nice because a) it's&lt;br /&gt;
extremely scalable and b) it's pretty easy to grasp the basics. The&lt;br /&gt;
idea is that you write two functions, &lt;code&gt;map&lt;/code&gt; and&lt;br /&gt;
&lt;code&gt;reduce&lt;/code&gt; which will be applied to a large dataset to get&lt;br /&gt;
yourself some interesting data. For our purposes here, I'll&lt;br /&gt;
illustrate using Zarkov map/reduce.&lt;br /&gt;
Let's say you want to see how many timestamped objects in some&lt;br /&gt;
collection exist for each date. Here's how you'd write your Zarkov&lt;br /&gt;
map/reduce functions:&lt;br /&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;The map/reduce framework will take these functions and do&lt;br /&gt;
(basically) the following (but efficiently distributed over several&lt;br /&gt;
workers):&lt;br /&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map_reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="n"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;map_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="n"&gt;map_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itemgetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kv_pairs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;itemgetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt; 
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;kv_pairs&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; 
        &lt;span class="n"&gt;output_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;And that's pretty much all there is to it (conceptually).&lt;br /&gt;
&lt;h2&gt;The Problem with Map/Reduce&lt;/h2&gt;The problem arises when you have several output collections that&lt;br /&gt;
all depend on a single input collection (or even depend on the same&lt;br /&gt;
&lt;em&gt;documents&lt;/em&gt; in the input collection). Map/reduce can get you&lt;br /&gt;
there, bit it'll execute the query once for each output collection,&lt;br /&gt;
and it needs to distribute the data to the &lt;code&gt;map()&lt;/code&gt;&lt;br /&gt;
functions once for every output collection. This isn't too&lt;br /&gt;
efficient.&lt;br /&gt;
Let's extend our example above by tracking the date, month, and&lt;br /&gt;
year of each object. In classic map/reduce, we'd need three&lt;br /&gt;
&lt;code&gt;map&lt;/code&gt; functions:&lt;br /&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map_month&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map_year&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;Now, if we treat these three jobs separately, we get a lot of&lt;br /&gt;
duplicated effort. Particularly, Zarkov must query the input&lt;br /&gt;
collection three times and transfer the data to map workers three&lt;br /&gt;
times. (There's also duplicate serialization/deserialization of the&lt;br /&gt;
input objects, though the Python &lt;code&gt;bson&lt;/code&gt; library is quite&lt;br /&gt;
fast.)&lt;br /&gt;
&lt;h2&gt;XMapReduce to the Rescue&lt;/h2&gt;The solution Zarkov uses for this case is to allow the&lt;br /&gt;
&lt;code&gt;map&lt;/code&gt; function to return a &lt;em&gt;target collection&lt;/em&gt;&lt;br /&gt;
along with key and value. What &lt;code&gt;xmapreduce&lt;/code&gt; does is&lt;br /&gt;
transform the map functions above into one &lt;code&gt;xmap&lt;/code&gt;&lt;br /&gt;
function, and tweaking our &lt;code&gt;reduce&lt;/code&gt; function just a bit&lt;br /&gt;
to take the collection as an input:&lt;br /&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;'by_date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
            &lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;'by_month'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;'by_year'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="s"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
            &lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xreduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;The xmapreduce algorithm is just a slight bit more complex than&lt;br /&gt;
the map/reduce algorithm:&lt;br /&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;xmap_reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="n"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input_collection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;map_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
    &lt;span class="n"&gt;keyfunc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'k'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; 
    &lt;span class="n"&gt;map_results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;keyfunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map_results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyfunc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="p"&gt;])span&amp;gt; 
        &lt;span class="n"&gt;output_db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;Now, we can invoke a single job to calculate all three&lt;br /&gt;
aggregates, with close to a 3x reduction in data transfer and a&lt;br /&gt;
significant speedup. Assuming that a) your jobs &lt;em&gt;can&lt;/em&gt; be&lt;br /&gt;
combined into a single xmapreduce job and b) you find it acceptable&lt;br /&gt;
to code the target collection in your map jobs, xmapreduce should&lt;br /&gt;
give you a significant speedup over classic map/reduce. In our&lt;br /&gt;
case, we started with 12 map/reduce jobs and ended up with 4&lt;br /&gt;
xmapreduce jobs, with a tremendous speedup, but of course your&lt;br /&gt;
mileage may vary. Happy hacking!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-9060752972438346337?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/W7Qm-plcIDf5pnRC6Twzi61XfJA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/W7Qm-plcIDf5pnRC6Twzi61XfJA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/W7Qm-plcIDf5pnRC6Twzi61XfJA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/W7Qm-plcIDf5pnRC6Twzi61XfJA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=4t-fM1387fU:Dd5y9UhvKNU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=4t-fM1387fU:Dd5y9UhvKNU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=4t-fM1387fU:Dd5y9UhvKNU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=4t-fM1387fU:Dd5y9UhvKNU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=4t-fM1387fU:Dd5y9UhvKNU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/4t-fM1387fU" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/zarkov" title="Extending Zarkov's Map/Reduce" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/9060752972438346337/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/extending-zarkovs-mapreduce.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/9060752972438346337?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/9060752972438346337?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/4t-fM1387fU/extending-zarkovs-mapreduce.html" title="Extending Zarkov's Map/Reduce" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/extending-zarkovs-mapreduce.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEENSX07eip7ImA9WhdSFkw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-7427505832143316374</id><published>2011-07-25T12:51:00.000-04:00</published><updated>2011-07-25T12:51:38.302-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-25T12:51:38.302-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="flot" /><category scheme="http://www.blogger.com/atom/ns#" term="aggregation" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="json" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><title>Zarkov is a Web Service</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;Over the past few weeks I've been working on a service in Python that I'm calling, in the tradition of naming projects after characters in Flash Gordon, &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt;. So what exactly is Zarkov? Well, Zarkov is many things (and may grow to more):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;Zarkov is an event logger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2011/07/zarkov-is-lightweight-map-reduce.html"&gt;Zarkov is a lightweight map-reduce framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2011/07/zarkov-is-aggregation-service.html"&gt;Zarkov is an aggregation service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zarkov is a webservice&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In my &lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;first post&lt;/a&gt;, I discussed Zarkov as an event logger. &lt;a href="/2011/07/zarkov-is-lightweight-map-reduce.html"&gt;Next&lt;/a&gt;, I explored Zarkov as a map-reduce framework. &lt;a href="/2011/07/zarkov-is-aggregation-service.html"&gt;After that&lt;/a&gt;, I talked about using Zarkov's aggregation definitions. Today, I'll focus on how you can get those aggregates &lt;em&gt;out&lt;/em&gt; of Zarkov using Zarkov's built-in JSON web service.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Basic usage&lt;/h2&gt;&lt;p&gt;In order to expose Zarkov's webservice, you'll need to add a key to your development.yaml telling it what port to listen on:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="l-Scalar-Plain"&gt;web_port&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;8081&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once that's set up, you need to start the Zarkov server:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml serve
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we need some data to be put into Zarkov. Luckily, Zarkov comes with a 'perftest' command that we can use to fill the data:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml init
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# zcmd perftest &amp;lt;url&amp;gt; &amp;lt;num_events&amp;gt; &amp;lt;concurrency&amp;gt; &amp;lt;rate&amp;gt;&lt;/span&gt; 
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml perftest tcp://localhost:6543 1000 1 100
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml aggregate perftest
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can request data from Zarkov by using simple HTML form requests, receiving the results in JSON:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s1"&gt;'http://127.0.0.1:8081/q?p=perftest.&amp;amp;c=sum_by_second'&lt;/span&gt; 
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v&amp;quot;&lt;/span&gt;: &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;00&amp;quot;&lt;/span&gt;, 100&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;01&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;02&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;03&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;04&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;05&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;06&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;07&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;08&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]]}&lt;/span&gt; 
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s1"&gt;'http://127.0.0.1:8081/q?p=perftest.&amp;amp;c=sum_by_second&amp;amp;b=03&amp;amp;e=07'&lt;/span&gt; 
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v&amp;quot;&lt;/span&gt;: &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;03&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;04&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;05&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;06&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;07&amp;quot;&lt;/span&gt;, 101&lt;span class="o"&gt;]]}&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Integrating with Flot&lt;/h2&gt;&lt;p&gt;The data format returned by the Zarkov web service is a list of (key, value) pairs for every value calculated in the aggregate.  If you compute your aggregates using ISO timestamps as part of the key, Zarkov will helpfully convert these timestamps to &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;-acceptable timestamps for graphing:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s1"&gt;'http://127.0.0.1:8081/q?p=perftest.&amp;amp;c=sum_by_second_ts'&lt;/span&gt; 
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;v&amp;quot;&lt;/span&gt;: &lt;span class="o"&gt;[[&lt;/span&gt;1311626220000.0, 100&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626221000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626222000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626223000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626224000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626225000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626226000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626227000.0, 101&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;1311626228000.0, 101&lt;span class="o"&gt;]]}&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In order to use the automatic timestamp feature, you'll need to make sure that your key ends with an ISO formatted datetime prefixed by a slash ("/") character. You can set the definition of the perftest task in zarkov.helpers to see how this can be done. Also note that you can specify a &lt;code&gt;prefix&lt;/code&gt; argument to your query. This is prepended onto the collection name. If it's not provided, Zarkov will assume you mean the &lt;code&gt;zagg.&lt;/code&gt; prefix.&lt;/p&gt;&lt;p&gt;To demonstrate the flot integration, let's set up a tiny webapp in  /tmp/zarkov. First we'll create the directory structure and grab a copy of flot:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;mkdir /tmp/zarkov
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;mkdir /tmp/zarkov/static
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;pushd&lt;/span&gt; /tmp/zarkov/static
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;wget http://flot.googlecode.com/files/flot-0.7.tar.gz
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;tar xzf flot-0.7.tar.gz
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we'll create an index page in /tmp/zarkov/index.html:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Zarkov Graph Test&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt; 
   &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;placeholder&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width:600px;height:300px&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;flot/jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;flot/jquery.flot.min.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
  &lt;span class="c"&gt;&amp;lt;!--[if lte IE 8]&amp;gt;&amp;lt;script language=&amp;quot;javascript&amp;quot; type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;/static/flot/excanvas.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;![endif]--&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;index.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the corresponding index.js:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'div#placeholder'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;plot_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;xaxes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s1"&gt;'time'&lt;/span&gt; &lt;span class="p"&gt;}]};&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;perftest.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sum_by_second_ts&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; 
    &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="s1"&gt;'/ws/q'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;series&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; 
            &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; 
            &lt;span class="p"&gt;});&lt;/span&gt; 
            &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plot_options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;span class="p"&gt;}());&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here's our little webapp. You'll need to &lt;code&gt;pip install paste&lt;/code&gt; if you don't already have it.&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gevent.pywsgi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WSGIServer&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;paste.urlparser&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StaticURLParser&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;paste.urlmap&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;URLMap&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zarkov.web&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebApp&lt;/span&gt; 
 
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;URLMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'/static'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StaticURLParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/tmp/zarkov/static'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'/ws'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
 
&lt;span class="n"&gt;web_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WSGIServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;web_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serve_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now if we run the webapp with the Zarkov &lt;code&gt;script&lt;/code&gt; command and visit &lt;a href="http://localhost" rel="nofollow"&gt;http://localhost&lt;/a&gt;:8081/static/index.html, we can see a pretty graph:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml script /tmp/zarkov/test.py
&lt;/pre&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hMwQBzdt0JA/Ti2e--1lBgI/AAAAAAAAAlk/cPQumkaHYE8/s1600/flot.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="158" width="320" src="http://4.bp.blogspot.com/-hMwQBzdt0JA/Ti2e--1lBgI/AAAAAAAAAlk/cPQumkaHYE8/s320/flot.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Hope you've enjoyed this brief intro to Zarkov. If you've got any questions, comments, or suggestions please let me know either in the comments, Zarkov's discussion forums, or Zarkov's ticket tracker (all conveniently located at &lt;a href="http://sf.net/p/zarkov"&gt;http://sf.net/p/zarkov&lt;/a&gt;). Happy hacking!&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-7427505832143316374?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/CWSxYUq12BOmxeTFX7LzMb_huKs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CWSxYUq12BOmxeTFX7LzMb_huKs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/CWSxYUq12BOmxeTFX7LzMb_huKs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CWSxYUq12BOmxeTFX7LzMb_huKs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=dcAYsoEpCP0:LvnZBXiJzbE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=dcAYsoEpCP0:LvnZBXiJzbE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=dcAYsoEpCP0:LvnZBXiJzbE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=dcAYsoEpCP0:LvnZBXiJzbE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=dcAYsoEpCP0:LvnZBXiJzbE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/dcAYsoEpCP0" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/zarkov" title="Zarkov is a Web Service" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/7427505832143316374/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/zarkov-is-web-service.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/7427505832143316374?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/7427505832143316374?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/dcAYsoEpCP0/zarkov-is-web-service.html" title="Zarkov is a Web Service" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-hMwQBzdt0JA/Ti2e--1lBgI/AAAAAAAAAlk/cPQumkaHYE8/s72-c/flot.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/zarkov-is-web-service.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIAQHg8fCp7ImA9WhdSFEg.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-8959751758448069625</id><published>2011-07-23T16:17:00.002-04:00</published><updated>2011-07-23T16:22:21.674-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-23T16:22:21.674-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="websockets" /><category scheme="http://www.blogger.com/atom/ns#" term="flot" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="gevent" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><category scheme="http://www.blogger.com/atom/ns#" term="zeromq" /><title>Gevent, ZeroMQ, WebSockets, and Flot FTW!</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;As part of the work I've been doing on &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt; I've had the opportunity to play around with a lot of cool technologies, among which are &lt;a href="http://www.gevent.org"&gt;gevent&lt;/a&gt;, &lt;a href="path=http://www.zeromq.org"&gt;ZeroMQ&lt;/a&gt;,  &lt;a href="http://websocket.org" &gt;WebSockets&lt;/a&gt;, and &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt;. It took a while to get to the point where I could actually get things done, but once I was there, things were incredibly simple. In this post I'll show you how to use these three techologies together to build a simple web app with realtime server push data.&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://dl.dropbox.com/u/24086834/blog/20110723/ss.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="403" width="642" src="http://dl.dropbox.com/u/24086834/blog/20110723/ss.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;&lt;p&gt;Before you can run the example program, you'll need to install the following Python libraries:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;pyzmq&lt;/li&gt;
&lt;li&gt;gevent&lt;/li&gt;
&lt;li&gt;gevent-zeromq&lt;/li&gt;
&lt;li&gt;gevent-websocket&lt;/li&gt;
&lt;li&gt;paste (for a simple WSGI http static file server)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The easiest way to do this is to:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;pyzmq&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt; &lt;span class="n"&gt;gevent_zeromq&lt;/span&gt; &lt;span class="n"&gt;paste&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may see messages about missing system libraries; you'll need to have libzmq-dev 2.1+ and libevent-dev installed to build the Python packages.&lt;/p&gt;&lt;p&gt;You'll also need to download &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; JQuery plugin and untar it in your project directory.&lt;/p&gt;&lt;h2&gt;Greenlets&lt;/h2&gt;&lt;p&gt;One of the things you'll need to understand before running the code is the concept of green threads or greenlets. These behave just like threads except for two caveats:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;they never run concurrently with one another&lt;/li&gt;
&lt;li&gt;they only yield to one another at specified points&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;What this ends up meaning is that they're very lightweight to create compared to threads and you can actually be a little sloppier with shared data between greenlets since you can deterministically know when a greenlet might yield to another greenlet. The gevent library provides our greenlet support, and makes greenlets even more useful by turning blocking system calls into yield points so that another greenlet can run while one is waiting on data.&lt;/p&gt;&lt;h2&gt;ZeroMQ&lt;/h2&gt;&lt;p&gt;ZeroMQ sounds like it's a message queue like RabbitMQ, but it's really not. ZeroMQ is a library that provides many message queue-like features, but requires no broker and very little setup. We'll be using ZeroMQ pub/sub sockets to manage communication in this demo. Pub/sub sockets have the nice feature that published messages get distributed to all subscribers currently connected, or if no subscribers are connected, the message simply gets dropped.&lt;/p&gt;&lt;p&gt;ZeroMQ was designed to work with threads rather than greenlets, so we'll also need to use the excellent gevent_zeromq library to "greenletize" ZeroMQ. For this example, we'll write a loop that pushes (x,y) point values out on a zmq.PUB socket:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zmq_producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Produce a nice time series sine wave'''&lt;/span&gt; 
    &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PUB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tcp://127.0.0.1:5000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
 
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
        &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; 
        &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; being passed in is created in our &lt;code&gt;main()&lt;/code&gt; function with the call &lt;code&gt;zmq.Context()&lt;/code&gt;. The context is just a place for ZeroMQ to store some global state; you generally only create one per application.&lt;/p&gt;&lt;p&gt;OK, now that we have our producer, let's look at the ZeroMQ server. What we'll do here is just relay messages received on an incoming tcp &lt;code&gt;zmq.SUB&lt;/code&gt; socket and publish them on an outgong inproc &lt;code&gt;zmq.PUB&lt;/code&gt; socket:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;zmq_server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Funnel messages coming from the external tcp socket to an inproc socket'''&lt;/span&gt; 
    &lt;span class="n"&gt;sock_incoming&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;sock_outgoing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PUB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;sock_incoming&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tcp://*:5000'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;sock_outgoing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'inproc://queue'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;sock_incoming&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock_incoming&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="n"&gt;sock_outgoing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only thing to note here is that we have to tell ZeroMQ that we want to subscribe to all messages on the &lt;code&gt;zmq.SUB&lt;/code&gt; socket.&lt;/p&gt;&lt;h2&gt;Gevent WSGI Servers&lt;/h2&gt;&lt;p&gt;Gevent provides a &lt;em&gt;fast&lt;/em&gt; implementation of the WSGI standard in the &lt;code&gt;gevent.pywsgi&lt;/code&gt; module. We'll be using two instances of the server (greenlets are lightweight) in this demo. One will simply serve up static files, while the other one will provide our WebSocket connectivity. Here's the code to set up these two servers:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# websocket server: copies inproc zmq messages to websocket&lt;/span&gt; 
&lt;span class="n"&gt;ws_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pywsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;WebSocketApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;handler_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WebSocketHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="c"&gt;# http server: serves up static files&lt;/span&gt; 
&lt;span class="n"&gt;http_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pywsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;paste&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StaticURLParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple; just specify the bind address for the server, the WSGI app to call, and in the case of the WebSocket server, the WebSocket handler class. The &lt;code&gt;http_server&lt;/code&gt; above is mostly uninteresting, as it just serves up static files via paste. The ws_server has a (slightly) more interesting implementation:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebSocketApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Funnel messages coming from an inproc zmq socket to the websocket'''&lt;/span&gt; 
 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; 
 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'wsgi.websocket'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUB&lt;/span&gt;&lt;span class="p"&gt;)/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setsockopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUBSCRIBE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'inproc://queue'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
            &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
            &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What's happening here is that when we get a connection to the websocket address (port 9999 in our example), we do the following:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Subscribe to the inproc socket that our zmq_server is publishing messages to&lt;/li&gt;
&lt;li&gt;Grab the websocket from the &lt;code&gt;environ&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Relay messages from the zmq socket to the websocket&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Client-side&lt;/h2&gt;&lt;p&gt;So the server-side stuff is straightforward; what about the client-side? Well, it's pretty easy as well. Here's the (static) HTML page we'll use:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;ZWS Example&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/flot/jquery.min.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/flot/jquery.flot.min.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;graph.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;ZMQ - WebSocket Example&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;conn_status&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Not Connected&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;placeholder&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;width:600px;height:300px;&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here, all we're doing is pulling in the JQuery and Flot libraries as well as our custom graph.js and setting up a couple of placeholders. The Javascript is also pretty straightforward. I've tried to provide inline commentary:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// Open up a connection to our server&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ws://localhost:9999/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="c1"&gt;// Save our plot placeholder&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;$placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#placeholder'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="c1"&gt;// Maximum # of data points to plot&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;datalen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="c1"&gt;// This will be the plot object&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="c1"&gt;// Set up some options on our data series&lt;/span&gt; 
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;series&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt; 
        &lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; 
    &lt;span class="p"&gt;};&lt;/span&gt; 
    &lt;span class="c1"&gt;// What do we do when we get a message?&lt;/span&gt; 
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
        &lt;span class="c1"&gt;// Keep the data series a manageable length&lt;/span&gt; 
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;datalen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="c1"&gt;// Create the plot if it's not there already&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setupGrid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="c1"&gt;// Update the plot&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$placeholder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="nx"&gt;xaxis&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="nx"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;timeformat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%H:%M:%S&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;minTickSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;second&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
                &lt;span class="p"&gt;},&lt;/span&gt; 
                &lt;span class="nx"&gt;yaxis&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                    &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                    &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; 
                &lt;span class="p"&gt;}&lt;/span&gt; 
            &lt;span class="p"&gt;});&lt;/span&gt; 
            &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="c1"&gt;// Just update our conn_status field with the connection status&lt;/span&gt; 
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Connected&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Error&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onclose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'#conn_status'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;b&amp;gt;Closed&amp;lt;/b&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Putting it all together&lt;/h2&gt;&lt;p&gt;To put everything together, here's the main function I used:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; 
    &lt;span class="sd"&gt;'''Set up zmq context and greenlets for all the servers, then launch the web&lt;/span&gt; 
&lt;span class="sd"&gt;    browser and run the data producer'''&lt;/span&gt; 
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
 
    &lt;span class="c"&gt;# zeromq: tcp to inproc gateway&lt;/span&gt; 
    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq_server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# websocket server: copies inproc zmq messages to websocket&lt;/span&gt; 
    &lt;span class="n"&gt;ws_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pywsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;WebSocketApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;handler_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WebSocketHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# http server: serves up static files&lt;/span&gt; 
    &lt;span class="n"&gt;http_server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pywsgi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSGIServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;paste&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlparser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StaticURLParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; 
    &lt;span class="c"&gt;# Start the server greenlets&lt;/span&gt; 
    &lt;span class="n"&gt;http_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="n"&gt;ws_server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
    &lt;span class="c"&gt;# Open a couple of webbrowsers&lt;/span&gt; 
    &lt;span class="n"&gt;webbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://localhost:8000/graph.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;webbrowser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://localhost:8000/graph.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="c"&gt;# Kick off the producer&lt;/span&gt; 
    &lt;span class="n"&gt;zmq_producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For fun, I threw a couple of webbrowser calls at the end so you can see the data getting distributed to all the clients that connect to our server. If you'd like to see the full program including flot et. al., here's a &lt;a href="http://dl.dropbox.com/u/24086834/blog/20110723/zmq_websocket.tar.gz"&gt;download&lt;/a&gt;. Hope you enjoy playing around with gevent, ZeroMQ, and WebSockets as much as I did!&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-8959751758448069625?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/W1V34FW32PWoJVMYh2AoH3ZsKPg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/W1V34FW32PWoJVMYh2AoH3ZsKPg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/W1V34FW32PWoJVMYh2AoH3ZsKPg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/W1V34FW32PWoJVMYh2AoH3ZsKPg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=83smGt6yEuY:8ehSwFkICa4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=83smGt6yEuY:8ehSwFkICa4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=83smGt6yEuY:8ehSwFkICa4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=83smGt6yEuY:8ehSwFkICa4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=83smGt6yEuY:8ehSwFkICa4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/83smGt6yEuY" height="1" width="1"/&gt;</content><link rel="enclosure" type="image/png" href="http://dl.dropbox.com/u/24086834/blog/20110723/ss.png" length="0" /><link rel="enclosure" type="application/x-gtar-compressed" href="http://dl.dropbox.com/u/24086834/blog/20110723/zmq_websocket.tar.gz" length="0" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/8959751758448069625/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/gevent-zeromq-websockets-and-flot-ftw.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8959751758448069625?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8959751758448069625?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/83smGt6yEuY/gevent-zeromq-websockets-and-flot-ftw.html" title="Gevent, ZeroMQ, WebSockets, and Flot FTW!" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>6</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/gevent-zeromq-websockets-and-flot-ftw.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYMR3wyeSp7ImA9WhdQEEw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-3850474142074933295</id><published>2011-07-22T10:55:00.003-04:00</published><updated>2011-08-10T17:03:06.291-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-10T17:03:06.291-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="mapreduce" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><title>Zarkov is an aggregation service</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;Over the past few weeks I've been working on a service in Python that I'm calling, in the tradition of naming projects after characters in Flash Gordon, &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt;. So what exactly is Zarkov? Well, Zarkov is many things (and may grow to more):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;Zarkov is an event logger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/2011/07/zarkov-is-lightweight-map-reduce.html"&gt;Zarkov is a lightweight map-reduce framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zarkov is an aggregation service&lt;/li&gt;
&lt;li&gt;Zarkov is a webservice&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In my &lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;first post&lt;/a&gt;, I discussed Zarkov as an event logger. &lt;a href="/2011/07/zarkov-is-lightweight-map-reduce.html"&gt;Next&lt;/a&gt;, I explored Zarkov as a map-reduce framework. Today, I'll focus on Zarkov as an aggregation service suitable for doing near real-time analytics.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Aggregation Definitions&lt;/h2&gt;&lt;p&gt;In Zarkov, aggregations are defined in the agg_def collection. Here's the Ming schema for that collection:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="c"&gt;# Aggregations defined over (by default) the 'event' collection&lt;/span&gt; 
&lt;span class="n"&gt;agg_def&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="s"&gt;'agg_def'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doc_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; 
    &lt;span class="c"&gt;# Name for the aggregation&lt;/span&gt; 
    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="c"&gt;# Input collection to aggregate&lt;/span&gt; 
    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'object'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;if_missing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'event'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="c"&gt;# Pointer to python Aggregation subclass&lt;/span&gt; 
    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'task_name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="c"&gt;# Timestamp of last aggregation&lt;/span&gt; 
    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'last_run'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;if_missing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1970&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; 
 
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AggDef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="c"&gt;# some helper methods&lt;/span&gt; 
 
&lt;span class="n"&gt;orm_session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AggDef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agg_def&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;AggDefs basically define an input collection ('object'), a meaningful 'name', and a 'task_name ' to define the logic behind the aggregation.  task_name is actually a dotted name that's suitable for use in Python's import statement that points to a 'Task' subclass. There is also a 'last_run' timestamp that is used to restrict which documents in the object collection (by default zarkov.event) are examined for aggregation. When you request an aggregation from Zarkov, several things happen:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Zarkov loads the AggDef from MongoDB&lt;/li&gt;
&lt;li&gt;Zarkov imports the Task subclass defined in the AggDef&lt;/li&gt;
&lt;li&gt;Zarkov calls the 'run' method of the Task subclass with a REQ socket connected to the Zarkov map-reduce router&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Task Definitions&lt;/h2&gt;&lt;p&gt;Normally, you won't override the 'run' method yourself, instead overriding the _start_jobs method:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_start_jobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'_start_jobs'&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If "full" is true, you'll need to treat this task as a "full recompute" rather than an incremental update. Usually, this just means that you'll be overwriting the output collection rather than reducing your results into it. There are a couple of things that the Task class does for you before calling _start_jobs:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;"New" object documents have been identified. This is done by querying the object collection for documents whose 'aggregates' field does not contain this AggDef's name. In a "full" aggregation, this is done by querying the entire collection. If it is not a "full" aggregation, the query is further restricted to documents that have been inserted into zarkov since the last run of this AggDef.&lt;/li&gt;
&lt;li&gt;"New" object documents have been marked for this  via their 'jobs' field. All the documents that may require processing can thus be queried with  &lt;code&gt;{"jobs":self._lock_id}&lt;/code&gt;, where &lt;code&gt;self&lt;/code&gt; is the Task instance.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In your &lt;code&gt;_start_jobs&lt;/code&gt; method, then, you'll mostly be starting Zarkov map-reduce jobs. The Task parent class provides a &lt;code&gt;start_mr()&lt;/code&gt; method to help with that:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_mr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
             &lt;span class="n"&gt;map_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reduce_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The arguments are similar to the fields in the map-reduce request message covered in the &lt;a href="/p/zarkov/blog/2011/07/zarkov-is-a-map-reduce-framework/"&gt;previous post&lt;/a&gt;. If 'full' is true, the out_mode will be set to 'replace', otherwise it will be set to 'reduce'. &lt;code&gt;start_mr()&lt;/code&gt;, in addition to actually kicking off the map-reduce job, notes the job ID in the &lt;code&gt;Task&lt;/code&gt; instance so that the &lt;code&gt;run()&lt;/code&gt; method can wait until all the started map-reduce jobs are complete before returning.&lt;/p&gt;&lt;p&gt;So how do you actually invoke an aggregation? Let's assume we have the following task definition in a module 'my.tasks':&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zarkov.task&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zarkov&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; 
 
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
 
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_start_jobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc_session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; 
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_mr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zagg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;my_agg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can tell Zarkov about the agg via the following code (probably via &lt;code&gt;zcmd shell&lt;/code&gt; or &lt;code&gt;zcmd script&lt;/code&gt;:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;ming.orm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; 
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zarkov&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; 
 
&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AggDef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'my_agg_def'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'my.tasks.MyTask'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AggDef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Running aggregations&lt;/h2&gt;&lt;p&gt;Now to trigger the aggregation, just make sure your Zarkov server and/or workers are running and execute the following command:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml aggregate my_agg_def
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Wait a bit, and when the command exits, you should hve a brand new collection &lt;code&gt;zagg.my_agg&lt;/code&gt; with the counts you requested. If you run the &lt;code&gt;aggregate&lt;/code&gt; command multiple times, it should reduce only the &lt;em&gt;new&lt;/em&gt; events added since the last aggregation run. And that's all there is to it.&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-3850474142074933295?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3FtpG9ljIPT0xllsaKvFUQY4qj8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3FtpG9ljIPT0xllsaKvFUQY4qj8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3FtpG9ljIPT0xllsaKvFUQY4qj8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3FtpG9ljIPT0xllsaKvFUQY4qj8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=FWxvaSp3Phs:qtWpH4e5WL4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=FWxvaSp3Phs:qtWpH4e5WL4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=FWxvaSp3Phs:qtWpH4e5WL4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=FWxvaSp3Phs:qtWpH4e5WL4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=FWxvaSp3Phs:qtWpH4e5WL4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/FWxvaSp3Phs" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/zarkov" title="Zarkov is an aggregation service" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/3850474142074933295/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/zarkov-is-aggregation-service.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/3850474142074933295?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/3850474142074933295?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/FWxvaSp3Phs/zarkov-is-aggregation-service.html" title="Zarkov is an aggregation service" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/zarkov-is-aggregation-service.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYNRX4-fip7ImA9WhdQEEw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-5340976074015546667</id><published>2011-07-21T10:10:00.005-04:00</published><updated>2011-08-10T17:03:14.056-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-10T17:03:14.056-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="mapreduce" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><title>Zarkov is a Lightweight Map-Reduce Framework</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;Over the past few weeks I've been working on a service in Python that I'm calling, in the tradition of naming projects after characters in Flash Gordon, &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt;. So what exactly is Zarkov? Well, Zarkov is many things (and may grow to more):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;Zarkov is an event logger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Zarkov is a lightweight map-reduce framework&lt;/li&gt;
&lt;li&gt;Zarkov is an aggregation service&lt;/li&gt;
&lt;li&gt;Zarkov is a webservice&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In my &lt;a href="/2011/07/zarkov-is-event-logger.html"&gt;previous post&lt;/a&gt;, I discussed Zarkov as an event logger. While this may be useful (say for logging to a central location from several different servers), there's a bit more to Zarkov. Today I'll focus on the map-reduce framework provided by Zarkov. If you want instructions on setting up Zarkov or getting events into it, please see my previous post.&lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;Why Another Map-Reduce Framework?&lt;/h2&gt;&lt;p&gt;Good question. Currently there exist two big frameworks of which I'm aware: &lt;a href="/nf/redirect/?path=http%3A//hadoop.apache.org/" rel="nofollow"&gt;Hadoop&lt;/a&gt; and &lt;a href="/nf/redirect/?path=http%3A//discoproject.org/" rel="nofollow"&gt;Disco&lt;/a&gt;. Hadoop is a framework written in Java that includes all sorts of goodness to make sure that your larger-than-life job gets executed relatively efficiently on a large cluster. Disco has the same kinds of things. Both of them require a good bit of setup to get running. Both of them provide a distributed filesystem that they &lt;em&gt;really&lt;/em&gt; like to get jobs from. Neither has tight MongoDB integration.&lt;/p&gt;&lt;p&gt;I wanted something smaller. Something &lt;em&gt;easy&lt;/em&gt; to configure, with tight MongoDB integration. "But wait," I hear you say, "MongoDB has built-in map-reduce!" In fact, Zarkov began life as a front-end for MongoDB's own map-reduce. Unfortunately, there are a few problems with the built-in mapreduce command in MongoDB:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;it saves its intermediate results in MongoDB collections (lots of locking)&lt;/li&gt;
&lt;li&gt;it is restricted to a single thread per mongod process (thanks a lot, Spidermonkey :-p )&lt;/li&gt;
&lt;li&gt;it doesn't allow scaling compute power independent of shards&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;So I set out to write my own....&lt;/p&gt;&lt;h2&gt;Setting up a single-node Zarkov map-reduce (zmr) cluster&lt;/h2&gt;&lt;p&gt;This is actually pretty simple. This is how you do it:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# Create the default directory in which zmr stores its temp files&lt;/span&gt; 
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;mkdir /tmp/zmr
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml serve
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yes, that's the same command as the event logger. You specify in the development.yaml file how many worker processes you want to keep "locally", and these will start up automatically with your router. For more complex setups, or to run the zmr framework without the Zarkov event logger, you can do the following:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# Start &amp;quot;router&amp;quot; -- this will also start local worker procs&lt;/span&gt; 
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml zmr-router &amp;amp;
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then on your worker nodes:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd zmr-worker tcp://worker-ip:5555 &amp;amp;
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that there's actually no need to specify the config file for the worker process; workers will connect to the router to retrieve their configuration. You do want to make sure that you start all your workers before you start your job in order to correctly load-balance between them.&lt;/p&gt;&lt;h2&gt;Running a job on a zmr cluster&lt;/h2&gt;&lt;p&gt;Running a job is actually pretty simple. You need to connect to the zmr request port (by default this is 5555) on the router using a ZeroMQ REQ socket and submit a BSON message of the following format:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;database: what MongoDB database to read/write to&lt;/li&gt;
&lt;li&gt;collection: what collection forms the input to the job?&lt;/li&gt;
&lt;li&gt;query: a bson.Binary() containing a BSON-encoded query object&lt;/li&gt;
&lt;li&gt;map: python source code string containing a map function&lt;/li&gt;
&lt;li&gt;reduce: python source code string containing a reduce function&lt;/li&gt;
&lt;li&gt;map_name: name of the function in the "map" param&lt;/li&gt;
&lt;li&gt;reduce_name: name of the function in the "reduce" param&lt;/li&gt;
&lt;li&gt;out: collection in which to store the result of the job&lt;/li&gt;
&lt;li&gt;out_type: replace, merge, or reduce, corresponding to the MongoDB mapreduce command options&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The map function has the following format:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;do_something_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;new_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Basically, it's a generator that takes a sequence of dicts and yields zero or more (key, dict) pairs. The reduce function has the following format:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;one_value_from_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So for instance, if we wanted to count the number of objects, grouped by the field 'foo', you could do the following:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
 
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result would be a collection with its objects' _id fields equal to the 'foo' values from the input collection and its 'value' fields equal to the number of objects with that 'foo' value.&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-5340976074015546667?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dLKk4EmULK1nTOtkK2WV3FLtks4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dLKk4EmULK1nTOtkK2WV3FLtks4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dLKk4EmULK1nTOtkK2WV3FLtks4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dLKk4EmULK1nTOtkK2WV3FLtks4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hI7CsaCfNa8:w8UQliSfG_E:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hI7CsaCfNa8:w8UQliSfG_E:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hI7CsaCfNa8:w8UQliSfG_E:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hI7CsaCfNa8:w8UQliSfG_E:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hI7CsaCfNa8:w8UQliSfG_E:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/hI7CsaCfNa8" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/zarkov" title="Zarkov is a Lightweight Map-Reduce Framework" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/5340976074015546667/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/zarkov-is-lightweight-map-reduce.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5340976074015546667?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5340976074015546667?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/hI7CsaCfNa8/zarkov-is-lightweight-map-reduce.html" title="Zarkov is a Lightweight Map-Reduce Framework" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/zarkov-is-lightweight-map-reduce.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IHSHc5fCp7ImA9WhdQEE0.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-4525065332453128917</id><published>2011-07-20T15:29:00.003-04:00</published><updated>2011-08-10T16:52:19.924-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-10T16:52:19.924-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="zarkov" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><title>Zarkov is an event logger</title><content type="html">&lt;div class="markdown_content"&gt;&lt;p&gt;Over the past few weeks I've been working on a service in Python that I'm calling, in the tradition of naming projects after characters in Flash Gordon, &lt;a href="http://sf.net/p/zarkov"&gt;Zarkov&lt;/a&gt;. So what exactly is Zarkov? Well, Zarkov is many things (and may grow to more):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Zarkov is an event logger&lt;/li&gt;
&lt;li&gt;Zarkov is a lightweight map-reduce framework&lt;/li&gt;
&lt;li&gt;Zarkov is an aggregation service&lt;/li&gt;
&lt;li&gt;Zarkov is a webservice&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In the next few posts, I'll be going over each of the components of Zarkov and how they work together. Today, I'll focus on Zarkov as an event logger. &lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;Technologies&lt;/h2&gt;&lt;p&gt;So there are just a few prerequisite technologies you should know something about before working with Zarkov. I'll give a brief overview of these here.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;ZeroMQ: ZeroMQ is used for Zarkov's wire and buffering protocol all over the place. Generally you'll use PUSH sockets to send data and events to Zarkov, and REQ sockets to talk to the Zarkov map-reduce router.&lt;/li&gt;
&lt;li&gt;MongoDB: Zarkov uses MongoDB to store events and aggregates, so you should have a MongoDB server handy if you'll be doing anything with Zarkov. We also use Ming, an object-document mapper developed at SourceForge, to do most of our interfacing with MongoDB.&lt;/li&gt;
&lt;li&gt;Gevent: Internally, Zarkov uses gevent's "green threads" to keep things nice and lightweight. If you're just &lt;em&gt;using&lt;/em&gt; Zarkov, you probably don't need to know a lot about gevent, but if you start hacking on the source code, it's all over the place (as well as special ZeroMQ and Ming adapters for gevent). So it's probably good to have at least a passing familiarity.&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Installation&lt;/h2&gt;&lt;p&gt;In order to install Zarkov, you'll need to be able to install ZeroMQ and gevent, which probably means installing the zeromq and libevent development libs. In Ubuntu, I had to install zeromq2-1 from source (which isn't too tough):&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://download.zeromq.org/zeromq-2.1.7.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;tar xzf zeromq-2.1.7.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;zeromq-2.1.7
&lt;span class="nv"&gt;$ &lt;/span&gt;./configure --prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/local &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo make install
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# if you're on ubuntu, this next line will work&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt; sudo apt-get install libevent-dev
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# otherwise you need to&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;wget http://monkey.org/~provos/libevent-1.4.13-stable.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;tar xzf libevent-1.4.13-stable.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;libevent-1.4.13-stable
&lt;span class="nv"&gt;$ &lt;/span&gt;./configure --prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/local &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make
&lt;span class="nv"&gt;$ &lt;/span&gt;sudo make install
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you should be able to do a regular pip install to get everything else:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;virtualenv zarkov
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source &lt;/span&gt;zarkov/bin/activate
&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;pip install Zarkov
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, you should customize your development.yaml file. Here's a convenient example we use in testing:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="l-Scalar-Plain"&gt;bson_bind_address&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;tcp://0.0.0.0:6543&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;json_bind_address&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;tcp://0.0.0.0:6544&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;web_port&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;8081&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;backdoor&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;127.0.0.1:6545&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;mongo_uri&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;mongodb://localhost:27017&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;mongo_database&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;zarkov&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;verbose&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;true&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;incremental&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;0&lt;/span&gt; 
&lt;span class="l-Scalar-Plain"&gt;zmr&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;req_uri&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;tcp://127.0.0.1:5555&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;req_bind&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;tcp://0.0.0.0:5555&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;worker_uri&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;tcp://0.0.0.0&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;local_workers&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;2&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;job_root&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;/tmp/zmr&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;map_page_size&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;250000000&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;map_job_size&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;10000&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;outstanding_maps&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;16&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;outstanding_reduces&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;16&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;request_greenlets&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;16&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;compress&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# compression level&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;src_port&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# choose a random port&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;sink_port&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;# choose a random port&lt;/span&gt; 
        &lt;span class="l-Scalar-Plain"&gt;processes_per_worker&lt;/span&gt;&lt;span class="p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l-Scalar-Plain"&gt;null&lt;/span&gt; &lt;span class="c1"&gt;# default == # of cpus&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Zarkov defines a format for an event stream which tries to be fairly generic (though our main use-case is logging SourceForge events for later aggregation). A Zarkov event is a BSON object containing the following data:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;timestamp (datetime) : when did the event occur?&lt;/li&gt;
&lt;li&gt;type (str): what is the type of event?&lt;/li&gt;
&lt;li&gt;context (object): in what context did the event occur? On SourceForge, this includes the project context, the user logged in, the IP address, etc.&lt;/li&gt;
&lt;li&gt;extra (whatever): this is purely up to the event generator. It might be a string, integer, object, array, whatever. (It should be supported by BSON, of course.)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The Zarkov events are stored in a MongoDB database (again with the Flash Gordon references). Assuming you've already installed Zarkov, to run the server you'd execute the following command::&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml serve
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to test, you can use the file zsend.py (included with Zarkov) to send a message to the server::&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{&amp;quot;type&amp;quot;:&amp;quot;nop&amp;quot;}'&lt;/span&gt; | zsend.py tcp://localhost:6543
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To confirm it got there, you can use the 'shell' subcommand from zcmd:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="o"&gt;(&lt;/span&gt;zarkov&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$ &lt;/span&gt;zcmd -y development.yaml shell
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, in the shell you're given, execute the following commands:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ZM&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;'_id'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'4e2723eeb240217416000001'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'aggregates'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'context'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; 
  &lt;span class="s"&gt;'jobs'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;272000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;u'nop'&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Your _id value will probably be different). To actually &lt;em&gt;use&lt;/em&gt; Zarkov as an event logger, you'll probably want to actually send the ZeroMQ messages yourself. Zarkov includes a client to do just that. From the zcmd shell:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;zarkov&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; 
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZarkovClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tcp://localhost:6543'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'nop'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'sample_context_key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'sample_context_val'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; 
&lt;span class="n"&gt;In&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;ZM&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
&lt;span class="n"&gt;Out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; 
&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;'_id'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'4e2723eeb240217416000001'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'aggregates'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'context'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; 
  &lt;span class="s"&gt;'jobs'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;272000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;u'nop'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'_id'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'4e2725a8b240217483000001'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'aggregates'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'context'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;u'sample_context_key'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;u'sample_context_val'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
  &lt;span class="s"&gt;'extra'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="s"&gt;'jobs'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; 
  &lt;span class="s"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2011&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;756000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;u'nop'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to customize things further, the ZarkovClient code is actualy quite&lt;br /&gt;
short:&lt;/p&gt;&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span class="s"&gt;'''Python client for zarkov.'''&lt;/span&gt; 
&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt; 
 
&lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;bson&lt;/span&gt; 
 
&lt;span class="n"&gt;class&lt;/span&gt; &lt;span class="n"&gt;ZarkovClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
 
    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zmq&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PUSH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
 
    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
            &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
 
    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;event_noval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;zarkov&lt;/span&gt; &lt;span class="nb"&gt;import&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; 
        &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
                &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
        &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'$command'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'event_noval'&lt;/span&gt; 
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
 
    &lt;span class="n"&gt;def&lt;/span&gt; &lt;span class="n"&gt;_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; 
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'$command'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; 
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-4525065332453128917?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dfsQkesO2tSStncGKZGyQFAHrsc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dfsQkesO2tSStncGKZGyQFAHrsc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dfsQkesO2tSStncGKZGyQFAHrsc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dfsQkesO2tSStncGKZGyQFAHrsc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=GCWcsp1obJQ:dPB0rvhVTKE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=GCWcsp1obJQ:dPB0rvhVTKE:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=GCWcsp1obJQ:dPB0rvhVTKE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=GCWcsp1obJQ:dPB0rvhVTKE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=GCWcsp1obJQ:dPB0rvhVTKE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/GCWcsp1obJQ" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/zarkov" title="Zarkov is an event logger" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/4525065332453128917/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/07/zarkov-is-event-logger.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/4525065332453128917?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/4525065332453128917?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/GCWcsp1obJQ/zarkov-is-event-logger.html" title="Zarkov is an event logger" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/07/zarkov-is-event-logger.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0QNQ3k8eyp7ImA9WhZaEks.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-82768176396886943</id><published>2011-06-28T08:49:00.000-04:00</published><updated>2011-06-28T08:49:52.773-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-06-28T08:49:52.773-04:00</app:edited><title>MongoDC: Rapid, Scalable Web Development with MongoDB, Ming, and Python</title><content type="html">Here are the slides from my #mongodc talk for those interested....&lt;br /&gt;
&lt;br /&gt;
&lt;div style="width:425px" id="__ss_8441476"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/rick446/rapid-scalable-web-development-with-mongodb-ming-and-python" title="Rapid, Scalable Web Development with MongoDB, Ming, and Python"&gt;Rapid, Scalable Web Development with MongoDB, Ming, and Python&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8441476" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/rick446"&gt;Rick Copeland&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-82768176396886943?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/anQwvdNGujt6BuBnvdcWrcmjnwA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/anQwvdNGujt6BuBnvdcWrcmjnwA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/anQwvdNGujt6BuBnvdcWrcmjnwA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/anQwvdNGujt6BuBnvdcWrcmjnwA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=vcw5lAfpXGg:B_RxmFpc55A:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=vcw5lAfpXGg:B_RxmFpc55A:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=vcw5lAfpXGg:B_RxmFpc55A:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=vcw5lAfpXGg:B_RxmFpc55A:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=vcw5lAfpXGg:B_RxmFpc55A:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/vcw5lAfpXGg" height="1" width="1"/&gt;</content><link rel="related" href="http://www.slideshare.net/rick446/rapid-scalable-web-development-with-mongodb-ming-and-python" title="MongoDC: Rapid, Scalable Web Development with MongoDB, Ming, and Python" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/82768176396886943/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/06/mongodc-rapid-scalable-web-development.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/82768176396886943?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/82768176396886943?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/vcw5lAfpXGg/mongodc-rapid-scalable-web-development.html" title="MongoDC: Rapid, Scalable Web Development with MongoDB, Ming, and Python" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/06/mongodc-rapid-scalable-web-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEEQH07fSp7ImA9WhZVE0k.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-196027621949997355</id><published>2011-05-25T13:30:00.000-04:00</published><updated>2011-05-25T13:30:01.305-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-25T13:30:01.305-04:00</app:edited><title>MongoSF Slides</title><content type="html">Here are the slides from my talk at MongoSF about Allura and how we're using MongoDB to build the next generation SourceForge developer tools. Enjoy!&lt;br /&gt;
&lt;br /&gt;
&lt;div style="width:425px" id="__ss_8088860"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/rick446/allura-an-open-source-mongodb-based-document-oriented-sourceforge" title="Allura - an Open Source MongoDB Based Document Oriented SourceForge"&gt;Allura - an Open Source MongoDB Based Document Oriented SourceForge&lt;/a&gt;&lt;/strong&gt; &lt;iframe src="http://www.slideshare.net/slideshow/embed_code/8088860" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"&gt;&lt;/iframe&gt; &lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/rick446"&gt;Rick Copeland&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-196027621949997355?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/uzBkWhktz2PNS_4N6sN22D3jYr0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uzBkWhktz2PNS_4N6sN22D3jYr0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/uzBkWhktz2PNS_4N6sN22D3jYr0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/uzBkWhktz2PNS_4N6sN22D3jYr0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=ccIlZV4A2I8:apAfUzUgFfc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=ccIlZV4A2I8:apAfUzUgFfc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=ccIlZV4A2I8:apAfUzUgFfc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=ccIlZV4A2I8:apAfUzUgFfc:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=ccIlZV4A2I8:apAfUzUgFfc:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/ccIlZV4A2I8" height="1" width="1"/&gt;</content><link rel="related" href="http://www.slideshare.net/rick446/allura-an-open-source-mongodb-based-document-oriented-sourceforge" title="MongoSF Slides" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/196027621949997355/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/05/mongosf-slides.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/196027621949997355?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/196027621949997355?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/ccIlZV4A2I8/mongosf-slides.html" title="MongoSF Slides" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/05/mongosf-slides.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ABQX0_eCp7ImA9WhZSFkg.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-508588634114514652</id><published>2011-03-31T20:57:00.005-04:00</published><updated>2011-04-01T08:09:10.340-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-01T08:09:10.340-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sourceforge" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Allura Sprint-orials</title><content type="html">So with our initial announcement at PyCon and the sprints afterwards, the newly open source SourceForge project hosting platform &lt;a href="http://sf.net/p/allura/"&gt;Allura&lt;/a&gt; has gotten a bit of attention.  One thing we want to do at SourceForge is to be responsive and open to the community, to make Allura much more than a "throw it over the wall" open source project.  To that end, we've been looking at holding a sprint-orial series.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
So what exactly is a "sprint-orial"? It's kind of like a sprint and a tutorial combined.  If you don't feel like you know enough to contribute to an Allura sprint, you're welcome to attend to get help setting up Allura or learning where to start hacking on it (the "tutorial" aspect). If you want to start contributing to the project immediately, it's a chance to get together with other contributors in a focused setting.  We find that we get a lot more done and have a lot more fun when we're all in a room together (even if it's just an IRC channel). If you're somewhere in between, you're also welcome to come and learn or contribute as your desire and expertise direct you. If you attend , you'll have access to SourceForge developers for answers to questions, help installing, or direction in ways to contribute.&lt;br /&gt;
&lt;br /&gt;
So that's what a sprint-orial is. What we &lt;span style="font-style: italic;"&gt;don't&lt;/span&gt; know is when and where to do the sprinting. My first thought is that we could do monthly Saturday sprint/tutorials. I can personally commit to this level of involvement. I live north of Atlanta and would be happy to host the sprinting, but I'm also willing to travel within reason, and SourceForge has tentatively (and quite generously) offered to foot the bill for my travel.  So if there are groups of Allura hackers who would like a visit, we can schedule one or more of the sprints for your neck of the woods.&lt;br /&gt;
&lt;br /&gt;
So what happens if the sprint isn't in your neck of the woods? Well, the good news is that you can still join us in IRC in &lt;a href="irc://freenode.net/allura"&gt;#allura on FreeNode&lt;/a&gt; during the sprint (or most other times as well, we usually have quite a few SF.net engineers hanging around in there on weekdays).&lt;br /&gt;
&lt;br /&gt;
So what do you think? Anyone out there interested in a sprint-orial? Please let me know in the comments, or on twitter at &lt;a href="http://twitter.com/rick446"&gt;@rick446&lt;/a&gt;. Thanks!&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-508588634114514652?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/CkBVSxsq9XdLMEcdv-HwnJQr__M/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CkBVSxsq9XdLMEcdv-HwnJQr__M/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/CkBVSxsq9XdLMEcdv-HwnJQr__M/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/CkBVSxsq9XdLMEcdv-HwnJQr__M/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=xvY95tQTb8w:lAflwc0J1eA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=xvY95tQTb8w:lAflwc0J1eA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=xvY95tQTb8w:lAflwc0J1eA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=xvY95tQTb8w:lAflwc0J1eA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=xvY95tQTb8w:lAflwc0J1eA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/xvY95tQTb8w" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/allura/" title="Allura Sprint-orials" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/508588634114514652/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/03/allura-sprint-orials.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/508588634114514652?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/508588634114514652?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/xvY95tQTb8w/allura-sprint-orials.html" title="Allura Sprint-orials" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/03/allura-sprint-orials.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UGRn84eyp7ImA9WhZSFkw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-8170132315486030587</id><published>2011-03-09T14:53:00.004-05:00</published><updated>2011-03-31T20:53:47.133-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-31T20:53:47.133-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="sourceforge" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="pycon" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Allura, the Open Source Forge</title><content type="html">So in kind of a soft launch, SourceForge released the project I've been working on (&lt;a href="http://sf.net/p/allura"&gt;Allura&lt;/a&gt;) under the Apache license last month.  The project happens to be the actual software underlying the SourceForge.net 'beta' tools, and we're hoping to get lots of community involvement, starting at PyCon.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;First off, Mark Ramm will be presenting a couple of talks, one focused on how we're using Python at Sourceforge (&lt;a href="http://us.pycon.org/2011/schedule/presentations/150/"&gt;Scaling Python Past 100&lt;/a&gt;).  I will also be presenting a poster at the poster session on Sunday (&lt;a href="http://us.pycon.org/2011/schedule/presentations/276/"&gt;Using the Allura Platform to Create Your Own Forge&lt;/a&gt;).  While this is nice and all, the thing that has me really excited is that we'll be hosting a &lt;a href="http://us.pycon.org/2011/sprints/projects/"&gt;sprint&lt;/a&gt; on the Allura platform next Monday through Thursday where we'd love to have people come and help SourceForge to become the forge you'd like it to be.&lt;br /&gt;&lt;br /&gt;If you want to check out Allura, feel free to &lt;a href="https://sourceforge.net/p/add_project"&gt;register&lt;/a&gt; a SourceForge Beta project and play around with it.  Or you can just fork the Allura &lt;a href="http://sourceforge.net/p/allura/git/"&gt;repository&lt;/a&gt; into your own user project area on SourceForge.  Or feel free to clone it locally and play around with it.  We tie together some cool technologies that I'll be blogging about in future articles, including &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt;, &lt;a href="http://lucene.apache.org/solr/"&gt;Solr&lt;/a&gt;, and &lt;a href="http://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;, to give you a unified set of tools (right now including Repositories, Wiki, Tracker, and Forums) to manage your project.&lt;br /&gt;&lt;br /&gt;So if you're interested in contributing, or you're just curious about Allura and want to learn more, please come by our sprints.  We'll probably also host an open spaces Allura/SourceForge BOF during the conference proper.  As always, feedback is welcome and encouraged, and we'll try to be as responsive as possible (most of our engineers will actually be at PyCon this year).  See you there!&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-8170132315486030587?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/4vyXEgL-Jg5Fxxe1_sex4dM5DPk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4vyXEgL-Jg5Fxxe1_sex4dM5DPk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/4vyXEgL-Jg5Fxxe1_sex4dM5DPk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/4vyXEgL-Jg5Fxxe1_sex4dM5DPk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=VCBwsj2lwYU:HkrB9YtYlzY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=VCBwsj2lwYU:HkrB9YtYlzY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=VCBwsj2lwYU:HkrB9YtYlzY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=VCBwsj2lwYU:HkrB9YtYlzY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=VCBwsj2lwYU:HkrB9YtYlzY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/VCBwsj2lwYU" height="1" width="1"/&gt;</content><link rel="related" href="http://sf.net/p/allura" title="Allura, the Open Source Forge" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/8170132315486030587/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/03/allura-open-source-forge.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8170132315486030587?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8170132315486030587?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/VCBwsj2lwYU/allura-open-source-forge.html" title="Allura, the Open Source Forge" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>7</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/03/allura-open-source-forge.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UMRXk9fip7ImA9Wx9bFE0.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-7336501124194492185</id><published>2011-02-22T15:05:00.002-05:00</published><updated>2011-02-22T15:08:04.766-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-22T15:08:04.766-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><title>MongoATL Video</title><content type="html">Here's the video from my talk at MongoATL a few weeks ago.  Enjoy!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.10gen.com/video/mongoatlanta2011/sourceforge"&gt;http://www.10gen.com/video/mongoatlanta2011/sourceforge&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-7336501124194492185?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/82cQvGnHny23y4AZXXPsx1wYqzY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/82cQvGnHny23y4AZXXPsx1wYqzY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/82cQvGnHny23y4AZXXPsx1wYqzY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/82cQvGnHny23y4AZXXPsx1wYqzY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=WX0V8o0fK4k:Eek2RhO2YwA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=WX0V8o0fK4k:Eek2RhO2YwA:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=WX0V8o0fK4k:Eek2RhO2YwA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=WX0V8o0fK4k:Eek2RhO2YwA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=WX0V8o0fK4k:Eek2RhO2YwA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/WX0V8o0fK4k" height="1" width="1"/&gt;</content><link rel="related" href="http://www.10gen.com/video/mongoatlanta2011/sourceforge" title="MongoATL Video" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/7336501124194492185/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/02/mongoatl-video.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/7336501124194492185?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/7336501124194492185?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/WX0V8o0fK4k/mongoatl-video.html" title="MongoATL Video" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/02/mongoatl-video.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU4NSHk7cSp7ImA9Wx9UEUQ.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-5643554004479555079</id><published>2011-02-08T15:41:00.003-05:00</published><updated>2011-02-08T15:46:39.709-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-08T15:46:39.709-05:00</app:edited><title>MongoATL Slides</title><content type="html">Here are the slides from my &lt;a href="http://www.10gen.com/conferences/mongoatlanta2011"&gt;MongoATL&lt;/a&gt; presentation for those who might be interested.&lt;br /&gt;&lt;br /&gt;&lt;div style="width:425px" id="__ss_6854274"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/rick446/mongo-atl" title="MongoATL: How Sourceforge is Using MongoDB"&gt;MongoATL: How Sourceforge is Using MongoDB&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse6854274" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongoatl-110208143555-phpapp02&amp;stripped_title=mongo-atl&amp;userName=rick446" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse6854274" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mongoatl-110208143555-phpapp02&amp;stripped_title=mongo-atl&amp;userName=rick446" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/rick446"&gt;Rick Copeland&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-5643554004479555079?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/bJfeGvrifzVVO1_G2ZyjRb9-ScA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/bJfeGvrifzVVO1_G2ZyjRb9-ScA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/bJfeGvrifzVVO1_G2ZyjRb9-ScA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/bJfeGvrifzVVO1_G2ZyjRb9-ScA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=Fk8ssf2INLc:pCRoWXdJwT8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=Fk8ssf2INLc:pCRoWXdJwT8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=Fk8ssf2INLc:pCRoWXdJwT8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=Fk8ssf2INLc:pCRoWXdJwT8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=Fk8ssf2INLc:pCRoWXdJwT8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/Fk8ssf2INLc" height="1" width="1"/&gt;</content><link rel="related" href="http://www.slideshare.net/rick446/mongo-atl" title="MongoATL Slides" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/5643554004479555079/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/02/mongoatl-slides.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5643554004479555079?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5643554004479555079?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/Fk8ssf2INLc/mongoatl-slides.html" title="MongoATL Slides" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/02/mongoatl-slides.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUQGR3c6eip7ImA9Wx9VF0s.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-6359101418753010354</id><published>2011-02-03T14:56:00.003-05:00</published><updated>2011-02-03T15:02:06.912-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-03T15:02:06.912-05:00</app:edited><title>MongoATL!</title><content type="html">&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_nBbh7fCcv1w/TUsJamhx-8I/AAAAAAAAAi0/yNZEnJOZFGY/s1600/mongoAtlanta_badge_blank.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 303px; height: 96px;" src="http://1.bp.blogspot.com/_nBbh7fCcv1w/TUsJamhx-8I/AAAAAAAAAi0/yNZEnJOZFGY/s320/mongoAtlanta_badge_blank.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5569555716797234114" /&gt;&lt;/a&gt;&lt;br /&gt;Quick note to let anyone in the ATL area know that I'll be speaking on how SourceForge is using MongoDB at &lt;a href="http://www.10gen.com/conferences/mongoatlanta2011"&gt;MongoATL&lt;/a&gt; on February 8th (next Tuesday).  The registration cost is only $100 ($30 if you're a student), and it's a great way to learn about one of the most popular NoSQL databases on the scene today.&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-6359101418753010354?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/V9qH1MXyr5WZWgOUTwNJWsO45XU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/V9qH1MXyr5WZWgOUTwNJWsO45XU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/V9qH1MXyr5WZWgOUTwNJWsO45XU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/V9qH1MXyr5WZWgOUTwNJWsO45XU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=DWgCQg8xoYM:U89WkE0RQr8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=DWgCQg8xoYM:U89WkE0RQr8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=DWgCQg8xoYM:U89WkE0RQr8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=DWgCQg8xoYM:U89WkE0RQr8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=DWgCQg8xoYM:U89WkE0RQr8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/DWgCQg8xoYM" height="1" width="1"/&gt;</content><link rel="related" href="http://www.10gen.com/conferences/mongoatlanta2011" title="MongoATL!" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/6359101418753010354/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2011/02/mongoatl.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6359101418753010354?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6359101418753010354?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/DWgCQg8xoYM/mongoatl.html" title="MongoATL!" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_nBbh7fCcv1w/TUsJamhx-8I/AAAAAAAAAi0/yNZEnJOZFGY/s72-c/mongoAtlanta_badge_blank.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2011/02/mongoatl.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UDQH4_eyp7ImA9WhZSFkw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-5443983019546565997</id><published>2010-03-23T18:29:00.007-04:00</published><updated>2011-03-31T20:54:31.043-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-31T20:54:31.043-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="grok" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><category scheme="http://www.blogger.com/atom/ns#" term="zope" /><title>Review: Grok 1.0 Web Development</title><content type="html">&lt;p&gt;Those who know me technically know that I'm big into the TurboGears Python web development framework.  Despite my affinity for al things TurboGearish, it's often useful to spend some time studying other frameworks to see the different design decisions they made, and to maybe just get ideas on how you can improve your own chosen framework.  It was with that mindset that I sat down to read &lt;em&gt;&lt;a href="http://www.packtpub.com/grok-1-0-web-development/book?utm_source=blog.pythonisito.com&amp;utm_medium=bookrev&amp;utm_content=blog&amp;utm_campaign=mdb_002510"&gt;Grok 1.0 Web Development&lt;/a&gt;&lt;/em&gt; by Carlos de la Guardia.&lt;/p&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;iframe src="http://rcm.amazon.com/e/cm?lt1=_blank&amp;amp;bc1=000000&amp;amp;IS2=1&amp;amp;bg1=FFFFFF&amp;amp;fc1=000000&amp;amp;lc1=0000FF&amp;amp;t=pythonisitobl-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;m=amazon&amp;amp;f=ifr&amp;amp;md=10FE9736YVPPT7A0FBG2&amp;amp;asins=1847197485" style="width: 120px; height: 240px; float: right;" marginwidth="0" marginheight="0" frameborder="0" scrolling="no"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;p&gt;For those who are unfamiliar with Grok, Grok is a Python web framework built on top of the Zope 3 toolkit that endeavors to make Zope more accessible to those who are not already experts in the deep magic of Zope.  One of the problems with a layer like Grok is that sometimes Zope leaks through into your code.  In such cases, it's nice to have a guide for where Grok starts, where Zope begins, and how they work together.  This book can provide just that, as well as giving someone who may be new to Python web development a pretty good tutorial and head start on Grok development in particular, and Python web development in general.&lt;/p&gt;&lt;br /&gt;&lt;h1&gt;tl;dr Summary&lt;/h1&gt;&lt;br /&gt;&lt;p&gt;This is a good book.  It's not "OMG I need to drop all my TurboGears work right now and rewrite everything in Grok" good, but it gives you a good feel for the Grok framework and the Zope toolkit underneath it without being too overwhelming.  If you want to learn Grok or Zope web development, or if you just want to see how someone else did it, I'd say this is a good book to read.  If, however, you want an architectural description of Zope, or if you're brand-new to Python, HTML, CSS, and Javascript, this probably isn't the book for you.  &lt;/p&gt;&lt;br /&gt;&lt;h1&gt;Synopsis&lt;/h1&gt;&lt;br /&gt;&lt;p&gt;This book follows a running example of building a to-do list manager, which I think is a &lt;em&gt;great&lt;/em&gt; way to make the daunting world of Zope more accessible to us mortals.  By building the app from its barely-on-the-web-no-persistence-at-all roots to a multi-user, multi-project list manager with full security, a heterogenous database backend, etc., the book introduces Grok and Zope concepts in a logical and easy-to-follow way.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The book starts in chapter 1 with a high-level overview of what makes Grok Grok.  This is nice if only to give you your bearings before diving into the more tutorial-oriented remainder of the book.  Chapter 2 leads your through Grok's installation process including just a bit about Buildout, Zope's packaging and distribution system.  Chapter 3 launches into Grok views, including Zope Page Templates (Grok's templating language), form handling, and static resources.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In chapter 4, the Zope Object Database (ZODB) is introduced, as you add persistence to your to-do list application.  Some of the "magic" of Grok is also revealed, including how Grok magically knows what views go with what models and what templates.  More importantly, it lets you know how you can override this default behavior.  &lt;a href="http://www.packtpub.com/files/7481-grok-1-0-Web-development-sample-chapter-5-forms.pdf"&gt;Chapter 5&lt;/a&gt; returns to the topic of forms and introduces the grok.EditForm, Grok's solution to automatic form generation and validation.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Chapter 6 introduces the Catalog, which provides search capabilities to Grok applications.  Chapter 7 moves on to security, including authorization and authentication.  In this chapter, the author even leads you through adding your own authentication module to the todo-list application.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Chapter 8 presents the Grok Viewlet architecture, a set of tools that let you easily build complex templates from simple parts.  This is something that in my experience very few frameworks get right.  I will definitely be stealing some ideas from here for TurboGears ;-).  &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Chapters 9 and 10 return to the topic of persistence, with a more detailed discussion of the ZODB in chapter 9 and a description of how you can seamlessly integrate SQLAlchemy and relational databases into a Grok application, a topic near and dear to &lt;em&gt;my&lt;/em&gt; heart.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Chapters 11 and 12 go into a good bit of detail of the fundamental concepts behind Grok and Zope, including Zope interfaces and "grokkers" (the things that read the little Grok decorators that make everything so much easier than straight Zope).  If you want to know which chapters you can skip when getting started with Grok, it's these two.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Chapter 13 describes Grok's support fortesting and debugging which, like so much of Zope, is really well though out and cohesive once you "get it."  Chapter 14 concludes the book with a discussion of deployment and scaling Grok applications, including deployment behind Apache and ZEO (load-balancing) deployments.  Again, for the beginner, this is a chapter to save for later.&lt;/p&gt;&lt;br /&gt;&lt;h1&gt;Conclusion&lt;/h1&gt;&lt;br /&gt;&lt;p&gt;Overall, I think this is a well-written book.  There are a few typos in some of the examples, but it's easy enough to make the corrections in your head, and I didn't find them distracting at all.  The book flows well, and was written at a pace that I enjoyed.  (For an excerpt to judget for yourself, here's &lt;a href="http://www.packtpub.com/files/7481-grok-1-0-Web-development-sample-chapter-5-forms.pdf"&gt;Chapter 5: Forms&lt;/a&gt;  Again, I'm not going to drop all my TG stuff quite yet, but I do think Grok is worth a second look.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-5443983019546565997?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/0MUBF78PZ9opjf7W_js9ZOcBlUk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0MUBF78PZ9opjf7W_js9ZOcBlUk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/0MUBF78PZ9opjf7W_js9ZOcBlUk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0MUBF78PZ9opjf7W_js9ZOcBlUk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=aLyzN0XZNas:i0dmNnHl1Sk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=aLyzN0XZNas:i0dmNnHl1Sk:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=aLyzN0XZNas:i0dmNnHl1Sk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=aLyzN0XZNas:i0dmNnHl1Sk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=aLyzN0XZNas:i0dmNnHl1Sk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/aLyzN0XZNas" height="1" width="1"/&gt;</content><link rel="related" href="http://www.amazon.com/gp/product/1847197485?ie=UTF8&amp;tag=pythonisitobl-20&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1847197485" title="Review: Grok 1.0 Web Development" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/5443983019546565997/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2010/03/review-grok-10-web-development.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5443983019546565997?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/5443983019546565997?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/aLyzN0XZNas/review-grok-10-web-development.html" title="Review: Grok 1.0 Web Development" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2010/03/review-grok-10-web-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QEQn47eSp7ImA9WhZSFkw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-3309787348248281790</id><published>2009-12-07T17:11:00.006-05:00</published><updated>2011-03-31T20:55:03.001-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-31T20:55:03.001-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ming" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="mongodb" /><title>Ming 0.1 Released - Python Library for MongoDB</title><content type="html">One of the things that's been nice about working with &lt;a href="http://sourceforge.net"&gt;SourceForge&lt;/a&gt; for the last few months is the chance I get to work with new open source technology and contribute something back.  Well, the first (in a long series, I hope) of projects we're putting out there as the result of recent work is a little library we wrote called Ming.  It's been available over git for a while, but I just finished publishing our first "official" release, with a tutorial, over at &lt;a href="http://merciless.sourceforge.net"&gt;http://merciless.sourceforge.net&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/Ming/0.1"&gt;http://pypi.python.org/pypi/Ming/0.1&lt;/a&gt;.  Ming gives you a way of enforcing schemas on a MongoDB database in a Python application.  We also throw in automatic, lazy schema migration for good measure.   &lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;If you haven't heard of it yet, &lt;a href="http://www.mongodb.org"&gt;MongoDB&lt;/a&gt; describes itself as on its website as follows:&lt;br /&gt;&lt;blockquote&gt;MongoDB (from "humongous") is a scalable, high-performance, open source, schema-free, document-oriented database.&lt;br /&gt;&lt;/blockquote&gt;  &lt;br /&gt;There are lots of trade-offs to consider when using a non-relational database like MongoDB.  On the plus side, MongoDB is simple to develop for (no SQL), extremely fast, and models hierarchical relationships really nicely.  On the downside, we give up relational integrity constraints and transactional behavior.  &lt;br /&gt;&lt;br /&gt;With all the flexibility provided by MongoDB, however, comes some danger.  When the database is schema-free, you have to spend extra code making sure that the data you put in and the data you take out is valid for your application.  That's where Ming comes in.  A play off the character of &lt;a href="http://en.wikipedia.org/wiki/Ming_the_Merciless"&gt;Ming the Merciless&lt;/a&gt; who ruled the planet of Mongo with an iron fist in &lt;a href="http://en.wikipedia.org/wiki/Flash_Gordon"&gt;Flash Gordon&lt;/a&gt;, the Ming library provides a succinct way to specify the requirements your application has for the data it produces and consumes.  Ming also supports lazy migration of documents across schema revisions.  To give you a flavor for how easy this can be, here is a sample schema with migration from the tutorial:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;from ming.datastore import DataStore&lt;br /&gt;from ming import Session&lt;br /&gt;from ming import Document, Field, schema&lt;br /&gt;&lt;br /&gt;bind = DataStore('mongo://localhost:27017/tutorial')&lt;br /&gt;session = Session(bind)&lt;br /&gt;&lt;br /&gt;class OldWikiPage(Document):&lt;br /&gt;&lt;br /&gt;    class __mongometa__:&lt;br /&gt;        session = session&lt;br /&gt;        name = 'wiki_page'&lt;br /&gt;&lt;br /&gt;    _id = Field(schema.ObjectId)&lt;br /&gt;    title = Field(str)&lt;br /&gt;    text = Field(str, if_missing='')&lt;br /&gt;    metadata = Field(dict(&lt;br /&gt;            tags=[str],&lt;br /&gt;            categories=[str]))&lt;br /&gt;&lt;br /&gt;class WikiPage(Document):&lt;br /&gt;&lt;br /&gt;    class __mongometa__:&lt;br /&gt;        session = session&lt;br /&gt;        name = 'wiki_page'&lt;br /&gt;        version_of = OldWikiPage&lt;br /&gt;        def migrate(data):&lt;br /&gt;            result = dict(&lt;br /&gt;                data,&lt;br /&gt;                tags=data['metadata']['tags'],&lt;br /&gt;                categories=data['metadata']['categories'],&lt;br /&gt;                version=1)&lt;br /&gt;            del result['metadata']&lt;br /&gt;            return result&lt;br /&gt;&lt;br /&gt;    _id = Field(schema.ObjectId)&lt;br /&gt;    version = Field(1)&lt;br /&gt;    title = Field(str)&lt;br /&gt;    text = Field(str, if_missing='')&lt;br /&gt;    tags = Field([str])&lt;br /&gt;    categories = Field([str])&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Hopefully, that's enough to whet your appetite for now.  Rather than duplicating the entire tutorial, I'll direct you to the docs at &lt;a href="http://merciless.sourceforge.net"&gt;http://merciless.sourceforge.net&lt;/a&gt; for more information.  Let me know what you think!&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-3309787348248281790?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/MDBkLp9bXsvZkL427cBfw3RSo0Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MDBkLp9bXsvZkL427cBfw3RSo0Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/MDBkLp9bXsvZkL427cBfw3RSo0Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/MDBkLp9bXsvZkL427cBfw3RSo0Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hBKXzF0Xz90:beXXO3orJN8:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hBKXzF0Xz90:beXXO3orJN8:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hBKXzF0Xz90:beXXO3orJN8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=hBKXzF0Xz90:beXXO3orJN8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=hBKXzF0Xz90:beXXO3orJN8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/hBKXzF0Xz90" height="1" width="1"/&gt;</content><link rel="related" href="http://merciless.sourceforge.net" title="Ming 0.1 Released - Python Library for MongoDB" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/3309787348248281790/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2009/12/ming-01-released-python-library-for.html#comment-form" title="13 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/3309787348248281790?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/3309787348248281790?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/hBKXzF0Xz90/ming-01-released-python-library-for.html" title="Ming 0.1 Released - Python Library for MongoDB" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>13</thr:total><feedburner:origLink>http://blog.pythonisito.com/2009/12/ming-01-released-python-library-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QARXk_fip7ImA9WhZSFkw.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-6521885783224959317</id><published>2009-05-03T13:40:00.005-04:00</published><updated>2011-03-31T20:55:44.746-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-31T20:55:44.746-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="metapython" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="hygiene" /><category scheme="http://www.blogger.com/atom/ns#" term="macros" /><title>MetaPython 0.2.2 with Hygienic Macros</title><content type="html">&lt;p&gt;In my ever-expanding quest to, as &lt;a href="http://twitter.com/jgustak"&gt;@jgustak&lt;/a&gt; recently tweeted, "introduce evil to Python to prevent even scarier evil," I have released &lt;a href="http://metapython.org/"&gt;MetaPython 0.2.2&lt;/a&gt;  Once again, if you aren't familiar with MetaPython, a good place to start is the &lt;a href="http://code.google.com/p/metapython/wiki"&gt;tutorial&lt;/a&gt;which walks you through the construction of a macro-ized &lt;cite&gt;collections.namedtuple&lt;/cite&gt; from the Python 2.6 standard library.  If you already know MetaPython, here's the stuff that's new in 0.2.2.&lt;/p&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;One issue that macro implementors must eventually face is whether they want their&lt;br /&gt;macro system to be hygienic or not.  According to &lt;a href="http://en.wikipedia.org/wiki/Hygienic_macros"&gt;wikipedia&lt;/a&gt;,&lt;/p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Hygienic macros are macros whose expansion is guaranteed not to cause collisions with existing symbol definitions. They are a feature of programming languages such as Scheme and Dylan.&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;So what's all that mean?  The best way to explain is probably by showing the bad things unhygienic macros bring you.  Here is something you could do in MetaPython 0.2:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;def vadd(result, a, b):&lt;br /&gt;   defcode foo:&lt;br /&gt;       for i, (aa,bb) in enumerate(zip($a, $b)):&lt;br /&gt;           $&amp;lt;result&amp;gt;[i] = aa+bb&lt;br /&gt;   return foo&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;$vadd(?result, ?a, ?b)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Calling this macro &lt;cite&gt;$vadd(?z, ?x, ?y)&lt;/cite&gt; then gives you something like:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;for i,(aa,bb) in enumerate(zip(x, y)):&lt;br /&gt;   z[i] = aa+bb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Which is fine, as long as you weren't planning on using the &lt;cite&gt;i&lt;/cite&gt;, &lt;cite&gt;aa&lt;/cite&gt;, or &lt;cite&gt;bb&lt;/cite&gt; variables for anything important in the surrounding code.  This is especially bad because macros can introduce names that conflict with names in the context where they are expanded &lt;em&gt;surprisingly,&lt;/em&gt; without any indication to the macro &lt;em&gt;user&lt;/em&gt; that they are going to do so.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In MetaPython 0.2.1 and 0.2.2, you declare the block as &lt;cite&gt;defcode foo():&lt;/cite&gt; which tells MetaPython that the &lt;cite&gt;foo&lt;/cite&gt; block should not capture any variables from its context when it is expanded.  In this case, MetaPython detects that the variables &lt;cite&gt;i&lt;/cite&gt;, &lt;cite&gt;aa&lt;/cite&gt;, and &lt;cite&gt;bb&lt;/cite&gt; are assigned in the block and replaces them with "known unique" names (names that should not exist in the surrounding block, &lt;em&gt;wherever&lt;/em&gt; they are expanded.  In MetaPython 0.2.1 and 0.2.2, you get this expansion:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;for _mpy_0,(_mpy_1,_mpy_2) in enumerate(zip(x, y)):&lt;br /&gt;   z[_mpy_0] = _mpy_1 + _mpy_2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;So as long as you avoid using names starting with &lt;cite&gt;_mpy&lt;/cite&gt;, you should be fine.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now sometimes, you actually &lt;em&gt;want&lt;/em&gt; to capture a value from the context into which the macro is being defined.  One example is when you're writing a class factory like &lt;cite&gt;namedtuple&lt;/cite&gt;, covered in the tutorial.  Your goal is to generate a new class, and if that class has some weird &lt;cite&gt;_mpy_*&lt;/cite&gt; name, it's pretty useless.  So MetaPython lets you specify, via the arguments to &lt;cite&gt;defcode&lt;/cite&gt;, which names should be captured.  In the &lt;cite&gt;namedtuple&lt;/cite&gt; example, for instance, the &lt;cite&gt;defcode&lt;/cite&gt; declaration looks like &lt;cite&gt;defcode result(typename):&lt;/cite&gt;, where &lt;cite&gt;typename&lt;/cite&gt; is a variable containing the name of the class being created.  When MetaPython expands the code block, then, any names mentioned in the argument list will &lt;em&gt;not&lt;/em&gt; be auto-renamed by the "sanitizer."&lt;/p&gt;&lt;br /&gt;&lt;p&gt;MetaPython 0.2.1 and 0.2.2 both had this ability to hygienically expand macros, but 0.2.2 added the ability to use variable arguments to the &lt;cite&gt;defcode&lt;/cite&gt; block. One time when you might want to do this is in a domain-specific language context.  Say you wanted to specify that a class contained certain properties, and that those properties should be accessed via the &lt;cite&gt;property&lt;/cite&gt; builtin function.  For instance, say you wanted to have a class &lt;cite&gt;Foo&lt;/cite&gt; with properties &lt;cite&gt;a&lt;/cite&gt;, &lt;cite&gt;b&lt;/cite&gt;, &lt;cite&gt;c&lt;/cite&gt;, and &lt;cite&gt;d&lt;/cite&gt; implemented by semi-private instance variables &lt;cite&gt;_a&lt;/cite&gt;, &lt;cite&gt;_b&lt;/cite&gt;, &lt;cite&gt;_c&lt;/cite&gt;, and &lt;cite&gt;_d&lt;/cite&gt;.  You might write something like this:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;class Foo(object):&lt;br /&gt;   $has_properties(?a, ?b, ?c, ?d)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The implementation of has_properties, then, is the following:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;def has_properties(*props):&lt;br /&gt;   str_props = (str(p) for p in props)&lt;br /&gt;   gen = ( (p,&lt;br /&gt;            '_' + p,&lt;br /&gt;            '_get_' + p,&lt;br /&gt;            '_set_' + p)&lt;br /&gt;           for p in str_props )&lt;br /&gt;   defcode result(*props):&lt;br /&gt;       $for pub, pri, getter, setter in gen:&lt;br /&gt;           def $&amp;lt;getter&amp;gt;(self):&lt;br /&gt;               return self.$pri&lt;br /&gt;           def $&amp;lt;setter&amp;gt;(self, value):&lt;br /&gt;               self.$pri =  value&lt;br /&gt;           $pub = property($getter, $setter)&lt;br /&gt;   return result&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The final expanded version of &lt;cite&gt;Foo&lt;/cite&gt; is then:&lt;/p&gt;&lt;br /&gt;&lt;pre class="literal-block"&gt;class Foo (object ):&lt;br /&gt;  def _mpy_3 (self ):&lt;br /&gt;    return self ._a&lt;br /&gt;&lt;br /&gt;   def _mpy_7 (self ,value ):&lt;br /&gt;       self ._a =value&lt;br /&gt;&lt;br /&gt;   a =property (_mpy_3 ,_mpy_7 )&lt;br /&gt;   def _mpy_1 (self ):&lt;br /&gt;       return self ._b&lt;br /&gt;&lt;br /&gt;   def _mpy_5 (self ,value ):&lt;br /&gt;       self ._b =value&lt;br /&gt;&lt;br /&gt;   b =property (_mpy_1 ,_mpy_5 )&lt;br /&gt;   def _mpy_2 (self ):&lt;br /&gt;       return self ._c&lt;br /&gt;&lt;br /&gt;   def _mpy_6 (self ,value ):&lt;br /&gt;       self ._c =value&lt;br /&gt;&lt;br /&gt;  c =property (_mpy_2 ,_mpy_6 )&lt;br /&gt;  def _mpy_4 (self ):&lt;br /&gt;       return self ._d&lt;br /&gt;&lt;br /&gt;  def _mpy_8 (self ,value ):&lt;br /&gt;       self ._d =value&lt;br /&gt;&lt;br /&gt;  d =property (_mpy_4 ,_mpy_8 )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Note in particular that the getters and setters were sanitized, while the actual property names &lt;cite&gt;a&lt;/cite&gt;, &lt;cite&gt;b&lt;/cite&gt;, &lt;cite&gt;c&lt;/cite&gt;, and &lt;cite&gt;d&lt;/cite&gt; were skipped.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;So MetaPython now has what I think is a workable hygienic macro system with appropriate escapes for "non-hygienic" operation.  Any comments, questions, or criticisms are welcome, as well as ideas for how you are using or might use MetaPython.  Let me know what you think!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-6521885783224959317?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/NU7B3OjT3Fmy14UM6DHV3dHyRwc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NU7B3OjT3Fmy14UM6DHV3dHyRwc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/NU7B3OjT3Fmy14UM6DHV3dHyRwc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/NU7B3OjT3Fmy14UM6DHV3dHyRwc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=us9yAK-yrJM:u2roG_zPIV4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=us9yAK-yrJM:u2roG_zPIV4:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=us9yAK-yrJM:u2roG_zPIV4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=us9yAK-yrJM:u2roG_zPIV4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=us9yAK-yrJM:u2roG_zPIV4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/us9yAK-yrJM" height="1" width="1"/&gt;</content><link rel="related" href="http://metapython.org" title="MetaPython 0.2.2 with Hygienic Macros" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/6521885783224959317/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2009/05/metapython-022-with-hygienic-macros.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6521885783224959317?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/6521885783224959317?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/us9yAK-yrJM/metapython-022-with-hygienic-macros.html" title="MetaPython 0.2.2 with Hygienic Macros" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.pythonisito.com/2009/05/metapython-022-with-hygienic-macros.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08HQn8ycSp7ImA9WxJSEkQ.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-8646679290917812355</id><published>2009-04-17T09:34:00.004-04:00</published><updated>2009-05-02T16:23:53.199-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-02T16:23:53.199-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="metapython" /><category scheme="http://www.blogger.com/atom/ns#" term="python programming" /><category scheme="http://www.blogger.com/atom/ns#" term="macros" /><title>MetaPython 0.2 Release</title><content type="html">For those intrepid souls who are interested in generating Python code from the macros and code quoting facilities of &lt;a href="http://metapython.org"&gt;MetaPython&lt;/a&gt;, I have spun a new release.  If you aren't familiar with MetaPython, a good place to start is the &lt;a href="http://code.google.com/p/metapython/wiki/Tutorial"&gt;tutorial&lt;/a&gt;, which walks you through the construction of a macro for generating a macro-ized version of &lt;code&gt;collections.namedtuple&lt;/code&gt; from the Python 2.6 standard library.  If you are already familiar with MetaPython, I will try to summarize the changes and ideas in this release here.&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;MetaPython 0.1 was mainly a proof of concept implementation to show that it was possible to generate a moderately useful macro facility in a short period of time.  I did, however, make one really questionable decision: to use &lt;a href="http://jinja.pocoo.org/2/"&gt;Jinja2&lt;/a&gt; as the templating language for code quotes.  &lt;br /&gt;&lt;br /&gt;Don't get me wrong; Jinja2 is an awesome templating language.  But if I am generating a language extension (MetaPython) that allows you to change the text of a module as just before it gets imported, wouldn't it be nice to also be able to change the text of a code quote using the same syntax?  Hence version 0.2.&lt;br /&gt;&lt;br /&gt;There was also the issue of ugly syntax with the &lt;code&gt;?&lt;/code&gt;,  &lt;code&gt;$&lt;/code&gt;, and &lt;code&gt;{%...%}&lt;/code&gt; operators.  Although &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; are still there, I have tried to normalize their use a bit.  &lt;code&gt;$&lt;/code&gt; always introduces a construct that should be executed or evaluated "earlier" than the surrounding code.  (Inside a &lt;code&gt;defcode...:&lt;/code&gt; block, this means at block construction time, otherwise it means at import time.)  The &lt;code&gt;?&lt;/code&gt; operator now has a much more limited use as an inline code quoting operator.&lt;br /&gt;&lt;br /&gt;Other than syntax changes, one of the main things you'll notice in MetaPython 0.2 is the introduction of import-time control statements (&lt;code&gt;$for...&lt;/code&gt;, &lt;code&gt;$if...&lt;/code&gt;, etc.)  These allow the conditional or repeated expansion of code blocks.  These constructs basically obviate the need for another template language (Jinja2) inside &lt;code&gt;defcode...&lt;/code&gt; blocks.&lt;br /&gt;&lt;br /&gt;Another big change and move toward normalization is that macro calls are now just import-time function calls.  This means that their arguments are evaluated before they are called, not passed through as code objects.  In order to get the old behavior, you can simply quote the arguments you wish to be sent through unevaluated.  For instance, in the old syntax, creating a named tuple was accomplished via &lt;code&gt;?namedtuple(Point, x, y)&lt;/code&gt;, whereas in the new syntax, you would type &lt;code&gt;$namedtuple(?Point, ?x, ?y)&lt;/code&gt;.  This makes it substantially easier to write macros which need non-code arguments, and follows the Python Zen of "Explicit is better than implicit."&lt;br /&gt;&lt;br /&gt;There have also been significant reworking of the internal MetaPython parser and code construction machinery, although this should not be user-visible.  So try it out, let me know what you think, and have fun!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-8646679290917812355?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/J4Uf9MaDVE9lpqgZLofl5a0XHUU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J4Uf9MaDVE9lpqgZLofl5a0XHUU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/J4Uf9MaDVE9lpqgZLofl5a0XHUU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/J4Uf9MaDVE9lpqgZLofl5a0XHUU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=f5jLHtnEfT4:6xERlOQNRAY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=f5jLHtnEfT4:6xERlOQNRAY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=f5jLHtnEfT4:6xERlOQNRAY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=f5jLHtnEfT4:6xERlOQNRAY:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=f5jLHtnEfT4:6xERlOQNRAY:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/f5jLHtnEfT4" height="1" width="1"/&gt;</content><link rel="related" href="http://metapython.org" title="MetaPython 0.2 Release" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/8646679290917812355/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2009/04/metapython-02-release.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8646679290917812355?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/8646679290917812355?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/f5jLHtnEfT4/metapython-02-release.html" title="MetaPython 0.2 Release" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://blog.pythonisito.com/2009/04/metapython-02-release.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkMDRXk5eSp7ImA9WxVaE0U.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-4380292888467251781</id><published>2009-04-10T13:23:00.002-04:00</published><updated>2009-04-10T13:27:54.721-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-10T13:27:54.721-04:00</app:edited><title>MetaPython Presentation</title><content type="html">Last night at the &lt;a href="http://pyatl.org"&gt;Python Atlanta&lt;/a&gt; meetup I gave a brief talk on MetaPython, including the motivations for doing something so profane as adding macros and code quoting to Python.  The video is &lt;a href="http://blip.tv/file/1979340#share"&gt;on blip.tv&lt;/a&gt; and you can find the slides on the &lt;a href="http://metapython.org/presentations.html"&gt;MetaPython.org&lt;/a&gt;  Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-4380292888467251781?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/httV5G7vKcHAHBtRv3UL0-Pv8HY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/httV5G7vKcHAHBtRv3UL0-Pv8HY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/httV5G7vKcHAHBtRv3UL0-Pv8HY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/httV5G7vKcHAHBtRv3UL0-Pv8HY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=RAuaqsoADgk:3MIwph-j7hg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=RAuaqsoADgk:3MIwph-j7hg:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=RAuaqsoADgk:3MIwph-j7hg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=RAuaqsoADgk:3MIwph-j7hg:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=RAuaqsoADgk:3MIwph-j7hg:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/RAuaqsoADgk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/4380292888467251781/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2009/04/metapython-presentation.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/4380292888467251781?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/4380292888467251781?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/RAuaqsoADgk/metapython-presentation.html" title="MetaPython Presentation" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://blog.pythonisito.com/2009/04/metapython-presentation.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0ADRXo5eyp7ImA9WxVUFEU.&quot;"><id>tag:blogger.com,1999:blog-18508356.post-2354346033423422807</id><published>2009-03-19T09:11:00.006-04:00</published><updated>2009-03-19T14:02:54.423-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-19T14:02:54.423-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="python" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><category scheme="http://www.blogger.com/atom/ns#" term="macros" /><title>Announcing MetaPython - Macros for Python</title><content type="html">As I mentioned in my &lt;a href="http://blog.pythonisito.com/2009/03/python-macros.html"&gt;last post&lt;/a&gt;, I have been considering writing some version of macros for Python and was looking for use cases.  Well, having gotten the use cases I so desired from my wonderful commenters, I went ahead and put together an import hook and &lt;a href="http://code.google.com/p/metapython/"&gt;Google Code project&lt;/a&gt; that I'm calling MetaPython &amp;mdash; all just in time for &lt;a href="http://us.pycon.org/2009/about/"&gt;PyCon!&lt;/a&gt; (I have no talks, but I will be there, and would love to have a MetaPython Open Space if anyone's interested.)&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;So what's all the excitement about?  MetaPython introduces some hooks to allow you to modify module code just before it is seen by the Python interpreter (at what I'm calling "import time").  The import-time syntax is pretty simple, and is (almost) all denoted by a question mark &lt;code&gt;?&lt;/code&gt; prefix (question marks are currently syntax errors in regular Python).  Here is a trivial example that defines an import-time function (which as we will see can be used as a macro) that will conditionally remove a function call (and the evaluation of its associated arguments).  Suppose the following text is saved in a file "cremove.mpy":&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;def cremove(debug, expr):&lt;br /&gt;    debug = eval(str(debug))&lt;br /&gt;    defcode empty_result:&lt;br /&gt;        pass&lt;br /&gt;    if debug:&lt;br /&gt;        result = expr&lt;br /&gt;    else:&lt;br /&gt;        result = empty_result&lt;br /&gt;    return result&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The idea here is that &lt;code&gt;cremove&lt;/code&gt; will be called with two &lt;code&gt;metapython.Code&lt;/code&gt; values, &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;expr&lt;/code&gt;.  &lt;code&gt;cremove&lt;/code&gt; will convert debug to its Python code representation by calling &lt;code&gt;str()&lt;/code&gt; and then evaluate the result.  If &lt;code&gt;debug&lt;/code&gt; is true, then &lt;code&gt;expr&lt;/code&gt; will be returned.  Otherwise a &lt;code&gt;pass&lt;/code&gt; statement will be returned (defined using the MetaPython import-time construct &lt;code&gt;defcode&lt;/code&gt; which defines a code template).  To actually call cremove as a macro, we will need to define another MetaPython module, say "test_cremove.mpy":&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;import logging&lt;br /&gt;logging.basicConfig(level=logging.DEBUG)&lt;br /&gt;log = logging.getLogger(__name__)&lt;br /&gt;&lt;br /&gt;?from cremove import cremove&lt;br /&gt;&lt;br /&gt;def do_test():&lt;br /&gt;    ?cremove(True, log.debug('This statement will be logged'))&lt;br /&gt;    ?cremove(False, log.debug('This statement will not be logged'))&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here, we do an import-time import (the &lt;code&gt;?from... import&lt;/code&gt; business).  This makes the module we're importing available at import-time (a regular import would be seen just as another line of Python code at import-time).  To actually call cremove as a macro, we just need to prefix it with the "?" as shown.&lt;br /&gt;&lt;br /&gt;Now, to actually test this, we'll need to install MetaPython and fire up an interpreter.  MetaPython is available from the &lt;a href="http://pypi.python.org/pypi"&gt;CheeseShop&lt;/a&gt;, so to get it just run &lt;code&gt;easy_install MetaPython&lt;/code&gt;.  Once it's installed, we can test our MetaPython code as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt;&gt; import metapython; metapython.install_import_hook()&lt;br /&gt;&gt;&gt;&gt; import test_cremove&lt;br /&gt;&gt;&gt;&gt; test_cremove.do_test()&lt;br /&gt;DEBUG:test_cremove:This statement will be logged&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since macro expansion can get pretty complex and it's always tricky debugging code you've never seen, the fully-expanded module is available as the &lt;code&gt;__expanded__&lt;/code&gt; attribute of the MetaPython module:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt;&gt; print test_cremove.__expanded__&lt;br /&gt;import logging &lt;br /&gt;logging .basicConfig (level =logging .DEBUG )&lt;br /&gt;log =logging .getLogger (__name__ )&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def do_test ():&lt;br /&gt;    log .debug ('This statement will be logged')&lt;br /&gt;    pass  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There's a lot more to MetaPython, but hopefully this has whetted your appetite.  There's a &lt;a href="http://code.google.com/p/metapython/wiki/Tutorial"&gt;short tutorial&lt;/a&gt; available that shows how you can implement the &lt;code&gt;collections.namedtuple&lt;/code&gt; class factory using a macro.  It also shows how you can use Jinja2 syntax along with the defcode construct to dynamically produce Python code.  Have fun, and let me know what you think!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;!-- // MAILCHIMP SUBSCRIBE CODE \\ --&gt;
&lt;a href="http://eepurl.com/ifqEc"&gt;Subscribe to our newsletter&lt;/a&gt;
&lt;!-- \\ MAILCHIMP SUBSCRIBE CODE // --&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/18508356-2354346033423422807?l=blog.pythonisito.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/G6Thg8rQykN5bbz8PbPfsdr-y1s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/G6Thg8rQykN5bbz8PbPfsdr-y1s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/G6Thg8rQykN5bbz8PbPfsdr-y1s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/G6Thg8rQykN5bbz8PbPfsdr-y1s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=y9ZDDQ_dnyE:yZmpR5sFX6A:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=y9ZDDQ_dnyE:yZmpR5sFX6A:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=y9ZDDQ_dnyE:yZmpR5sFX6A:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:TzevzKxY174"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=TzevzKxY174" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:l6gmwiTKsz0"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?d=l6gmwiTKsz0" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/JustALittlePython?a=y9ZDDQ_dnyE:yZmpR5sFX6A:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/JustALittlePython?i=y9ZDDQ_dnyE:yZmpR5sFX6A:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/JustALittlePython/~4/y9ZDDQ_dnyE" height="1" width="1"/&gt;</content><link rel="related" href="http://code.google.com/p/metapython/" title="Announcing MetaPython - Macros for Python" /><link rel="replies" type="application/atom+xml" href="http://blog.pythonisito.com/feeds/2354346033423422807/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://blog.pythonisito.com/2009/03/announcing-metapython-macros-for-python.html#comment-form" title="13 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/2354346033423422807?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/18508356/posts/default/2354346033423422807?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/JustALittlePython/~3/y9ZDDQ_dnyE/announcing-metapython-macros-for-python.html" title="Announcing MetaPython - Macros for Python" /><author><name>Rick Copeland</name><uri>http://www.blogger.com/profile/11612114223288841087</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://2.bp.blogspot.com/_nBbh7fCcv1w/SLBwYXLKA2I/AAAAAAAAABM/EgRC77lqhr0/S220/headshot_enhanced.jpg" /></author><thr:total>13</thr:total><feedburner:origLink>http://blog.pythonisito.com/2009/03/announcing-metapython-macros-for-python.html</feedburner:origLink></entry></feed>

