<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>Kyle Banker | Hwaet!</title>
  <subtitle>(hwaet - it means, &quot;ho there!&quot;)</subtitle>
  <link href="http://kylebanker.com/blog/" rel="self" />
  <link href="http://kylebanker.com/" />
  <updated>2010-07-19T12:53:01-04:00</updated>
  <author>
    <name>Kyle Banker</name>
    <email>kylebanker@gmail.com</email>
  </author>
  <id>http://kylebanker.com/</id>
  
  <entry>
    <title>E-commerce Inventory with MongoDB</title>
    <link href="/blog/2010/06/07/mongodb-inventory-transactions/" />
    <id>tag:kylebanker.com,2010-06-07:1275957957</id>
    <updated>2010-06-07T20:45:57-04:00</updated>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;/blog/2010/04/30/mongodb-and-ecommerce/&quot;&gt;my last post&lt;/a&gt;, I sketched out how a few e-commerce domains might be modeled
using MongoDB. Among the examples was an illustration of how inventory could be
managed using the database&amp;#8217;s &lt;a href=&quot;http://www.mongodb.org/display/DOCS/findandmodify+Command&quot;&gt;find_and_modify
command&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Inventory as Documents&lt;/h3&gt;
&lt;p&gt;To recap, I suggested that each physical inventory item be represented as a separate entry
in an inventory collection. When a user adds an item to her cart, we run a
find_and_modify operation, which searches for an item with the given sku that&amp;#8217;s
marked as &lt;strong&gt;available&lt;/strong&gt;. If the item is found, we mark the item as &lt;strong&gt;in-cart&lt;/strong&gt; and set an
expiration date so that a separate reaper process can periodically comb through
the collection and reset any unpurchaed items.&lt;/p&gt;
&lt;p&gt;Given that find_and_modify works atomically, the query and update all happen
within the scope of a single atomic operation. This works great when the user is
merely adding one item to the cart (or checking out with just one item.)  But,
as two commenters pointed out, the situation becomes a bit dicier when the user
is trying to add or check out with more than one item. How do we handle this
situation atomically?&lt;/p&gt;
&lt;p&gt;The short answer is that, strictly speaking, we can&amp;#8217;t. MongoDB does not support
multi-object transactions at the moment and probably won&amp;#8217;t anytime soon (doing
so would add too much complexity in a sharded situation). However, there is a
workaround that will serve in most situations, which is to simulate a
transaction at the application layer.&lt;/p&gt;
&lt;h3&gt;Add to cart&lt;/h3&gt;
&lt;p&gt;To show that this is possible, I built &lt;a href=&quot;http://github.com/banker/inventory&quot;&gt;a simple test
application&lt;/a&gt; which you can check out &lt;a href=&quot;http://github.com/banker/inventory&quot;&gt;on
github&lt;/a&gt;. If you clone the repo, you can run
the test suite; the code is relatively easy to follow.&lt;/p&gt;
&lt;p&gt;The algorithm goes something like this, assuming that multiple items are added to the cart
in a single operation:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For each item&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Determine if that item is &lt;strong&gt;available&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;If the item is &lt;strong&gt;available&lt;/strong&gt;, set its state to &lt;strong&gt;in-cart&lt;/strong&gt;,
and push that item&amp;#8217;s _id value onto the order document.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;If at any point an item can&amp;#8217;t be transitioned, perform a rollback. This involves removing
all the added items from the cart, reverting their state to &lt;strong&gt;available&lt;/strong&gt;, and
then raising an exception.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;#8217;s it. The biggest drawback here is that the process isn&amp;#8217;t atomic. If
either the database server or the application server crashes in the middle of the
operation, then we&amp;#8217;re probably left in an inconsistent state. Thus, this
techinque shouldn&amp;#8217;t be used in an accounting system. But you already knew &lt;em&gt;that&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Checkout&lt;/h3&gt;
&lt;p&gt;With just a few slight modifications, the code I posted can also be used at
checkout. The checkout process is quite similar:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Use find_and_modify to move all cart items to a &lt;strong&gt;purchased&lt;/strong&gt; state. If this
fails for any one item, roll all the items back to the &lt;strong&gt;in-cart&lt;/strong&gt; state, and abort the purchase.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;If all items advance, authorize the credit card. If the credit card&lt;/li&gt;
	&lt;li&gt;authorizes, the order can be
transitioned to the next logical state (e.g., &lt;strong&gt;ready-to-ship&lt;/strong&gt;). If the card
fails to authorize, revert the cart items to &lt;strong&gt;in-cart&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Is the inventory control technique outlined here as robust as what you&amp;#8217;d get with a
transactional relational database engine? Obviously not.&lt;/p&gt;
&lt;p&gt;Is it sufficient for ensuring that no more inventoy is sold than is available?
Yes.&lt;/p&gt;
&lt;p&gt;Not all e-commerce sites need to limit purchases based on available inventory.
But if you&amp;#8217;re building one that does, and you want to use MongoDB, the
find_and_modify technique presented here just may do the trick.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>MongoDB and E-commerce</title>
    <link href="/blog/2010/04/30/mongodb-and-ecommerce/" />
    <id>tag:kylebanker.com,2010-04-30:1272648045</id>
    <updated>2010-04-30T13:20:45-04:00</updated>
    <content type="html">&lt;p&gt;There are still a number of misconceptions regarding MongoDB&amp;#8217;s
suitability for &lt;nobr&gt;e-commerce&lt;/nobr&gt; sites. I refer, in particular, to this
&lt;a href=&quot;http://stackoverflow.com/questions/2581738/are-there-any-e-commerce-websites-that-use-nosql-databases&quot;&gt;stackoverflow
posting&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The hesitant reaction there expressed is understandable, as most of the databases
falling under the NoSQL umbrella would fare poorly in an &lt;nobr&gt;e-commerce&lt;/nobr&gt; setting.
But this is not the case with MongoDB. Indeed, with its support for rich data models,
dynamic, indexed queries, atomic operations, and replicated writes, MongoDB can
be a viable and even desirable database for &lt;nobr&gt;e-commerce&lt;/nobr&gt;; here&amp;#8217;s why.&lt;/p&gt;
&lt;h2&gt;Catalog Management&lt;/h2&gt;
&lt;p&gt;If you need some insight into the way in which catalog management is handled
with relational databases, have a quick look at the schemas of the popular
&lt;a href=&quot;http://www.magentocommerce.com/wiki/development/magento_database_diagram&quot;&gt;Magento &lt;nobr&gt;e-commerce&lt;/nobr&gt; framework&lt;/a&gt; or &lt;a href=&quot;https://cwiki.apache.org/confluence/display/OFBTECH/Data+Model+Diagrams&quot;&gt;Apache&amp;#8217;s OfBiz&lt;/a&gt;.
What you&amp;#8217;ll see is a flurry of tables working together to provide a flexible schema
on top of a fundamentally inflexible style of database system.&lt;/p&gt;
&lt;p&gt;What this means is that the data for any given product is spread out across
dozens of tables. This increases the complexity of the code required to persist
and query individual products and makes a shell-based inqury all but
impossible. Ask yourself if you&amp;#8217;d rather write the SQL JOIN to pull together a
product modeled like this:&lt;/p&gt;
&lt;div class=&quot;clear&quot;&gt;
&lt;p&gt;&lt;img src=&quot;/assets/content/2010/magento-product-schema.png&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;Or issue a simple &lt;strong&gt;find&lt;/strong&gt; from the MongoDB JavaScript shell to pull back a JSON
object like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Find a product object&lt;/span&gt;
db.products.&lt;span class=&quot;SupportFunction&quot;&gt;find&lt;/span&gt;({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d2fa5&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)});

