{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div style=\"font-size: 18px;\">\n",
    "Master in Computer Science <br>\n",
    "Master in Data Science and Economics\n",
    "</div>\n",
    "\n",
    "<div style=\"font-size: 24px; margin-top: 2rem;\">Algorithms for massive datasets</div>\n",
    "\n",
    "<div style=\"font-size: 36px; margin-top: 5rem; margin-bottom: 3rem; text-align: center;\">noSQL databases: MongoDB</div>\n",
    "\n",
    "Prof. Dario Malchiodi\n",
    "\n",
    "<div style=\"width: 50%; margin: auto;\">\n",
    "<img src=\"img/logo-di.jpg\" style=\"height: 4rem; margin-top: 4.75rem; float: left;\">\n",
    "<img src=\"img/logo-demm.jpg\" style=\"height: 3.5rem; margin-top: 5rem; margin-left: 0.5rem; margin-right: 0.5rem; float: left;\">\n",
    "<img src=\"img/logo-unimi.jpg\" style=\"height: 3.5rem; margin-top: 5rem; float: left;\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">RDBMS</div>\n",
    "\n",
    "<p>Organize data exploiting <em>relations</em> (that is, sets of tuples)</p>\n",
    "\n",
    "<p>Among the most used:</p>\n",
    "\n",
    "<div class=\"left-col\">\n",
    "\n",
    " <ul>\n",
    "   <li>Oracle</li>\n",
    "   <li>MySQL</li>\n",
    "   <li>PostgreSQL</li>\n",
    "   <li>SQLite</li>\n",
    " </ul>\n",
    "\n",
    "</div>\n",
    "\n",
    "<div class=\"right-col\">\n",
    "\n",
    "<img src=\"img/table.png\" width=\"50%\">\n",
    "\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">noSQL DBMS</div>\n",
    "\n",
    "<p>Again, a wide variety:</p>\n",
    "\n",
    "<ul>\n",
    "  <li>MongoDB</li>\n",
    "  <li>CouchDB</li>\n",
    "  <li>Cassandra</li>\n",
    "  <li>Redis</li>\n",
    "</ul>\n",
    "\n",
    "<p><a href=\"http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis\">This post</a> is a good starting point in order to understand how they differ.</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Why noSQL?</div>\n",
    "\n",
    "<p>Because the world is messy!</p>\n",
    "\n",
    "<ul>\n",
    " <li>Data often happen <strong>not</strong> to follow a predefined scheme</li>\n",
    " <li>specifically, in the data science domain</li>\n",
    "</ul>\n",
    "\n",
    "<p>Incidentally, noSQL gives us interesting advantages:</p>\n",
    "\n",
    "<ul>\n",
    " <li>dealing with complex structures at once</li>\n",
    " <li>scaling horizontally (sharding)</li>\n",
    " <li>robustness through redundancy</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Enter MongoDB</div>\n",
    "\n",
    "<p>MongoDB (<a href=\"http://www.mongodb.com\">http://www.mongodb.com</a>) is a <strong>document-oriented</strong> open source noSQL DBMS.</p>\n",
    "\n",
    "<ul>\n",
    " <li>Developed since 2007</li>\n",
    " <li>In production since the 1.4 release (march 2010)</li>\n",
    " <li>Latest stable release: 4.2, released on August 2019</li>\n",
    " <li>(the name refers to «humongous»)</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Enter MongoDB</div>\n",
    "\n",
    "<p>MongoDB has, however, some critical points:</p>\n",
    "\n",
    "<ul>\n",
    " <li>indices are organized using B-trees requiring a lot of RAM</li>\n",
    " <li>databases tend to take more disk space than their omologous on a RDBMS</li>\n",
    " <li>no transactions support is provided</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">In a classical DBMS</div>\n",
    "\n",
    "<ul>\n",
    " <li>A db agregates tables</li>\n",
    " <li>A table aggregates rows</li>\n",
    " <li>A row aggregates values for (fixed) attributes</li>\n",
    " <li>Tables can be joined</li>\n",
    " <li>A <strong>schema</strong> fixes how rows in a table are structured</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">In a <del>classical</del> document oriented DBMS</div>\n",
    "\n",
    "<ul>\n",
    "    <li>A db agregates <del>tables</del> collections</li>\n",
    "    <li>A <del>table</del> collection aggregates <del>rows</del> documents</li>\n",
    "    <li>A <del>row</del> aggregates <del>values for (fixed) attributes</del> (variable) fields encoded as (name, value) pairs</li>\n",
    " <li>Tables <strong>cannot</strong> be joined (at least, not directly)</li>\n",
    "    <li><del>A <strong>schema</strong> fixes how</del> rows in a table are not structured</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Representation of data</div>\n",
    "\n",
    "<ul>\n",
    " <li>The row of a relational database can be encoded as a sequence of fixed typed values</li>\n",
    " <li>This is not possible in MongoDB, according to the lack of a prefixed structure in documents</li>\n",
    " <li>This is why documents are described using the JSON format</li>\n",
    " <li>JSON (JavaScript Object Notation, <a href=\"http://www.json.org\">http://www.json.org</a>) is a lightweight data-interchange format, easy to read (by humans) and to parse (by computers)</li>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Lightweightness</div>\n",
    "\n",
    "<p>Simply put:</p>\n",
    "\n",
    "<pre>['one', 'two', 'three']</pre>\n",
    "\n",
    "<p>is much more simple than</p>\n",
    "\n",
    "```\n",
    "<array>\n",
    "  <element>one</element>\n",
    "  <element>two</element>\n",
    "  <element>three</element>\n",
    "</array>```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">JSON: Backus–Naur form</div>\n",
    "\n",
    "<img src=\"img/object.gif\">\n",
    "<img src=\"img/array.gif\">\n",
    "<img src=\"img/value.gif\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">An example</div>\n",
    "\n",
    "<table>\n",
    "  <tr>\n",
    "    <th>First Name</th>\n",
    "    <th>Last Name</th>\n",
    "    <th>License plate</th>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>Donald</td>\n",
    "    <td>Duck</td>\n",
    "    <td>313</td>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>Mickey</td>\n",
    "    <td>Mouse</td>\n",
    "    <td>113</td>\n",
    "  </tr>\n",
    "  <tr>\n",
    "    <td>Goofy</td>\n",
    "    <td></td>\n",
    "    <td>114</td>\n",
    "  </tr>\n",
    "</table>\n",
    "\n",
    "<p>becomes</p>\n",
    "\n",
    "<pre>\n",
    "{'first-name': 'Donald', 'last-name': 'Duck',\n",
    " 'license-plate': 313},\n",
    "{'first-name': 'Mickey', 'last-name': 'Mouse',\n",
    " 'license-plate': 113},\n",
    "{'first-name': 'Goofy', 'license-plate': 114}\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Internal representation</div>\n",
    "\n",
    "<p>MongoDB actually stores data using the BSON format (Binary JSON, <a href=\"http://bsonspec.org\">http://bsonspec.org/</a>), designed in order to:</p>\n",
    "\n",
    "<ul>\n",
    "  <li>efficiently serialize JSON documents</li>\n",
    "  <li>easily traverse such documents</li>\n",
    "  <li>keep lightweightness</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Embedded documents</div>\n",
    "\n",
    "<p>Documents can be nested:</p>\n",
    "\n",
    "<pre>\n",
    "{'first-name': 'Donald',\n",
    " 'last-name': 'Duck',\n",
    " 'car': {'model': 'American Bantam',\n",
    "         'license-plate': 313}\n",
    "}\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Arrays</div>\n",
    "\n",
    "<p>Arrays are first-class objects in BSON:</p>\n",
    "\n",
    "\n",
    "<pre>\n",
    "{'first-name': 'Donald',\n",
    " 'last-name': 'Duck',\n",
    " 'car': {'model': 'American Bantam',\n",
    "         'license-plate': 313},\n",
    " 'relatives': ['Huey', 'Dewey', 'Louie']\n",
    "}\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MongoDB components</div>\n",
    "\n",
    "<table>\n",
    "  <tr><td>`mongod`</td><td>server (listening on port 27017)</td></tr>\n",
    "  <tr><td>mongo</td><td>javascript client</td></tr>\n",
    "  <tr><td>API</td><td>for Python, Java, Scala, Ruby, C, C++, ... (<a href=\"http://api.mongodb.org\">http://api.mongodb.org</a>)</td></tr>\n",
    "  <tr><td>installation</td><td><a href=\"https://docs.mongodb.com/manual/installation\">https://docs.mongodb.com/manual/installation</a></td></tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Manual execution</div>\n",
    "\n",
    "<pre>\n",
    "$ mongod --dbpath .\n",
    "2018-05-28T08:47:04.813+0200 I CONTROL  [initandlisten] MongoDB starting : pid=5172 port=27017 dbpath=. ...\n",
    "...\n",
    "\n",
    "$ mongo\n",
    "MongoDB shell version: 3.6.5\n",
    "...\n",
    ">\n",
    "</pre>\n",
    "\n",
    "<p>Luckily, you shouldn't need to do that for the interactive part. Ready?</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">We need data for the interactive part</div>\n",
    "\n",
    "Let's see who's who in Duckburg\n",
    "\n",
    "<img src=\"https://vignette.wikia.nocookie.net/scrooge-mcduck/images/1/11/Donald_Duck_Family_Tree.jpg/revision/latest/scale-to-width-down/1000?cb=20191124113106\" width=\"90%\" />\n",
    "<p style=\"text-align: right; font-size: 0.8rem;\">Source: <a href=\"https://scrooge-mcduck.fandom.com/wiki/Scrooge_McDuck_Wikia\">Scrooge McDuck Wiki</a></p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Preliminary operations</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pymongo\n",
    "client = pymongo.MongoClient('mongodb://mongodb:27017')\n",
    "duckburg_db = client.duckburg # silently creates db"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['admin', 'config', 'local']"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.list_database_names()       # it's empty, thus only default dbs will show up"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks = duckburg_db.ducks       # silently creates collection within db\n",
    "duckburg_db.list_collection_names()  # it will not show up, either"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Insertion of documents in a collection</div>\n",
    "\n",
    "The easiest way to put one document in a db is through the `insert_one` method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "r = ducks.insert_one({'first_name': 'Donald',\n",
    "                     'last_name': 'Duck',\n",
    "                     'gender': 'M',\n",
    "                     'car': {'model': 'American Bantam',\n",
    "                             'license_plate': 313},\n",
    "                     'birth_year': 1920,\n",
    "                     'first_appearance': 1934})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.InsertOneResult at 0x7fb208d24230>"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `insert_one` method returns an object gathering information about the result\n",
    "\n",
    "- `acknowledged` is a boolean field which becomes `True` once the write operation has been acknowledged (there's more here, but we will not consider it)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r.acknowledged"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `inserted_id` is a field containing an univocal identificator for the newly created document (we'll see this more in depth in a moment)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ObjectId('5e6a45bb917c9dd74a98fc93')"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r.inserted_id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">What happens after this first write operation?</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['admin', 'config', 'duckburg', 'local']\n",
      "['ducks']\n"
     ]
    }
   ],
   "source": [
    "print(client.list_database_names())\n",
    "print(duckburg_db.list_collection_names())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"slide-title\">A couple of technical details</div>\n",
    "\n",
    "<ul>\n",
    "  <li>maximum document size: 16MB</li>\n",
    "  <li>insertion also as an <em>upsert</em> (will see that later)</li>    \n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Let's populate the db</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.InsertManyResult at 0x7fb208d29870>"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "characters = [\n",
    "    {'first_name': 'Grandma', 'last_name': 'Duck', 'gender': 'F',\n",
    "     'car': {'model': 'Detroit Electric'},\n",
    "     'birth_year': 1833, 'first_appearance': 1943,\n",
    "     'hobbies': ['cooking', 'gardening']},\n",
    "    {'first_name': 'Scrooge', 'last_name': 'McDuck', 'gender': 'M',\n",
    "     'birth_year': 1867, 'first_appearance': 1947,\n",
    "     'hobbies': ['finance', 'savings', 'swimming in money bins']},\n",
    "    {'first_name': 'Gyro', 'last_name': 'Gearloose', 'gender': 'M',\n",
    "     'first_appearance': 1952, 'hobbies': ['invention', 'study']},\n",
    "    {'first_name': 'Ludwig', 'last_name': 'Von Drake', 'gender': 'M',\n",
    "     'first_appearance': 1961, 'hobbies': ['study']}\n",
    "]\n",
    "\n",
    "r = ducks.insert_many(characters)\n",
    "r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ObjectId('5e6a46b4917c9dd74a98fc94'),\n",
       " ObjectId('5e6a46b4917c9dd74a98fc95'),\n",
       " ObjectId('5e6a46b4917c9dd74a98fc96'),\n",
       " ObjectId('5e6a46b4917c9dd74a98fc97')]"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r.inserted_ids"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection I</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'_id': ObjectId('5e6a45bb917c9dd74a98fc93'),\n",
       " 'first_name': 'Donald',\n",
       " 'last_name': 'Duck',\n",
       " 'gender': 'M',\n",
       " 'car': {'model': 'American Bantam', 'license_plate': 313},\n",
       " 'birth_year': 1920,\n",
       " 'first_appearance': 1934}"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "duck = ducks.find_one()\n",
    "duck"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is no guarantee about <em>which</em> document will be retrieved!\n",
    "\n",
    "Note that JSON-encoded documents can be accessed using the dictionary syntax"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Donald'"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "duck['first_name']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Wait: _id? ObjectId?</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ObjectId('5e6a45bb917c9dd74a98fc93')"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.find_one()['_id']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "The `_id` field is automatically added to all documents; it contains a unique identifier to be used for indexing purposes. By default MongoDB sets it to an `ObjectId` instance"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ObjectId('5e6a4b8f917c9dd74a98fc98'),\n",
       " ObjectId('5e6a4b8f917c9dd74a98fc99'),\n",
       " ObjectId('5e6a4b8f917c9dd74a98fc9a')]"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from bson.objectid import ObjectId\n",
    "[ObjectId(), ObjectId(), ObjectId()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection II</div>\n",
    "\n",
    "Selection of _several_ documents matching a query is done through the `find` method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.cursor.Cursor at 0x7fb218306fd0>"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = ducks.find()\n",
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will speak of cursors in a while, for the moment we just cast them to lists in order to see which documents are matched"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'_id': ObjectId('5e6a45bb917c9dd74a98fc93'),\n",
       "  'first_name': 'Donald',\n",
       "  'last_name': 'Duck',\n",
       "  'gender': 'M',\n",
       "  'car': {'model': 'American Bantam', 'license_plate': 313},\n",
       "  'birth_year': 1920,\n",
       "  'first_appearance': 1934},\n",
       " {'_id': ObjectId('5e6a46b4917c9dd74a98fc94'),\n",
       "  'first_name': 'Grandma',\n",
       "  'last_name': 'Duck',\n",
       "  'gender': 'F',\n",
       "  'car': {'model': 'Detroit Electric'},\n",
       "  'birth_year': 1833,\n",
       "  'first_appearance': 1943,\n",
       "  'hobbies': ['cooking', 'gardening']},\n",
       " {'_id': ObjectId('5e6a46b4917c9dd74a98fc95'),\n",
       "  'first_name': 'Scrooge',\n",
       "  'last_name': 'McDuck',\n",
       "  'gender': 'M',\n",
       "  'birth_year': 1867,\n",
       "  'first_appearance': 1947,\n",
       "  'hobbies': ['finance', 'savings', 'swimming in money bins']},\n",
       " {'_id': ObjectId('5e6a46b4917c9dd74a98fc96'),\n",
       "  'first_name': 'Gyro',\n",
       "  'last_name': 'Gearloose',\n",
       "  'gender': 'M',\n",
       "  'first_appearance': 1952,\n",
       "  'hobbies': ['invention', 'study']},\n",
       " {'_id': ObjectId('5e6a46b4917c9dd74a98fc97'),\n",
       "  'first_name': 'Ludwig',\n",
       "  'last_name': 'Von Drake',\n",
       "  'gender': 'M',\n",
       "  'first_appearance': 1961,\n",
       "  'hobbies': ['study']}]"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(ducks.find())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"slide-title\">A more compact view of documents</div>\n",
    "\n",
    "Let's build a function providing a more compact visualization of ducks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Donald Duck'"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def show_duck(d):\n",
    "    try:\n",
    "        return '{0} {1}'.format(d['first_name'], d['last_name'])\n",
    "    except KeyError:\n",
    "        return d['first_name']\n",
    "    \n",
    "show_duck(duck)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: simple selectors I</div>\n",
    "\n",
    "<p>Based on the existence of specific fields</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Donald Duck', 'Grandma Duck']"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'car': {'$exists': True}})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: simple selectors II</div>\n",
    "\n",
    "<p>Selecting specific values</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Grandma Duck']"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'gender': 'F'})]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Ludwig Von Drake']"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'first_appearance': 1961})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: simple selectors III</div>\n",
    "\n",
    "<p>On the basis of a relation</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Gyro Gearloose', 'Ludwig Von Drake']"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'first_appearance': {'$gt': 1950}})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "<p>Filtering more than one value for a field</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Scrooge McDuck', 'Ludwig Von Drake']"
      ]
     },
     "execution_count": 90,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'first_appearance': {'$in': [1947,\n",
    "                                                                1961]}})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: complex selectors I</div>\n",
    "\n",
    "<p>Logical conjunction</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Donald Duck', 'Scrooge McDuck']"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'first_appearance': {'$lt': 1950}, \n",
    "                                   'gender': 'M'})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>Logical disjunction</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Donald Duck', 'Grandma Duck']"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'$or': [{'birth_year': {'$gt': 1900}}, \n",
    "                                           {'gender': 'F'}]})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: complex selectors II</div>\n",
    "\n",
    "<p>Array contents</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Gyro Gearloose', 'Ludwig Von Drake']"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'hobbies': 'study'})]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Ludwig Von Drake']"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'hobbies': ['study']})]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Gyro Gearloose']"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'hobbies.1': 'study'})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading a collection: complex selectors II</div>\n",
    "\n",
    "<p>Embedded documents</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Donald Duck']"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[show_duck(d) for d in ducks.find({'car.model': 'American Bantam'})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Reading collections: complex selectors III</div>\n",
    "\n",
    "<p>Regular expressions</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Donald Duck', 'Grandma Duck', 'Scrooge McDuck']"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import re\n",
    "regx = re.compile(\"uck$\", re.IGNORECASE)\n",
    "\n",
    "[show_duck(d) for d in ducks.find({'last_name': regx})]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "<div class=\"slide-title\">Beware of flexibility</div>\n",
    "\n",
    "Using regular expressions, as well as `$lt` or `$gt`, may lead to inefficiency in query execution."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Projections I</div>\n",
    "\n",
    "Implemented via a second argument to `find`\n",
    "\n",
    "<ul><li>either specifying fields to be shown...</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'_id': ObjectId('5e6a46b4917c9dd74a98fc96'),\n",
       "  'first_name': 'Gyro',\n",
       "  'last_name': 'Gearloose'}]"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(ducks.find({'hobbies.1': 'study'},\n",
    "                {'first_name': 1, 'last_name': 1}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Projections II</div>\n",
    "\n",
    "<ul><li>...or fields to be excluded.</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'first_name': 'Gyro', 'last_name': 'Gearloose'}]"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(ducks.find({'hobbies.1': 'study'},\n",
    "                {'_id': 0, 'gender': 0, 'birth_year': 0,\n",
    "                 'first_appearance': 0, 'hobbies': 0}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Projections III</div>\n",
    "\n",
    "<ul>\n",
    "  <li>The select/exclude options are mutually exclusive.</li>\n",
    "  <li>Only exception: the `_id` field can be filtered out:</li>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'first_name': 'Gyro', 'last_name': 'Gearloose'}]"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(ducks.find({'hobbies.1': 'study'},\n",
    "                {'first_name': 1, 'last_name': 1, '_id': 0}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Indicization I</div>\n",
    "\n",
    "<p>Collections can be indexed specifying a set of fields and the corresponding sort order (the syntax used by pymongo is in this case different from the original API)</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'first_name_1_first_appearance_-1'"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.create_index([('first_name', pymongo.ASCENDING),\n",
    "                    ('first_appearance', pymongo.DESCENDING)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Indicization II</div>\n",
    "\n",
    "<p>If a query is <em>covered</em> by an index, that is\n",
    "\n",
    "<ul>\n",
    "  <li>all filtered fields,</li>\n",
    "  <li>and all displayed fields</li>\n",
    "</ul>\n",
    "  \n",
    "<p>are within the index, the query is executed via index traversal.</p>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'first_name': 'Grandma', 'first_appearance': 1943},\n",
       " {'first_name': 'Gyro', 'first_appearance': 1952}]"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "regx = re.compile(\"^G.*\", re.IGNORECASE)\n",
    "list(ducks.find({'first_name': regx, 'first_appearance': {'$lt': 1955}},\n",
    "                {'_id': 0, 'first_name': 1, 'first_appearance': 1}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Cursors</div>\n",
    "\n",
    "`find` returns a <em>cursor</em> to the query resultset, accessible via\n",
    "\n",
    "- invocation of the `next` method (until `StopIteration`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Donald Duck\n",
      "Grandma Duck\n",
      "Scrooge McDuck\n",
      "Gyro Gearloose\n",
      "Ludwig Von Drake\n"
     ]
    }
   ],
   "source": [
    "cursor = ducks.find()\n",
    "try:\n",
    "    while True:\n",
    "        print(show_duck(cursor.next()))\n",
    "except StopIteration:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "- positional access through `__getitem__` (aka `[]`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Grandma Duck'"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cursor = ducks.find()\n",
    "show_duck(cursor[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Ludwig Von Drake'"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_duck(cursor[4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(even going back)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Scrooge McDuck'"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "show_duck(cursor[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "- `list` conversion, `for`-based iteration,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Donald Duck\n",
      "Grandma Duck\n",
      "Scrooge McDuck\n",
      "Gyro Gearloose\n",
      "Ludwig Von Drake\n"
     ]
    }
   ],
   "source": [
    "cursor = ducks.find()\n",
    "\n",
    "for d in cursor:\n",
    "    print(show_duck(d))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "- invocation of the `skip`, `limit`, `sort`, and `count` methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ludwig Von Drake\n",
      "Scrooge McDuck\n",
      "Gyro Gearloose\n",
      "Donald Duck\n",
      "Grandma Duck\n"
     ]
    }
   ],
   "source": [
    "cursor = ducks.find().sort('last_name', pymongo.DESCENDING)\n",
    "for d in cursor:\n",
    "    print(show_duck(d))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.cursor.Cursor at 0x7fb2180978d0>"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cursor.rewind()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">The <code>update</code> method is wicked!</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:3: DeprecationWarning: update is deprecated. Use replace_one, update_one or update_many instead.\n",
      "  This is separate from the ipykernel package so we can avoid doing imports until\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True}"
      ]
     },
     "execution_count": 119,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.insert_one({'first_name': 'Magica', 'last_name': 'De Spell',\n",
    "                  'first_appearance': 1961})\n",
    "ducks.update({'first_name': 'Magica'}, {'first_appearance': 2000})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note a couple of things:\n",
    "\n",
    "- the used method is deprecated;\n",
    "- it returns a dictionary (instead of an object) summarizing the effects of the operation\n",
    "\n",
    "Let's double-check:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "metadata": {},
   "outputs": [],
   "source": [
    "ducks.find_one({'first_name': 'Magica'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'_id': ObjectId('5e6a58c3917c9dd74a98fc9b'), 'first_appearance': 2000}"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.find_one({'first_appearance': 2000})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>This (deprecated) form of update completely replaced the document!</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Solution</div>\n",
    "\n",
    "Use the `update_one` and `update_many` methods"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "update only works with $ operators",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-122-6a0ad72fdd9c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      4\u001b[0m                   'first_appearance': 1961})\n\u001b[1;32m      5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mducks\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate_one\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m'first_name'\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'Magica'\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m'first_appearance'\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m2000\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/opt/conda/lib/python3.7/site-packages/pymongo/collection.py\u001b[0m in \u001b[0;36mupdate_one\u001b[0;34m(self, filter, update, upsert, bypass_document_validation, collation, array_filters, session)\u001b[0m\n\u001b[1;32m    991\u001b[0m         \"\"\"\n\u001b[1;32m    992\u001b[0m         \u001b[0mcommon\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_is_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"filter\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilter\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 993\u001b[0;31m         \u001b[0mcommon\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_ok_for_update\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    994\u001b[0m         \u001b[0mcommon\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_list_or_none\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'array_filters'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0marray_filters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    995\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/opt/conda/lib/python3.7/site-packages/pymongo/common.py\u001b[0m in \u001b[0;36mvalidate_ok_for_update\u001b[0;34m(update)\u001b[0m\n\u001b[1;32m    533\u001b[0m     \u001b[0mfirst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miter\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    534\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mis_document\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mfirst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstartswith\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'$'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 535\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'update only works with $ operators'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    536\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    537\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mValueError\u001b[0m: update only works with $ operators"
     ]
    }
   ],
   "source": [
    "# OK, let's revert things\n",
    "ducks.find_one_and_delete({'first_appearance': 2000})\n",
    "ducks.insert_one({'first_name': 'Magica', 'last_name': 'De Spell',\n",
    "                  'first_appearance': 1961})\n",
    "\n",
    "ducks.update_one({'first_name': 'Magica'}, {'first_appearance': 2000})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">WTF(irst_appearance)?</div>\n",
    "\n",
    "- This error is due to the fact that these methods don't allow to implicitly replace a whole document\n",
    "- Instead, the use of `$set`, which actually updates one or more fields leaving the rest of the document untouched, is enforced"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.UpdateResult at 0x7fb208d24b40>"
      ]
     },
     "execution_count": 123,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = ducks.update_one({'first_name': 'Magica'},\n",
    "                     {'$set': {'first_appearance': 2000}})\n",
    "r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The operation returns an object gathering information about the result:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "# n. of matched documents\n",
    "print(r.matched_count)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 125,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# n. of modified documents\n",
    "r.modified_count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [],
   "source": [
    "# None if no upsertion, id of created document otherwise\n",
    "# this deals with upsertions, will be explained later on\n",
    "r.upserted_id"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'n': 1, 'nModified': 1, 'ok': 1.0, 'updatedExisting': True}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# results in the update-style\n",
    "r.raw_result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can check that the method behaves as expected"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'_id': ObjectId('5e6a5992917c9dd74a98fc9c'),\n",
       " 'first_name': 'Magica',\n",
       " 'last_name': 'De Spell',\n",
       " 'first_appearance': 2000}"
      ]
     },
     "execution_count": 127,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.find_one({'first_name': 'Magica'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`update_one` only updates one of the identified documents, `update_many` updates all of them."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Removing fields</div>\n",
    "\n",
    "There is also the possibility of <em>removing</em> fields in a document, using the `$unset` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.UpdateResult at 0x7fb208d48690>"
      ]
     },
     "execution_count": 128,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.update_one({'first_name': 'Magica'},\n",
    "                 {'$unset': {'first_appearance': ''}})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'_id': ObjectId('5e6a5992917c9dd74a98fc9c'),\n",
       " 'first_name': 'Magica',\n",
       " 'last_name': 'De Spell'}"
      ]
     },
     "execution_count": 129,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.find_one({'first_name': 'Magica'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Advanced update actions</div>\n",
    "\n",
    "- `$inc` and `$dec` respectively increment and decrement a numeric quantity\n",
    "- `$push` adds elements to arrays\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 130,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'_id': ObjectId('5e6a46b4917c9dd74a98fc96'),\n",
       " 'first_name': 'Gyro',\n",
       " 'last_name': 'Gearloose',\n",
       " 'gender': 'M',\n",
       " 'first_appearance': 1952,\n",
       " 'hobbies': ['invention', 'study', 'lamps']}"
      ]
     },
     "execution_count": 130,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.update_one({'first_name': 'Gyro'}, {'$push': {'hobbies': 'lamps'}})\n",
    "ducks.find_one({'first_name': 'Gyro'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `$pop` extracts the last or first element in an array\n",
    "- `$pull` removes all instances of an element from an array"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">The <code>$</code> operator</div>\n",
    "\n",
    "When dealing with arrays, `$` is a special operator denoting the index of the first occurrence of a value matched in an update query"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 131,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['invention', 'study', 'robotic lamps']"
      ]
     },
     "execution_count": 131,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.update_one({'hobbies': 'lamps'},\n",
    "                 {'$set': {'hobbies.$': 'robotic lamps'}})\n",
    "ducks.find_one({'first_name': 'Gyro'})['hobbies']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Advanced uses of <code>$</code></div>\n",
    "\n",
    "Note that `$` can be used also in order to dig down to hierarchies. Suppose a document contains the following value for `hobbies`:\n",
    "\n",
    "<pre>[{'name': 'invention', 'day': 'Monday'},\n",
    " {'name': 'study', 'day': 'Friday'}]</pre>\n",
    "\n",
    "<p>The update query modifies the day for the <em>study</em> hobby:</p>\n",
    "\n",
    "<pre>{{'hobbies.name': 'study'},\n",
    " {'$set': {'hobbies.$.day': 'Tuesday'}}}</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Upserts</div>\n",
    "\n",
    "<p>What happens if one attempts to update a non-existing document?</p>\n",
    "\n",
    "- nothing...\n",
    "- ...unless the `upsert=True` additional argument has been specified in `update_one` or `update_many`: in this case the operation results in an insert-upon-update (upsert)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Removing...</div>\n",
    "\n",
    "<ul>\n",
    "  <li>at most one document identified by a query</li> \n",
    "</ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.DeleteResult at 0x7fb209d97820>"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.delete_one({'first_name': 'Ludwig'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<ul><li>all documents identified by a query</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.DeleteResult at 0x7fb209d97c30>"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.delete_many({'first_name': 'Ludwig'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<ul><li>all documents in a collection</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<pymongo.results.DeleteResult at 0x7fb209d97f50>"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ducks.delete_many({})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<ul><li>a collection</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "ducks.drop()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<ul><li>a database</li></ul>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "client.drop_database('duckburg')\n",
    "\n",
    "# or\n",
    "#duckburg_db.command('dropDatabase')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Joins and scalability</div>\n",
    "\n",
    "<ul>\n",
    "  <li>Scalability in MongoDB is achieved through <strong>sharding</strong>, that is distributing and replicating data across several machines</li>\n",
    "  <li>This however doesn't allow to perform joins automatically</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Handcrafted joins I</div>\n",
    "\n",
    "`ObjectId`s can be used to manually recreate one-to-many joins..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "donald_id = ducks.find_one({'first_name': 'Donald'})['_id']\n",
    "for d in ['Huey', 'Dewey', 'Louie']:\n",
    "    ducks.insert_one({'first_name': d, 'uncle': donald_id})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<p>...at the price of an additional query (to recover, in this case, the uncle).</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Handcrafted joins II</div>\n",
    "\n",
    "<p>Many-to-many joins can be implemented</p>\n",
    "\n",
    "<ul><li>using arrays</li></ul>\n",
    "\n",
    "<pre>\n",
    "ducks.insert_one({\n",
    "    'first_name': 'Donald',\n",
    "    'last_name': 'Duck',\n",
    "    'relatives': [ObjectId('516405b8b356cd6125b74e89'),\n",
    "                  ObjectId('516405b8b356cd6125b74e8a'),\n",
    "                  ObjectId('516405b8b356cd6125b74e8b')]})\n",
    "ducks.find({\n",
    "    'relatives': ObjectId('516405b8b356cd6125b74e8a')})\n",
    "</pre>\n",
    "\n",
    "<ul><li>or embedded documents</li></ul>\n",
    "\n",
    "<pre>\n",
    "ducks.insert_one({\n",
    "    'first_name': 'Donald',\n",
    "    'last_name': 'Duck',\n",
    "    'relatives': [{'first_name': 'Huey'},\n",
    "                  {'first_name': 'Dewey'},\n",
    "                  {'first_name': 'Louie'}]})\n",
    "ducks.find_one({'relatives.first_name': 'Dewey'})\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Denormalization</div>\n",
    "\n",
    "<ul>\n",
    "  <li>a possible solution (duplication not to be necessarily demonized)</li>\n",
    "  <li>critical limit: the maximal size of each document is 16 MBytes</li>\n",
    "  <li>(aside note: the whole text of Shakespeare's Hamlet requires 200 Kbytes)</li>\n",
    "  <li>the actual price to be paid for this is in having a higher number of queries (for instance, when duplicate data are updated)</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MapReduce</div>\n",
    "\n",
    "<pre>\n",
    "resource    date\n",
    "index       Jan 20 2010 4:30\n",
    "index       Jan 20 2010 5:30\n",
    "about       Jan 20 2010 6:00\n",
    "index       Jan 20 2010 7:00\n",
    "about       Jan 21 2010 8:00\n",
    "about       Jan 21 2010 8:30\n",
    "index       Jan 21 2010 8:30\n",
    "about       Jan 21 2010 9:00\n",
    "index       Jan 21 2010 9:30\n",
    "index       Jan 22 2010 5:00\n",
    "</pre>\n",
    "\n",
    "Starting from these data...\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MapReduce</div>\n",
    "\n",
    "...we want to obain\n",
    "\n",
    "<pre>\n",
    "resource    year    month   day     count\n",
    "index       2010    1       20      3\n",
    "about       2010    1       20      1\n",
    "about       2010    1       21      3\n",
    "index       2010    1       21      2\n",
    "index       2010    1       22      1\n",
    "</pre>\n",
    "\n",
    "We can use a MapReduce job in which\n",
    "\n",
    "- map emits 1 for each (resource, date) key\n",
    "- reduce sums up values"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MapReduce</div>\n",
    "\n",
    "We populate the database at first"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "import datetime\n",
    "\n",
    "ducks.hits.insert_one({'resource': 'index',\n",
    "                       'date': datetime.datetime(2010, 1, 20, 4, 30, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'index',\n",
    "                       'date': datetime.datetime(2010, 1, 20, 5, 30, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'about',\n",
    "                       'date': datetime.datetime(2010, 1, 20, 6, 0, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'index',\n",
    "                       'date': datetime.datetime(2010, 1, 20, 7, 0, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'about',\n",
    "                       'date': datetime.datetime(2010, 1, 21, 8, 0, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'index',\n",
    "                       'date': datetime.datetime(2010, 1, 21, 8, 30, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'about',\n",
    "                       'date': datetime.datetime(2010, 1, 21, 9, 0, 0, 0)})\n",
    "ducks.hits.insert_one({'resource': 'index',\n",
    "                       'date': datetime.datetime(2010, 1, 21, 9, 30, 0, 0)})\n",
    "_ = ducks.hits.insert_one({'resource': 'index',\n",
    "                           'date': datetime.datetime(2010, 1, 22, 5, 0, 0, 0)})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MapReduce</div>\n",
    "\n",
    "We subsequently write the map function (in javascript!)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [],
   "source": [
    "from bson.code import Code\n",
    "\n",
    "map = Code('''function() {\n",
    "  var key = {\n",
    "    resource: this.resource,\n",
    "    year: this.date.getFullYear(),\n",
    "    month: this.date.getMonth(),\n",
    "    day: this.date.getDate()\n",
    "  };\n",
    "  emit(key, {count: 1});\n",
    "}''')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">MapReduce</div>\n",
    "\n",
    "And we create analogously the reduce function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "reduce = Code('''function(key, values) {\n",
    "  var sum = 0;\n",
    "  values.forEach(function(value) {\n",
    "     sum += value['count'];\n",
    "  });\n",
    "  return {count: sum};\n",
    "}''')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Map-reduce</div>\n",
    "\n",
    "_et voilà!_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Collection(Database(MongoClient(host=['mongodb:27017'], document_class=dict, tz_aware=False, connect=True), 'duckburg'), 'hit_stats')"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = ducks.hits.map_reduce(map, reduce, 'hit_stats')\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'_id': {'resource': 'about', 'year': 2010.0, 'month': 0.0, 'day': 20.0}, 'value': {'count': 1.0}}\n",
      "{'_id': {'resource': 'about', 'year': 2010.0, 'month': 0.0, 'day': 21.0}, 'value': {'count': 2.0}}\n",
      "{'_id': {'resource': 'index', 'year': 2010.0, 'month': 0.0, 'day': 20.0}, 'value': {'count': 3.0}}\n",
      "{'_id': {'resource': 'index', 'year': 2010.0, 'month': 0.0, 'day': 21.0}, 'value': {'count': 2.0}}\n",
      "{'_id': {'resource': 'index', 'year': 2010.0, 'month': 0.0, 'day': 22.0}, 'value': {'count': 1.0}}\n"
     ]
    }
   ],
   "source": [
    "for doc in result.find():\n",
    "    print(doc)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Other aggregation forms</div>\n",
    "\n",
    "MongoDB also provides weaker forms of aggregation which do not require the use of map-reduce algorithms.\n",
    "\n",
    "This is done using the `aggregate` method in conjunction with operators such as `$match`, `$project`, `$group`, `$sort`, etc."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Final note</div>\n",
    "\n",
    "<p>There's no universal solution: the choice depends on the problem under study, as well as on personal design style</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">There's much more</div>\n",
    "\n",
    "<ul>\n",
    "  <li>Sharding</li>\n",
    "  <li>Aggregation framework</li>\n",
    "  <li>...</li>\n",
    "</ul>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Your turn!</div>\n",
    "\n",
    "- Run the provided docker-compose instances\n",
    "- Connect to `http://localhost:8888`\n",
    "- The following cells contain some challenges"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"slide-title\">Import the dataset</div>\n",
    "\n",
    "We will work with a book catalog already encoded in JSON format and available at the URL https://github.com/ozlerhakan/mongodb-json-files/blob/master/datasets/catalog.books.json\n",
    "\n",
    "You don't need to download this dataset, as it is already in the Docker image we are using (in the `/data/db` directory). You need however to import it into MongoDB: ssh into the mongodb container and execute:\n",
    "\n",
    "<pre>\n",
    "mongoimport --db books --collection books --file catalog.books.json\n",
    "</pre>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"slide-title\">Inspect data</div>\n",
    "\n",
    "Although we saw that a tabular description might not be the best resource to be used when visualizing noSQL data, we can build and show a Pandas dataframe as a quick way to peek into the dataset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>_id</th>\n",
       "      <th>authors</th>\n",
       "      <th>categories</th>\n",
       "      <th>isbn</th>\n",
       "      <th>longDescription</th>\n",
       "      <th>pageCount</th>\n",
       "      <th>publishedDate</th>\n",
       "      <th>shortDescription</th>\n",
       "      <th>status</th>\n",
       "      <th>thumbnailUrl</th>\n",
       "      <th>title</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>3</td>\n",
       "      <td>[Gojko Adzic]</td>\n",
       "      <td>[Software Engineering]</td>\n",
       "      <td>1617290084</td>\n",
       "      <td>NaN</td>\n",
       "      <td>0</td>\n",
       "      <td>2011-06-03</td>\n",
       "      <td>NaN</td>\n",
       "      <td>PUBLISH</td>\n",
       "      <td>https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....</td>\n",
       "      <td>Specification by Example</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1</td>\n",
       "      <td>[W. Frank Ableson, Charlie Collins, Robi Sen]</td>\n",
       "      <td>[Open Source, Mobile]</td>\n",
       "      <td>1933988673</td>\n",
       "      <td>Android is an open source mobile phone platfor...</td>\n",
       "      <td>416</td>\n",
       "      <td>2009-04-01</td>\n",
       "      <td>Unlocking Android: A Developer's Guide provide...</td>\n",
       "      <td>PUBLISH</td>\n",
       "      <td>https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....</td>\n",
       "      <td>Unlocking Android</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2</td>\n",
       "      <td>[W. Frank Ableson, Robi Sen]</td>\n",
       "      <td>[Java]</td>\n",
       "      <td>1935182722</td>\n",
       "      <td>When it comes to mobile apps, Android can do a...</td>\n",
       "      <td>592</td>\n",
       "      <td>2011-01-14</td>\n",
       "      <td>Android in Action, Second Edition is a compreh...</td>\n",
       "      <td>PUBLISH</td>\n",
       "      <td>https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....</td>\n",
       "      <td>Android in Action, Second Edition</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>6</td>\n",
       "      <td>[Satnam Alag]</td>\n",
       "      <td>[Internet]</td>\n",
       "      <td>1933988312</td>\n",
       "      <td>There's a great deal of wisdom in a crowd, but...</td>\n",
       "      <td>425</td>\n",
       "      <td>2008-10-01</td>\n",
       "      <td>NaN</td>\n",
       "      <td>PUBLISH</td>\n",
       "      <td>https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....</td>\n",
       "      <td>Collective Intelligence in Action</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>4</td>\n",
       "      <td>[Tariq Ahmed with Jon Hirschi, Faisal Abid]</td>\n",
       "      <td>[Internet]</td>\n",
       "      <td>1933988746</td>\n",
       "      <td>New web applications require engaging user-fri...</td>\n",
       "      <td>576</td>\n",
       "      <td>2009-02-02</td>\n",
       "      <td>NaN</td>\n",
       "      <td>PUBLISH</td>\n",
       "      <td>https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....</td>\n",
       "      <td>Flex 3 in Action</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   _id                                        authors              categories  \\\n",
       "0    3                                  [Gojko Adzic]  [Software Engineering]   \n",
       "1    1  [W. Frank Ableson, Charlie Collins, Robi Sen]   [Open Source, Mobile]   \n",
       "2    2                   [W. Frank Ableson, Robi Sen]                  [Java]   \n",
       "3    6                                  [Satnam Alag]              [Internet]   \n",
       "4    4    [Tariq Ahmed with Jon Hirschi, Faisal Abid]              [Internet]   \n",
       "\n",
       "         isbn                                    longDescription  pageCount  \\\n",
       "0  1617290084                                                NaN          0   \n",
       "1  1933988673  Android is an open source mobile phone platfor...        416   \n",
       "2  1935182722  When it comes to mobile apps, Android can do a...        592   \n",
       "3  1933988312  There's a great deal of wisdom in a crowd, but...        425   \n",
       "4  1933988746  New web applications require engaging user-fri...        576   \n",
       "\n",
       "  publishedDate                                   shortDescription   status  \\\n",
       "0    2011-06-03                                                NaN  PUBLISH   \n",
       "1    2009-04-01  Unlocking Android: A Developer's Guide provide...  PUBLISH   \n",
       "2    2011-01-14  Android in Action, Second Edition is a compreh...  PUBLISH   \n",
       "3    2008-10-01                                                NaN  PUBLISH   \n",
       "4    2009-02-02                                                NaN  PUBLISH   \n",
       "\n",
       "                                        thumbnailUrl  \\\n",
       "0  https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....   \n",
       "1  https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....   \n",
       "2  https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....   \n",
       "3  https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....   \n",
       "4  https://s3.amazonaws.com/AKIAJC5RLADLUMVRPFDQ....   \n",
       "\n",
       "                               title  \n",
       "0           Specification by Example  \n",
       "1                  Unlocking Android  \n",
       "2  Android in Action, Second Edition  \n",
       "3  Collective Intelligence in Action  \n",
       "4                   Flex 3 in Action  "
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db = client.books\n",
    "books = db.books\n",
    "\n",
    "import pandas as pd\n",
    "pd.DataFrame(list(books.find().limit(5)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"slide-title\">Challenges</div>\n",
    "\n",
    "1. How many books are there?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "2. Which is the highest number of co-authors in a book?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "3. How many distinct authors are there?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "4. Which author has the highest number of coauthors?\n",
    "\n",
    "Note: the empty string does not count as an author."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "5. How many coauthors has this author and who are these coauthors?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "6. Deduplicate author\n",
    "\n",
    "It seems our data are not that clean: one author is actually the empty string, Kimberly Tripp occurs twice (one time with the middle name, another one without it), and in two cases the author's name is actually the name for a group of authors. Let's clean up these things. First of all we can deduplicate records referring to Kimberly Tripp: write an update query modifying all occurrences of `'Kimberly L. Tripp'` into `'Kimberly Tripp'`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "7. Remove grouped authors\n",
    "\n",
    "Write an update query removing all occurrences of `'Contributions from 53 SQL Server MVPs'` and `Contributions from 53 SQL Server MVPs; Edited by Paul Nielsen` as author names, replacing them with the empty string."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "8. Write (but don't execute!) a query removing books with null authors\n",
    "\n",
    "Write a query which would remove all documents having the empty string as author. <strong>Don't execute the query</strong>, as it will remove interesting information we will need later on."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "9. Which class is used in order to store time stamps?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "10. How many books were published in 2009?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "11. Which are the first and the last publication years in temporal order?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "12. Display a bar plot showing the number of all published books between 1992 and 2015"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "13.  Build a function `get_coauthors_for_years`\n",
    "\n",
    "This function should take two years as arguments and return a dictionary associating each author to the set of his coauthors within the corresponding time span (if any of the arguments is `None` the search should be done on all books)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "14. Use the function `get_coauthors_for_years` in order to draw the collaboration graph of all books published between 1997 and 1999\n",
    "\n",
    "The collaboration graph here is intended as a graph having authors as nodes and connecting two nodes if the corresponding authors have coauthored at least one book.\n",
    "\n",
    "<strong>Hint</strong>: use the `nextworkx.Graph` class, specifically its `add_node` and `add_edge` methods, as well as the `networkx.draw` method in order to show a graph."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "15. Build and save the collaboration matrix of all authors\n",
    "\n",
    "Consider all books regardless of publication year, and build the authors' collaboration matrix, that is a square matrix whose dimension equals the number of authors. Entry $(r, c)$ in this matrix (where $r$ and $c$ refer respectively to rows and columns) should be 0 if $r$-th and $c$-th author never coauthored a book, otherwise it should be set to $\\frac{1}{n_c}$, where $n_c$ is the total number of coauthors of $c$-th author.\n",
    "\n",
    "We'll be interested actually only in non-null entries: save them as one row of a CSV file using the format $(r, c, m_{rc})$, being $m_{rc}$ the non-null element in row $r$ and column $c$. Use the name `author-connections.csv` for this file.\n",
    "\n",
    "<strong>Hint</strong>: if you get confused about what to place in rows and columns, take in mind that the resulting matrix should be <strong>columns-wise stochastic</strong>, that is the sum of all elements in any columns has to be equal to 1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**: postpone the remaining challenges until you have studied the PageRank algorithm and its variants"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "16. Compute the pagerank index for all authors using the matrix obtained in point 15"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "17. Build and save the collaboration matrix of all books\n",
    "\n",
    "The collaboration matrix of books is obtained in the same way as those of authors, where now two books are connected (and thus identify a non-null entry in the matrix) if they share at least one author. Also in this case, the obtained matrix should be column-wise stochastic."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "18. Build and save additional information\n",
    "\n",
    "Write in a CSV file the association of each book to the corresponding indices of rows and columns in the previously produced book connection matrix: each row of the file will have the format $(i,t)$, where $i$ is the index and $t$ is the title. Name this file `'book-index.csv'`\n",
    "\n",
    "Write a file `'book-categories.csv'` in which each line has the format $(i, j)$, where $i$ is an index of the book (in the previously produced matrix and vector) and $j$ is 1 if the corresponding book has the <em>Java</em> category in the dataset, and 0 otherwise."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "19. Compute the topic-sensitive pagerank for all books using the matrix created in point 17, as well as the information about the presence of the _Java_ category in order to drive the pagerank process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": true,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": true,
   "user_envs_cfg": false
  },
  "livereveal": {
   "center": "false",
   "theme": "white",
   "transition": "none"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": false,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