{_id: ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d2a43&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;),
 title: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;A Love Supreme [Original Recording Reissued]&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
 author: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;John Coltrane&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
 author_id: ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d2fa5&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;),

 details: {
   number_of_discs: &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;,
   label: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Impulse Records&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
   issue_date: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;December 9, 1964&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
   average_customer_review: &lt;span class=&quot;Constant&quot;&gt;4.95&lt;/span&gt;,
   asin: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;B0000A118M&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
 },

 pricing: {
  list: &lt;span class=&quot;Constant&quot;&gt;1198&lt;/span&gt;,
  retail: &lt;span class=&quot;Constant&quot;&gt;1099&lt;/span&gt;,
  savings: &lt;span class=&quot;Constant&quot;&gt;99&lt;/span&gt;,
  pct_savings: &lt;span class=&quot;Constant&quot;&gt;8&lt;/span&gt;
 },

 categories: [
   ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d2a43&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;),
   ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d2b44&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;),
   ObjectID(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;4bd87bd8277d094c458d29a1&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
 ]
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Of course, this is not a complete representation of a product, but it does
demonstrate how many of the more trivial tables existing in a relational
representation can be dispensed with in a document representation.&lt;/p&gt;
&lt;p&gt;For object-oriented data, documents simply make more sense, both
conceptually and performance-wise. A document-oriented representation of product
data means fewer entities (a handful of collections vs. dozens of tables),
better query performance (no server-side joins), and structures that fit the product
precisely. There&amp;#8217;s no longer any need to design some master schema that can
account for every single conceviable product.&lt;/p&gt;
&lt;p&gt;Catalog management is essentially content management, a domain MongoDB excels at.
To read more about this, see the &lt;a href=&quot;http://sf2010.drupal.org/conference/sessions/mongodb-humongous-drupalr&quot;&gt;Drupal presentation on their move to
MongoDB&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Shopping Carts and Orders&lt;/h2&gt;
&lt;p&gt;Allowing that a shopping cart is merely an order in a &amp;#8216;cart&amp;#8217; state, the
modeling of shopping carts and orders in MongoDB becomes quite straightforward:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;{&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: objectid(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;4b980a6dea2c3f4579da141e&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;),
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: objectid(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;4b980a6dea2c3f4579a4f54&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;),
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;cart&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,

 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: [
    {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jc-432&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
     &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;name&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;John Coltrane: A Love Supreme&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
     &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;retail_price&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;1099&lt;/span&gt;
    },

    {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;ly-211&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
     &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;name&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Larry Young: Unity&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
     &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;retail_price&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;1199&lt;/span&gt;
    },
  ],

 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;shipping_address&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: {
   &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;street&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;3333 Greene Ave.&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
   &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;city&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Brooklyn&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
   &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;NY&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
   &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;zip&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;11216&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
  },

  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;subtotal&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;2199&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that we can represent the order line items as an array of products. As is
usual with documents, this makes displaying the shopping cart more
straighforward, since no joins are involved. But it also solves the problem of
product versioning. It&amp;#8217;s often necessary to take a snapshot of a product when a
user purchases it. This can be accomplished in an RDBMS by linking to a
particular version of a product. Here, however, we simply store a snapshot of
the product in the order itself.&lt;/p&gt;
&lt;h3&gt;Querying Orders&lt;/h3&gt;
&lt;p&gt;Since MongoDB supports dynamic queries and secondary indexes, querying for
orders is a snap. It&amp;#8217;s possible, for example, to define an index on
product sku, which allows for efficient queries on all orders of a given product:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;db.orders.ensureIndex({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items.sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;});
db.orders.&lt;span class=&quot;SupportFunction&quot;&gt;find&lt;/span&gt;({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items.sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jc-431&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With MongoDB, we can query on arbitrary attributes, so any query on the orders
collection is possible. And for common queries, we can define indexes for
greater efficiency.&lt;/p&gt;
&lt;h3&gt;Aggregation&lt;/h3&gt;
&lt;p&gt;Of course, aggregation is also necessary. We&amp;#8217;ll want to report on orders in
different ways, and for that, map-reduce is available. As an example, here&amp;#8217;s how
to write a map-reduce command that aggregates order totals per zip code:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;map &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;  function() {&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;    emit(this['shipping_address']['zip'], {total: this.total})&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;  }&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;

reduce &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;  function(key, values) {&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;    var sum = 0;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;    values.forEach(function(doc) {&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;      sum += doc.total;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;    }&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;    return {total: sum};&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;  }&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;


db.orders.mapReduce(map, reduce, {out: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;order_totals_by_zip&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For more on MongoDB&amp;#8217;s map-reduce, see &lt;a href=&quot;http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/&quot;&gt;Map-Reduce Basics&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Updating Orders&lt;/h3&gt;
&lt;h4&gt;Incrementing Quantity&lt;/h4&gt;
&lt;p&gt;One way to go about adjusting quantity is to use the positional operator, which
lets you apply atomic operations to individual objects within an array. Here&amp;#8217;s how
we change the number of Coltrane albums we&amp;#8217;re ordering:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;db.orders.update({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: order_id, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items.sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jc-431&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;},
                 {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$set&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items.$.quantity&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;}});
&lt;/pre&gt;
&lt;/div&gt;
&lt;h4&gt;Adding and Removing Items&lt;/h4&gt;
&lt;p&gt;Likewise, atomic operators solve the problem of adding and removing products
from the cart. For instance, we can use the &lt;strong&gt;$push&lt;/strong&gt; atomic operator to add an
item to our cart:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;db.orders.update({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: order_id},
    {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$push&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;line_items&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;:
      {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;md-12&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;price&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;2500&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;title&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Basketball&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}}
     &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$inc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;subtotal&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;2500&lt;/span&gt;}});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When adjusting quantity and changing the cart items themselves, it&amp;#8217;s
necessary to update the order totals. Notice that we use the &lt;strong&gt;$inc&lt;/strong&gt; operator to
handle this.&lt;/p&gt;
&lt;h2&gt;Inventory&lt;/h2&gt;
&lt;p&gt;Not all &lt;nobr&gt;e-commerce&lt;/nobr&gt; sites need inventory management. But for those that do, MongoDB
can rise to the ocassion.&lt;/p&gt;
&lt;p&gt;One way to model inventory is to store a separate document per
physical item in our warehouse. So, for example, if our warehouse has
twenty copies of the Coltrane album, that translates to twenty distinct
documents in our inventory collection. Each document looks something like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;{&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: objectid(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;4b980a6dea2c3f4579da432a&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;),
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jc-431&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;available&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;expires&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;null&lt;/span&gt;,
 &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;order_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;null&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When a user tries to add an item to the cart, a &lt;strong&gt;findAndModify&lt;/strong&gt; command can be
issued to atomically mark the item as in-cart, associate the item with a given
order, and set an expiration time:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;query &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sku&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jc-431&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;available&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;};

update &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$set&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;:
          {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;:    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;cart&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
           &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;order_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: order_id,
           &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;expires&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;:  &lt;span class=&quot;Support&quot;&gt;Date&lt;/span&gt;.now() &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;60&lt;/span&gt;}};

item &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; db.inventory.findAndModify(query: query, update: update);
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If we get an item back from the &lt;strong&gt;findAndModify&lt;/strong&gt; operation, we know we have a unique lock on that
item, and we can store it in our cart. When the user goes to check out, the
item&amp;#8217;s state can be advance to &amp;#8216;purchased,&amp;#8217; or whatever the case calls for.&lt;/p&gt;
&lt;p&gt;Meanwhile, we can run a script in the background that frees cart inventory not
purchased in the fifteen-minute window. The update is trivial:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;db.inventory.update({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;cart&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;expires&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$lt&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Support&quot;&gt;Date&lt;/span&gt;.now()}},
  {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$set&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;available&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;expires&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;null&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;order_id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;null&lt;/span&gt;}},
  {multi: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;});
&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Transactions, Consistency, and Durability&lt;/h2&gt;
&lt;p&gt;A lot of the arguments levied against NoSQL in &lt;nobr&gt;e-commerce&lt;/nobr&gt; center
around transactions, consistency, and durability. A couple points, then, are
worth noting.&lt;/p&gt;
&lt;p&gt;Concerning transactions, it is true that MongoDB doesn&amp;#8217;t support the multi-object
kind; however, it does support atomic operations on individual documents. And
this, combined with the document-oriented modeling just described and a little
creativity, is adequate to many &lt;nobr&gt;e-commerce&lt;/nobr&gt; problems. Certainly, if we needed to
literally debit one account and credit another in the same operation, or if we
wanted rollback, we&amp;#8217;d need full-fledged transactions. However, the
transactionality provided by MongoDB may be sufficient for most, if not all, &lt;nobr&gt;e-commerce&lt;/nobr&gt;
operations.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re concerned about consistency and durability, know that write
operations in MongoDB can be made consistent across connections. Furthermore,
MongoDB 1.5 support near-real-time replication, so that it&amp;#8217;s now possible to
assure that an operation has been replicated before returning. See the documentation on
&lt;a href=&quot;http://www.mongodb.org/display/DOCS/Replication#Replication-ReplicationAcknowledgementviagetlasterror&quot;&gt;replication
acknowldgment&lt;/a&gt;
for details.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Certainly most NoSQL databases weren&amp;#8217;t built with &lt;nobr&gt;e-commerce&lt;/nobr&gt; in
mind. Databases that lack rich data models, dynamic queries, and any notion of
transactionality can&amp;#8217;t be expected to compete in the &lt;nobr&gt;e-commerce&lt;/nobr&gt;
space, and so it&amp;#8217;s understandable how one might think that MongoDB couldn&amp;#8217;t, either.&lt;/p&gt;
&lt;p&gt;But for the parts of an &lt;nobr&gt;e-commerce&lt;/nobr&gt; site comprising content management, using
MongoDB will mean a clear win. And even for the more transactional components of the
system, MongoDB has features that make the prospect of running an entire
e-commerce site on it a real possibility. So while the ideas in this post are
just a sketch, let&amp;#8217;s not run from the notion that a document database just might
do &lt;nobr&gt;e-commerce&lt;/nobr&gt;, and do it well.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Do the MongoDB Drivers Support Feature &lt;i&gt;X&lt;/i&gt;?</title>
    <link href="/blog/2010/03/28/does-the-driver-support-feature-x/" />
    <id>tag:kylebanker.com,2010-03-28:1269802605</id>
    <updated>2010-03-28T14:56:45-04:00</updated>
    <content type="html">&lt;p&gt;With MongoDB&amp;#8217;s aggressive roadmap, new features are constantly being added. Notable in the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/1.4+Release+Notes&quot;&gt;1.4 release&lt;/a&gt; are the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Updating#Updating-%24addToSet&quot;&gt;$addToSet update operator&lt;/a&gt;, the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/findandmodify+Command&quot;&gt;findAndModify command&lt;/a&gt;, and &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Geospatial+Indexing&quot;&gt;indexes supporting two-dimensional coordinates&lt;/a&gt;. Usually the first question from users is whether their driver supports the new features. Happily, the answer is almost always yes; in this post, I explain why that is so.&lt;/p&gt;
&lt;p&gt;New MongoDB features, not counting the many internal improvements, usually fit into one of three categories. Either we&amp;#8217;re dealing with a new query or update operator, a new database command, or an indexing enhancement. We&amp;#8217;ll look at each of these in turn, and show how it is that the drivers can immediately support these types of new features.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Query and Update Operators&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MongoDB 1.4 features an $addToSet update operator, which pushes an item onto an array only if that item doesn&amp;#8217;t yet exist in the array. If we want to uniquely add a tag to an existing array of tags, the following code will work:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;posts&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;update&lt;/span&gt;({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;slug&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;mongodb-in-ruby&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;},
  {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$addToSet&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;tags&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;technology&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}})
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This update will succeed only if the &lt;i&gt;tags&lt;/i&gt; array doesn&amp;#8217;t contain the term &amp;#8216;technology.&amp;#8217;&lt;/p&gt;
&lt;p&gt;Nothing in the driver has to change to support this new feature. This is because the documents sent to the &lt;i&gt;update&lt;/i&gt; method are translated directly into BSON and sent to the MongoDB server. Since there&amp;#8217;s no driver intervention, new update and query operators are automatically supported.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Database Commands&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Another new 1.4 feature is the findandmodify command. findandmodify is used to update a document atomically and immediately return it. The is useful for implementing queues. For instance, suppose we want to grab the latest item from a queue and mark it as being in progress. First, we need to construct the parameters of the command:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;command &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;OrderedHash&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;new&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; The 'findAndModify' key reference the name of the collection.&lt;/span&gt;
command[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;findandmodify&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;jobs&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; The 'query' filters the collection; here we only want 'new' items.&lt;/span&gt;
command[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;query&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;new&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; The sort order in this case allows us to fetch the oldest, unprocessed item.&lt;/span&gt;
command[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sort&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;created_at&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;}

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Here we specify the update we want,&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; which is to mark the object as being in progress&lt;/span&gt;
command[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;update&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;] &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$set&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;state&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;processing&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once we&amp;#8217;ve constructed the command, we send it to the database&amp;#8217;s command method, which all the drivers implement:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Send the command we just specified.&lt;/span&gt;
result &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;command&lt;/span&gt;(command)
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Straightforward enough. But to see what&amp;#8217;s really happening here, have a look at another way of accomplishing the same thing:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;cmd_collection&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$cmd&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]

result &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;cmd_collection&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(command).&lt;span class=&quot;Entity&quot;&gt;limit&lt;/span&gt;(&lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;next_document&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Do you see how it works? We&amp;#8217;re simply querying a special collection called &lt;i&gt;$cmd&lt;/i&gt;. The limit of -1 indicates that we don&amp;#8217;t want to create a cursor on the server. Since database commands are nothing more than queries on the &lt;i&gt;$cmd&lt;/i&gt; collection, the drivers will always support new commands right away.&lt;/p&gt;
&lt;p&gt;Note that some commands need to be run on the admin database. For instance, the new &lt;i&gt;logrotate&lt;/i&gt; command falls into this category. But most commands are run on whichever database your application is using. Have a look a the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/List+of+Database+Commands&quot;&gt;list of database commands&lt;/a&gt; for more on this, and look at the implementation of some commonly-used commands. Did you know that &lt;i&gt;count()&lt;/i&gt; is actually a command? Check out the &lt;a href=&quot;http://github.com/mongodb/mongo-ruby-driver/blob/b87e3dd3fb59906bea33150936f431b0b73d3781/lib/mongo/cursor.rb#L94&quot;&gt;Ruby driver&amp;#8217;s implementation&lt;/a&gt; for a bit of illumination.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Indexing Enhancements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;1.4 adds support for two-dimensional indexes, and accordingly, we&amp;#8217;ve added a few conveniences to the Ruby driver that simplify the creation of these indexes. If you have a &lt;i&gt;places&lt;/i&gt; collection and a field called &lt;i&gt;loc&lt;/i&gt; that specifies x- and y-coordinates, here&amp;#8217;s how you can create the index:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;places&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;places&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;places&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;create_index&lt;/span&gt;([[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;loc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;Support&quot;&gt;Mongo&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;GEO2D&lt;/span&gt;]])
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;But there&amp;#8217;s a more direct way of creating indexes: simply insert a document into the database&amp;#8217;s &lt;i&gt;system.indexes&lt;/i&gt; collection. See, each database has a special collection called &lt;i&gt;system.indexes&lt;/i&gt;. You can query the collection to find out which indexes have been defined, and you add and remove documents from that collection to define and delete indexes. If we were going to create the above index directly, it&amp;#8217;d look like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Create a document specifying the index&lt;/span&gt;
doc &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;name&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;loc2d&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;ns&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;myapp.places&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;key&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;loc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;2d&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;}}

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Get the system.indexes collection&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;indexes&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;system.indexes&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Insert the document&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;indexes&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;insert&lt;/span&gt;(doc)
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The index name can be anything. The &lt;i&gt;ns&lt;/i&gt; key specifies the namespace, which is the database name followed by a dot followed by the collection name. The &lt;i&gt;key&lt;/i&gt; defines the index itself. Note that if you&amp;#8217;re creating a compound index, &lt;i&gt;key&lt;/i&gt; should point to an ordered hash, since order in that case does matter.&lt;/p&gt;
&lt;p&gt;But that&amp;#8217;s all there is to it. If you want to explore further, look at the &lt;a href=&quot;http://github.com/mongodb/mongo-ruby-driver/blob/b87e3dd3fb59906bea33150936f431b0b73d3781/lib/mongo/collection.rb#L361&quot;&gt;Ruby implementation of Collection#create_index&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;The Answer is &lt;i&gt;Yes&lt;/i&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Next time you&amp;#8217;re wondering if the the drivers support some new MongoDB feature, ask yourself whether that new feature is new update or query operator, and new command, or an indexing enhancement. If so, the answer is an emphatic &lt;i&gt;yes&lt;/i&gt;.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Try MongoDB</title>
    <link href="/blog/2010/1/try-mongodb-browser/" />
    <id>tag:kylebanker.com,2010-01-02:1262452517</id>
    <updated>2010-01-02T12:15:17-05:00</updated>
    <content type="html">&lt;p&gt;Want to &lt;a href=&quot;http://mongo.kylebanker.com&quot;&gt;try MongoDB in your browser&lt;/a&gt;? Just deployed this a few days ago:&lt;/p&gt;
&lt;div class=&quot;clear&quot;&gt;&lt;a class=&quot;image&quot; href=&quot;http://mongo.kylebanker.com&quot;&gt;&lt;img width=&quot;640px&quot; src=&quot;/assets/content/2010/try-mongo-screenshot.png&quot; alt=&quot;Try MongoDB Screenshot&quot; title=&quot;Try MongoDB&quot;&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;One of the nice things about MongoDB&amp;#8217;s JavaScript shell is that we can run a variation on it directly in the browser. It doesn&amp;#8217;t provide near the functionality of the actual shell, but users can still get a feel for CRUD in MongoDB, and it&amp;#8217;s possible to use any of the nifty &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Advanced+Queries&quot;&gt;query&lt;/a&gt; and &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Atomic+Operations#AtomicOperations-Modifieroperations&quot;&gt;update operators&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Anyone who&amp;#8217;d like to contribute can fork &lt;a href=&quot;http://github.com/banker/mongulator&quot;&gt;the code on github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(Almost forgot to mention that this is indeed inspired by, and is a little homage to, &lt;a href=&quot;http://en.wikipedia.org/wiki/Why_the_lucky_stiff&quot;&gt;the great _why&lt;/a&gt;.)&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Slides: MongoDB (is) for Rubyists</title>
    <link href="/blog/2009/12/slides-mongodb-is-for-rubyists/" />
    <id>tag:kylebanker.com,2009-12-15:1260883659</id>
    <updated>2009-12-15T08:27:39-05:00</updated>
    <content type="html">&lt;p&gt;&lt;i&gt;Wherein I claim that MongoDB is for human-oriented programmers.&lt;/i&gt;&lt;/p&gt;
&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_2685470&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/kbanker/mongodb-is-for-rubyists-boston-ruby&quot; title=&quot;MongoDB (is) For Rubyists - Boston Ruby&quot;&gt;MongoDB (is) For Rubyists &amp;#8211; Boston Ruby&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=kb-mongodb-for-rubyists-boston-slides-091209151501-phpapp02&amp;stripped_title=mongodb-is-for-rubyists-boston-ruby&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=kb-mongodb-for-rubyists-boston-slides-091209151501-phpapp02&amp;stripped_title=mongodb-is-for-rubyists-boston-ruby&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;&lt;/div&gt;&lt;/div&gt;</content>
  </entry>
  
  <entry>
    <title>MongoDB Aggregation III: Map-Reduce Basics</title>
    <link href="/blog/2009/12/mongodb-map-reduce-basics/" />
    <id>tag:kylebanker.com,2009-12-12:1260638117</id>
    <updated>2009-12-12T12:15:17-05:00</updated>
    <content type="html">&lt;p&gt;If you&amp;#8217;re accustomed to working with relational databases, the thought of
specifying aggregations with map-reduce may be a bit intimidating. Here, in the
third in &lt;a href=&quot;http://kylebanker.com/blog/2009/11/mongodb-count-group/&quot;&gt;a series of articles on MongoDB aggregation&lt;/a&gt;, I explain map-reduce.
After reading this, and with a little practice, you&amp;#8217;ll be able to apply the
map-reduce paradigm to a huge number of aggregation problems.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;A comments example, of course&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s start with an example. Suppose we have a collection of comments with the following document structure:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;{ text: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;lmao! great article!&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
  author: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;kbanker&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
  votes: &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here we have a comment authored by &amp;#8216;kbanker&amp;#8217; with two votes. Now, we want to find the total number of votes each comment author has earned across the entire comment collection. It&amp;#8217;s a problem easily solved with map-reduce.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Mapping&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;As its name suggests, map-reduce essentially involves two operations. The
first, specified by our &lt;strong&gt;map&lt;/strong&gt; function, formats our data as a series of
key-value pairs. Our key is the comment author&amp;#8217;s name (this makes sense only if
this username is unique). Our value is a document containing the number of
votes. We generate these key-value pairs by &lt;strong&gt;emitting&lt;/strong&gt; them. See below:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Our key is author's userame; &lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; our value, the number of votes for the current comment.&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;map&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;() {
  emit(&lt;span class=&quot;Variable&quot;&gt;this&lt;/span&gt;.author, {votes: &lt;span class=&quot;Variable&quot;&gt;this&lt;/span&gt;.votes});
};
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When we run map-reduce, the &lt;strong&gt;map&lt;/strong&gt; function is applied to each document. This
results in a collection of key-value pairs. What do we do with these results?
It turns out that we don&amp;#8217;t even have to think about them because they&amp;#8217;re
automatically passed on to our &lt;strong&gt;reduce&lt;/strong&gt; function.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Reducing&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Specifically, the &lt;strong&gt;reduce&lt;/strong&gt; function will be invoked with two arguments: a key
and an array of values associated with that key. Returning to our example, we
can imagine our &lt;strong&gt;reduce&lt;/strong&gt; function receiving something like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;reduce(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;kbanker&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, [{votes: &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;}, {votes: &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;}, {votes: &lt;span class=&quot;Constant&quot;&gt;4&lt;/span&gt;}]);
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Given that, it&amp;#8217;s easy to come up with a reduce function for tallying these votes:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Add up all the votes for each key.&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;reduce&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;key, values&lt;/span&gt;) {
  &lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; sum &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;;
  values.forEach(&lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(doc) {
    sum &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.votes;
  });
  &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; {votes: sum};
};
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Results&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;So how do we we run it? From the shell, we pass our map and reduce functions to the mapReduce helper:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Running mapReduce.&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; op &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; db.comments.mapReduce(map, reduce);

{
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;result&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;tmp.mr.mapreduce_1260567078_11&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;timeMillis&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;8&lt;/span&gt;,
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;counts&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : {
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;input&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;6&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;emit&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;6&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;output&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
  },
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;ok&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;,
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that running the mapReduce helper returns &lt;strong&gt;stats on the operation&lt;/strong&gt;; the
results of the operation itself are stored in a temporary collection. In this
case, that collection is &lt;strong&gt;tmp.mr.mapreduce_1260567078_11&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The other stats also prove informative. First is the operation time in
milliseconds. Next are the number of input documents, the number of times we
called emit (this can be more than once per document), and the number of output
documents. Finally, we can be assured that the operation has succeeded because &amp;#8220;ok&amp;#8221; is 1.&lt;/p&gt;
&lt;p&gt;Of course, what we really want are the results. To get them, just query the output collection:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Getting the results from the shell&lt;/span&gt;
db[op.result].&lt;span class=&quot;SupportFunction&quot;&gt;find&lt;/span&gt;();

{ &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;hwaet&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;value&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : { &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;votes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;21&lt;/span&gt; } }
{ &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;kbanker&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;value&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : { &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;votes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;13&lt;/span&gt; } }
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;How do I execute map-reduce from Ruby?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Running map-reduce from Ruby (irb) assuming&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; that @comments references the comments collection&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Specify the map and reduce functions in JavaScript, as strings&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; map    &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;function() { emit(this.author, {votes: this.votes}); }&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; reduce &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;function(key, values) { &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;var sum = 0; &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;values.forEach(function(doc) { &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt; sum += doc.votes; &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;}); &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;return {votes: sum}; &lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;};&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Pass those to the map_reduce helper method&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;results&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;comments&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;map_reduce&lt;/span&gt;(map, result)

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Since this method returns an instantiated results collection,&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; we just have to query that collection and iterate over the cursor.&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;results&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;().&lt;span class=&quot;Entity&quot;&gt;to_a&lt;/span&gt;
=&amp;gt; [{&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;hwaet&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;value&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;=&amp;gt;{&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;votes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;=&amp;gt;&lt;span class=&quot;Constant&quot;&gt;21.0&lt;/span&gt;}}, 
    {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;=&amp;gt;&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;kbanker&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;value&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;=&amp;gt;{&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;votes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;=&amp;gt;&lt;span class=&quot;Constant&quot;&gt;13.0&lt;/span&gt;}}
   ]
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Practice&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;ve followed along, you should understand the basics of map-reduce in
MongoDB. For all the details on options, &lt;a href=&quot;http://www.mongodb.org/display/DOCS/MapReduce&quot;&gt;see the docs&lt;/a&gt;. For extra practice, fire up the MongoDB shell and experiment away.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>MongoDB Aggregation II: Grouping Elaborated</title>
    <link href="/blog/2009/11/mongodb-advanced-grouping/" />
    <id>tag:kylebanker.com,2009-11-30:1259592018</id>
    <updated>2009-11-30T09:40:18-05:00</updated>
    <content type="html">&lt;p&gt;This is the second in a series of articles on MongoDB&amp;#8217;s aggregation functions.
In the &lt;a href=&quot;http://kylebanker.com/blog/2009/11/mongodb-count-group/&quot;&gt;first installment&lt;/a&gt;, 
we looked at &lt;strong&gt;count()&lt;/strong&gt;,
&lt;strong&gt;distinct()&lt;/strong&gt;, and some of the basics of
&lt;strong&gt;group()&lt;/strong&gt;. But &lt;strong&gt;group()&lt;/strong&gt; is rather a beast, so
here we take an extended example, and look at two deep features: finalizers and
key functions.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Counting and Sums&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s assume we&amp;#8217;re hosting a social news application with a collection of comments. A comment document might look like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Comment document&lt;/span&gt;
{ &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;id&lt;/span&gt;         =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;fcfa23342&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;user_id&lt;/span&gt;    =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;a0fb00004&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;text&lt;/span&gt;       =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;mongodb be so funky&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;upvotes&lt;/span&gt;    =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;12&lt;/span&gt;
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;downvotes&lt;/span&gt;  =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;4&lt;/span&gt;
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;upvoters&lt;/span&gt;   =&amp;gt; [&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;a0fb00004&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;a0fb00005&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, ...]
  &lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;downvoters&lt;/span&gt; =&amp;gt; [&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;a0fb00000&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;a0fb00001&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, ...]
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now, we want to know which users have garnered the greatest number of upvotes
and downvotes. With &lt;strong&gt;group()&lt;/strong&gt;, this is straightforward. Our
results will include totals for upvotes and downvotes, with a running total
counting each comment. Hence, our initial document has three keys:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Initial, aggregator document (in JavaScript)&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; setupDoc &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; {
  upvote_total: &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;,
  downvote_total: &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;,
  count: &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Next, we need a reduce function that adds to these totals:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Reduce function (in JavaScript)&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;voteAdder&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;doc, prev&lt;/span&gt;) {
  prev.upvote_total   &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.upvotes;
  prev.downvote_total  &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.downvotes;
  prev.count          &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, we pass these to &lt;strong&gt;group()&lt;/strong&gt;, specifying
&lt;strong&gt;user_id&lt;/strong&gt; as the key to group by:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; In the MongoDB JS shell:&lt;/span&gt;
db.comments.group({
  key:     {user_id: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;},
  initial:  setupDoc,
  reduce:   voteAdder
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As expected, this returns an array of documents with the totals we sought:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Result of group()&lt;/span&gt;
[
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;user_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;a0fc46004&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upvote_total&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;Constant&quot;&gt;24&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;downvote_total&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;30&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;count&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     : &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
  },
  { 
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;user_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;a0fc46005&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;upvote_total&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;Constant&quot;&gt;68&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;downvote_total&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;count&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     : &lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;
  }
]
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Limiting&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If we don&amp;#8217;t need totals for the entire collection of comments, we might be
tempted to limit our results by modifying the reduce function:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Reduce function, totaling comments from 2009&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;voteAdderWithFilter&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;doc, prev&lt;/span&gt;) {
  &lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; startDate &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;).&lt;span class=&quot;SupportFunction&quot;&gt;getTime&lt;/span&gt;();
  &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;(doc.created_at.&lt;span class=&quot;SupportFunction&quot;&gt;getTime&lt;/span&gt;() &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;&lt;/span&gt; startDate) {
    prev.upvote_total  &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.upvotes;
    prev.downvote_tota &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.downvotes;
  }
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This gets us our totals, albeit inefficiently. The better route is to specify a query
filter. That way, we can take advantage of MongoDB&amp;#8217;s query engine and any indexes 
we may have declared. So we add a &lt;strong&gt;cond:&lt;/strong&gt; key to
&lt;strong&gt;group()&lt;/strong&gt;, passing in a document selector like we might pass to
&lt;strong&gt;find()&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; group(), with a query condition&lt;/span&gt;
db.comments.group({
  key:     {user_id: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;},
  initial:  setupDoc,
  reduce:   voteAdder,
  cond:    {created_at: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;$gte&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;)}}
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will assure that our groupings only include comments from 2009.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;The Finalizer&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;But what if counting and summing aren&amp;#8217;t enough? Maybe we need the average
number of votes per user, or perhaps we need to extract a weighted score. Enter
&lt;strong&gt;the finalizer&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sounds foreboding.&lt;/p&gt;
&lt;p&gt;But it&amp;#8217;s just an arbitrary function. It receives each of our result documents and performs whatever
operations we wish on them:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; A finalizer for averaging votes and calculating a score&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;finalizer&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;doc&lt;/span&gt;) {
  doc.average_upvotes &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; doc.upvotes / doc.count;
  doc.score &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; (&lt;span class=&quot;Constant&quot;&gt;0.8&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; doc.upvotes) &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; (&lt;span class=&quot;Constant&quot;&gt;0.2&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt; doc.downvotes);
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run &lt;strong&gt;group()&lt;/strong&gt; again, this time specifying the finalizer:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; group(), with a query condition and finalizer&lt;/span&gt;
db.comments.group({
  key:     {user_id: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;},
  initial:  setupDoc,
  reduce:   voteAdder,
  cond:    {created_at: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;$gte&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;)}},
  finalize: finalizer
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And the richer our results become:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; group() results, enriched with a finalizer&lt;/span&gt;
[
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;user_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;a0fc46004&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;uptotal&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;Constant&quot;&gt;24&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;downtotal&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;30&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;count&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     : &lt;span class=&quot;Constant&quot;&gt;2&lt;/span&gt;
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;average_upvotes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;12&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;score&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;          : &lt;span class=&quot;Constant&quot;&gt;13.2&lt;/span&gt;
  },
  { 
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;user_id&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;a0fc46005&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;uptotal&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;   : &lt;span class=&quot;Constant&quot;&gt;68&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;downtotal&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; : &lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;count&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;     : &lt;span class=&quot;Constant&quot;&gt;3&lt;/span&gt;
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;average_upvotes&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;22.667&lt;/span&gt;,
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;score&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;          : &lt;span class=&quot;Constant&quot;&gt;53.8&lt;/span&gt;
  }
]
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;But wait!&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MongoDB&amp;#8217;s &lt;strong&gt;group()&lt;/strong&gt; function has yet another trick up its
sleeves (right?!). Because you might not want to group by any of the available
keys in your documents, there&amp;#8217;s the &lt;strong&gt;keyf&lt;/strong&gt; option. You pass it a
function that returns a document to group by.&lt;/p&gt;
&lt;p&gt;This actually makes loads of sense. You may want to group users by last name:
A-F, G-L, etc. Or what if you want to arrange pageviews by month, or week, or hour.
If you haven&amp;#8217;t cached these values, this isn&amp;#8217;t so easy to do. But with a
&lt;strong&gt;keyf&lt;/strong&gt;unction, you&amp;#8217;ve got it made.&lt;/p&gt;
&lt;p&gt;So, to take an arbitrary but potentially interesting example, what if we
decided to group our comments by length? We could define our
comments as short (&amp;lt; 10 words), medium (between 10 and 50 words), 
and long (&amp;gt; 50 words), and see if any correlation between comment length
and votes exists.&lt;/p&gt;
&lt;p&gt;Can you see how our &lt;strong&gt;keyf&lt;/strong&gt; function would look?&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; A key function for grouping comments as short, medium, and long&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;commentLength&lt;/span&gt; = &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;comment&lt;/span&gt;) {
  &lt;span class=&quot;Storage&quot;&gt;var&lt;/span&gt; length &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; comment.&lt;span class=&quot;SupportConstant&quot;&gt;text&lt;/span&gt;.&lt;span class=&quot;SupportFunction&quot;&gt;split&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).&lt;span class=&quot;SupportConstant&quot;&gt;length&lt;/span&gt;;
  &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;(length &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;10&lt;/span&gt;) {
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; {&lt;span class=&quot;Storage&quot;&gt;short&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;};
  &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt;(length &lt;span class=&quot;Keyword&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;&amp;amp;&lt;/span&gt; length &lt;span class=&quot;Keyword&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;50&lt;/span&gt;) {
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; {medium: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;};
  &lt;span class=&quot;Keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;return&lt;/span&gt; {large: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;};
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then, just replace &lt;strong&gt;key&lt;/strong&gt; with &lt;strong&gt;keyf&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; group(), with a query condition and finalizer&lt;/span&gt;
db.comments.group({
  keyf:     commentLength,
  initial:  setupDoc,
  reduce:   voteAdder,
  cond:    {created_at: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;$gte&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;)}},
  finalize: finalizer
});
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;keyf&lt;/strong&gt; allows us to group our data in many a sane and zany
way. Exploring that range of possibility is left as an exercise to the reader.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Et, violà&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;So, that is indeed the gamut of &lt;strong&gt;group()&lt;/strong&gt; as of MongoDB 1.1.4.
Please feel free to drop a note on how you&amp;#8217;re using it in your apps.&lt;/p&gt;
&lt;p&gt;In the next article, I present the basics of the not-so-widely-understood
&lt;a href=&quot;http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/&quot;&gt;map-reduce&lt;/a&gt;.
Curious? &lt;a href=&quot;http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/&quot;&gt;Read on&amp;#8230;&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>MongoDB Aggregation I: Counting and Grouping</title>
    <link href="/blog/2009/11/mongodb-count-group/" />
    <id>tag:kylebanker.com,2009-11-22:1258951654</id>
    <updated>2009-11-22T23:47:34-05:00</updated>
    <content type="html">&lt;p&gt;This is the first in a series of articles detailing the syntax, patterns, and
use cases for MongoDB&amp;#8217;s aggregation functions:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Counting and Grouping&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://kylebanker.com/blog/2009/11/mongodb-advanced-grouping/&quot;&gt;Grouping Elaborated&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://kylebanker.com/blog/2009/12/mongodb-map-reduce-basics/&quot;&gt;Map-reduce Basics&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here, as an introduction, we cover the basics of &lt;b&gt;count()&lt;/b&gt;, &lt;b&gt;distinct()&lt;/b&gt;, and &lt;b&gt;group()&lt;/b&gt;.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;count()&lt;/b&gt; simply returns the number of documents in a collection.&lt;/p&gt;
&lt;p&gt;Suppose we have a collection where each document represents a pageview. We can
get the total number of pageviews like so:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Javascript&lt;/span&gt;
db.pageviews.count()
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Ruby&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;pageviews&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;count&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Admittedly more useful would be the number of pageviews from a given month:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Javascript&lt;/span&gt;
db.pageviews.count({date: 
  {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$gte&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Nov 1, 2009&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$lt&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;Date&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;Dec 1, 2009&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)}})
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Ruby&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;pageviews&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;({&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;date&lt;/span&gt; =&amp;gt; 
  {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$gte&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Support&quot;&gt;Time&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;utc&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;11&lt;/span&gt;), &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$lt&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Support&quot;&gt;Time&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;utc&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;2009&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;12&lt;/span&gt;)}}).&lt;span class=&quot;Entity&quot;&gt;count&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that we&amp;#8217;ve passed a document selector to the &lt;b&gt;count() &lt;/b&gt; method.
This ensures that only documents matching the selector are included in the total.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Distinct&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Another simple but useful aggregator is &lt;b&gt;distinct()&lt;/b&gt;, which returns an
array of unique values for a given key in a collection. We can specify a
root-level key or a nested key. Here, we request unique ip-addresses and user agents:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Javascript&lt;/span&gt;
db.pageviews.distinct(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;ip-address&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
db.pageviews.distinct({&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;})
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Ruby&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;pageviews&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;distinct&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;ip-address&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;pageviews&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;distinct&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Group&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MongoDB&amp;#8217;s &lt;b&gt;group()&lt;/b&gt; command provides some of the same functionality as
SQL&amp;#8217;s GROUP BY, although the syntax is rather different. Like most database
commands, &lt;b&gt;group()&lt;/b&gt; takes a document whose keys designate the parameters
of the operation:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Group by user-agent, returning a sum for each user-agent.&lt;/span&gt;
db.pageviews.group(
{
 key: {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;true&lt;/span&gt;}, 
 initial: {sum: &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;}, 
 &lt;span class=&quot;Entity&quot;&gt;reduce&lt;/span&gt;: &lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;doc, prev&lt;/span&gt;) { prev.sum &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;}
});

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Returns (Note: simplified)&lt;/span&gt;
[
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Mozilla 5.0 / Gecko&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sum&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;241&lt;/span&gt;
  },
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Mozilla 5.0 / Webkit&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; 
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sum&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;79&lt;/span&gt;
  }
]
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Looking at the command itself, the first parameter is clear enough:
&lt;b&gt;key&lt;/b&gt; specifies which key or keys to group by.&lt;/p&gt;
&lt;p&gt;The next two parameters, &lt;b&gt;initial&lt;/b&gt; and &lt;b&gt;reduce&lt;/b&gt;, may be less
familiar. Their use is analgous to most programming language implementations of
&lt;b&gt;inject&lt;/b&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;b&gt;initial&lt;/b&gt;, we provide a base, aggregator document for each grouped
result. Here, we&amp;#8217;re just saying that the base document should contain one key,
&lt;b&gt;sum&lt;/b&gt;, having a value of 0.&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Initial, aggregator document&lt;/span&gt;
{sum: &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The reduce function take two parameters: 1) the current document being
processed, and 2) the document we&amp;#8217;re aggregating over (which starts out as the &lt;b&gt;initial&lt;/b&gt; document described above).&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Reduce function&lt;/span&gt;
&lt;span class=&quot;Storage&quot;&gt;function&lt;/span&gt;(doc, prev) {
  prev.sum &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;;
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this simple case, we don&amp;#8217;t even use the doc parameter; we just 
increment our initial document&amp;#8217;s &lt;b&gt;sum&lt;/b&gt; key. Since a new document is used
for each grouping, the &lt;b&gt;group()&lt;/b&gt; function returns all those documents as
an array, adding any keys we&amp;#8217;re grouping by, so that we&amp;#8217;re given a result like this:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;//&lt;/span&gt; Result (Note: simplified)&lt;/span&gt;
[
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Mozilla 5.0 / Gecko&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sum&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;241&lt;/span&gt;
  },
  {
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;Mozilla 5.0 / Webkit&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; 
    &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sum&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;: &lt;span class=&quot;Constant&quot;&gt;79&lt;/span&gt;
  }
]
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The command we&amp;#8217;ve been describing could be run from Ruby as follows:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; Group from Ruby&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;pageviews&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;group&lt;/span&gt;([&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.agent&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;], &lt;span class=&quot;Constant&quot;&gt;nil&lt;/span&gt;, {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sum&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Constant&quot;&gt;0&lt;/span&gt;},
  &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;function(doc, prev) { prev.sum += 1}&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the second parameter, here &lt;b&gt;nil&lt;/b&gt;, we can provide a query selector, so
that &lt;b&gt;group()&lt;/b&gt; will only operate over a certain subset of the collection.
In the &lt;a href=&quot;http://kylebanker.com/blog/2009/11/mongodb-advanced-grouping/&quot;&gt;next installment&lt;/a&gt;, we explore that along with some of &lt;b&gt;group()&lt;/b&gt;&amp;#8217;s more advanced features.&lt;/p&gt;
&lt;p&gt;In the meantime, fire the up the &lt;a href=&quot;http://www.mongodb.org/display/DOCS/mongo+-+The+Interactive+Shell&quot;&gt;MongoDB shell&lt;/a&gt;, experiment with some of these
functions, and don&amp;#8217;t hesitate to &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Aggregation&quot;&gt;consult the docs&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>MongoDB in Three Minutes</title>
    <link href="/blog/2009/11/mongodb-in-three-minutes/" />
    <id>tag:kylebanker.com,2009-11-05:1257472772</id>
    <updated>2009-11-05T20:59:32-05:00</updated>
    <content type="html">&lt;p&gt;Couple nights back, I was given three minutes to demonstrate MongoDB before a
somewhat large group of people who&amp;#8217;d never heard of it. &lt;a href=&quot;http://github.com/banker/mongodb_examples&quot;&gt;Source code is on
github&lt;/a&gt;. At one minute each, the highlights are these:&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;1. Schema-free documents.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MongoDB is schema-free; this means that the structure of MongoDB data need not be defined up front.
MongoDB stores data as collections of documents. A document can be thought of
as a JSON object, Python dictionary, or Ruby hash (among other things). Documents are natural,
elegant ways of representing data, and are of the essence of MongoDB.&lt;/p&gt;
&lt;p&gt;Suppose we want to download a few tweets.&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;#&lt;/span&gt; DB connection and collection&lt;/span&gt;
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;  &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;Mongo&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;Connection&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;new&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;db&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;DATABASE_NAME&lt;/span&gt;)
&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;collection&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;nyc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
 
(&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;..&lt;span class=&quot;Constant&quot;&gt;5&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;page&lt;/span&gt;|
  &lt;span class=&quot;Support&quot;&gt;Twitter&lt;/span&gt;::&lt;span class=&quot;Entity&quot;&gt;Search&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;new&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;nyc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;).&lt;span class=&quot;Entity&quot;&gt;page&lt;/span&gt;(page).&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;tweet&lt;/span&gt;|
    &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;save&lt;/span&gt;(tweet)
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;That gets us the first five pages of &amp;#8216;nyc&amp;#8217;-related tweets and saves them to the
our &amp;#8216;nyc&amp;#8217; collection in the db. For each tweet, the Twitter gem returns a Ruby
hash, which saves naturally to the database.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;2. Dynamic queries.&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;MongoDB speaks the language of documents, enabling expressive queries. To take
a few examples:&lt;/p&gt;
&lt;p&gt;We can query for a specific key:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;DB&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;collection&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;nyc&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;)
  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;username&lt;/span&gt; =&amp;gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;hwaet&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)  
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or on a nested key pointing to an array:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.followers&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;234352343&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;})  
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can search a field using a regular expression:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;text&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;StringRegexp&quot;&gt;&lt;span class=&quot;StringRegexp&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;StringRegexp&quot;&gt;z.+&lt;/span&gt;&lt;span class=&quot;StringRegexp&quot;&gt;&lt;span class=&quot;StringRegexp&quot;&gt;/&lt;/span&gt;&lt;/span&gt;})  
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or query across a date range:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;created_at&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; {&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;$lte&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; =&amp;gt; &lt;span class=&quot;Support&quot;&gt;Time&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt; (&lt;span class=&quot;Constant&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;Keyword&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;24&lt;/span&gt;)}})  
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And all of this can be efficient because each collection can define up to 40
secondary indexes:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;create_index&lt;/span&gt;([&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;text&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;])
  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;create_index&lt;/span&gt;([&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;user.followers&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;])
  &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;create_index&lt;/span&gt;([from_user, &lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;], [&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;created_at&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;Constant&quot;&gt;1&lt;/span&gt;])
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;3. Binary Storage&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;What if we want to store images, videos, or music? MongoDB stores arbitrary
binary data, too.&lt;/p&gt;
&lt;p&gt;This code goes through our collection of tweets, fetches each user&amp;#8217;s profile
image, and saves it to the database using GridFS (i.e., MongoDB&amp;#8217;s specification for storing
larger binary objects).&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;nyc&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;find&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;tweet&lt;/span&gt;|
  filename &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; tweet[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;from_user&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;downcase&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;.jpg&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;Support&quot;&gt;GridStore&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;exist?&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;, filename)
 
  &lt;span class=&quot;Support&quot;&gt;GridStore&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;Variable&quot;&gt;&lt;span class=&quot;Variable&quot;&gt;@&lt;/span&gt;db&lt;/span&gt;, filename, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;w+&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do &lt;/span&gt;|&lt;span class=&quot;Variable&quot;&gt;file&lt;/span&gt;|
    data &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;Entity&quot;&gt;open&lt;/span&gt;(tweet[&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;profile_image_url&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]).&lt;span class=&quot;Entity&quot;&gt;read&lt;/span&gt;
    file.&lt;span class=&quot;Entity&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;image/jpeg&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
    file.&lt;span class=&quot;Entity&quot;&gt;puts&lt;/span&gt; data
  &lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If we want to serve those images with sinatra:&lt;/p&gt;
&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;twilight&quot;&gt;get &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/images/:id&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;do&lt;/span&gt;
  content_type &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;image/jpeg&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  filename &lt;span class=&quot;Keyword&quot;&gt;=&lt;/span&gt; params[&lt;span class=&quot;Constant&quot;&gt;&lt;span class=&quot;Constant&quot;&gt;:&lt;/span&gt;id&lt;/span&gt;].&lt;span class=&quot;Entity&quot;&gt;downcase&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;.jpg&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;Support&quot;&gt;GridStore&lt;/span&gt;.&lt;span class=&quot;Entity&quot;&gt;read&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;DB&lt;/span&gt;, filename)
&lt;span class=&quot;Keyword&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;&lt;strong&gt;Speed, scalability, ease of use&amp;#8230;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;All this is to say nothing of
&lt;a href=&quot;http://blog.boxedice.com/2009/07/25/choosing-a-non-relational-database-why-we-migrated-from-mysql-to-mongodb/&quot;&gt;production&lt;/a&gt;
&lt;a href=&quot;http://www.businessinsider.com/how-we-use-mongodb-2009-11&quot;&gt;case&lt;/a&gt;
&lt;a href=&quot;http://compoundthinking.com/blog/index.php/2009/07/16/turbogears-on-sourceforge/&quot;&gt;studies&lt;/a&gt; or &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Sharding&quot;&gt;paths
to scalability&lt;/a&gt;. Interested readers
are encouraged to &lt;a href=&quot;http://www.mongodb.org/display/DOCS/Downloads&quot;&gt;download a binary&lt;/a&gt; and start experimenting.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Review: Finite and Infinite Games</title>
    <link href="/blog/2009/08/review-finite-and-infinite-games-carse/" />
    <id>tag:kylebanker.com,2009-08-17:1250528226</id>
    <updated>2009-08-17T12:57:06-04:00</updated>
    <content type="html">&lt;p&gt;Western thinkers are fond of dividing the world in two. Though simplistic,
it&amp;#8217;s effective. The flesh vs. spirit characters in the novels of &lt;a href=&quot;http://en.wikipedia.org/wiki/Nikos_Kazantzakis&quot;&gt;Nikos
Kazantzakis&lt;/a&gt;
are memorable. The &lt;a href=&quot;http://www.amazon.com/Have-Be-Erich-Fromm/dp/0826409121&quot;&gt;modes of having and
being&lt;/a&gt; in &lt;a href=&quot;http://en.wikipedia.org/wiki/Erich_Fromm&quot;&gt;Erich
Fromm&lt;/a&gt; are illustrative.
And when &lt;a href=&quot;http://en.wikipedia.org/wiki/James_P._Carse&quot;&gt;James P. Carse&lt;/a&gt; wrote about finite and infinite games, we were given
another dichotomous lens for peering into the mysteries of human being.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;http://www.amazon.com/Finite-Infinite-Games-James-Carse/dp/0345341848&quot;&gt;Finite and Infinite
Games&lt;/a&gt;,
Carse asks us to construe human activities &amp;#8211; working, loving, walking, cooking,
reading, driving &amp;#8211; in fact, anything you can imagine &amp;#8211; as kinds of games, and
invites us to consider whether we play these games finitely or infinitely.&lt;/p&gt;
&lt;p&gt;Finite games are meant to come to an end. They are played for the purpose of
winning, vanquishing an opponent, or bringing about some desired outcome: a
student gains admission to the Ivy League, a worker receives a promotion, a
country goes to war and wins or loses.&lt;/p&gt;
&lt;p&gt;Finite games are &lt;i&gt;theatrical&lt;/i&gt;. They require an audience to say who has won, to
agree upon an end, and to confer titles upon the winners, which is all but
certain for finite players:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The outcome of a finite game is the past waiting to happen. Whoever
plays toward a certain outcome desires a particular past. By competing for a
future prize, finite players compete for a prized past.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Infinite players, on the other hand, play with the spirit of continuing
play. Such play is not theatrical but &lt;i&gt;dramatic&lt;/i&gt;. The drama lies in choosing an
undefined path that is not ultimately for oneself. Artists and visionaries,
those who revel in spontaneity and laughter, who are aware of their places in
society but do not take their places seriously &amp;#8212; these are infinite players.&lt;/p&gt;
&lt;p&gt;It soon becomes clear that finitude and infinity are not intrinsic to
particular games but to the attitude of the player. The simple act of eating a
meal can be either a finite or infinite game. I might be eating with a certain
end in mind, to gain energy or lose weight or make a political statement. I 
might also eat spontaneously, for no &lt;i&gt;reason&lt;/i&gt; other than to eat.&lt;/p&gt;
&lt;p&gt;To read this book is to subject our attitudes to the finite-infinite paradigm,
discover that we&amp;#8217;re engaged in both modes of play, and consider to what extent
we&amp;#8217;re comfortable with this. Our conclusion may depend on our response to the book&amp;#8217;s
compelling final chapter, whose subject is myth and whose main idea is
illustrated by the story of Copernicus:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Copernicus was a traveler who went with a hundred pairs of eyes,
daring to look again at all that is familiar in the hope of vision. What we
hear in this account is the ancient saga of the solitary wanderer, the
peregrinus, who risks anything for the sake of surprise. True, at a certain
point he stopped to look and may have ended his journey as a Master Player
setting down bounded fact. But what resounds most deeply in the life of
Copernicus is the journey that made knowledge possible and not the knowledge
that made the journey successful.&lt;/p&gt;
&lt;/blockquote&gt;</content>
  </entry>
  
</feed>
