├── .gitignore ├── README.md ├── common └── pdf-template.tex ├── en ├── mongodb.markdown ├── title.png ├── title.psd └── title.txt └── ja ├── Makefile ├── hamacro.sty ├── mongodb.markdown ├── stylesheet.css ├── template.html ├── template.tex ├── title.eps ├── title.svg └── title.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Output books 2 | 3 | *.pdf 4 | *.epub 5 | *.mobi 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MongoDBの薄い本 ## 2 | 「MongoDBの薄い本」はフリーのMongoDBの入門本「The Little MongoDB Book」の翻訳版です 3 | 4 | この本は[MongoDB interactive tutorial](http://mongly.com)が書かれたすぐ後に書かれました。なのでこの2つは補足的になっています。 5 | 6 | この本の原書は [Karl Seguin](http://openmymind.net)によって書かれ、[Perry Neal](http://twitter.com/perryneal)が手伝いました。 7 | 8 | 翻訳版は [濱野](http://twitter.com/hamano)が翻訳し、[田村](http://twitter.com/tamura__246)が手伝いました。 9 | 10 | この本が気に入ったのなら、きっと [The Little Redis Book](http://openmymind.net/2012/1/23/The-Little-Redis-Book/)も気に入るでしょう。 11 | 12 | ## その他の翻訳 ## 13 | 14 | * [英語(原書)](https://github.com/karlseguin/the-little-mongodb-book) 15 | * [ロシア語](https://github.com/jsmarkus/the-little-mongodb-book/tree/master/ru) 16 | * [中国語](https://github.com/justinyhuang/the-little-mongodb-book-cn) 17 | * [イタリア語](https://github.com/nicolaiarocci/the-little-mongodb-book/tree/master/it) 18 | * [スペイン語](https://github.com/uokesita/the-little-mongodb-book/tree/master/es) 19 | * [ポルトガル語](https://github.com/rafaelgou/the-little-mongodb-book/tree/master/pt_BR) 20 | * [日本語](http://www.cuspy.org/diary/2012-04-17) 21 | 22 | ## ライセンス ## 23 | この本は無料であり、[Attribution-NonCommercial 3.0 Unportedライセンス]()に基づいて配布されています。 24 | 25 | ## フォーマット ## 26 | この本は[Markdown](http://daringfireball.net/projects/markdown/) 記法で書かれており、[Pandoc](http://johnmacfarlane.net/pandoc/)を利用してPDFに変換されています。 27 | -------------------------------------------------------------------------------- /common/pdf-template.tex: -------------------------------------------------------------------------------- 1 | \documentclass{book} 2 | 3 | 4 | % Fonts and typography 5 | 6 | %% Typography 7 | \usepackage[no-math]{fontspec} 8 | \defaultfontfeatures{Mapping = tex-text, Scale = MatchLowercase} 9 | 10 | %% Fonts 11 | \setmainfont{Verdana} 12 | \setsansfont{Verdana} 13 | \setmonofont{Consolas} 14 | 15 | %% Set Sans font in headings 16 | \usepackage{sectsty} 17 | \allsectionsfont{\sffamily} 18 | 19 | %% Set polyglossia language 20 | \usepackage{polyglossia} 21 | \setdefaultlanguage{english} 22 | 23 | 24 | % Page 25 | 26 | %% Use full page in book style 27 | \usepackage{fullpage} 28 | 29 | %% Set line spacing 30 | \usepackage{setspace} 31 | \setstretch{1.2} 32 | 33 | %% Disable paragraph indentation 34 | \usepackage{parskip} 35 | 36 | %% Start sections from new page 37 | \let\stdsection\section 38 | \renewcommand\section{\newpage\stdsection} 39 | 40 | 41 | % Colors 42 | 43 | \usepackage{xcolor} 44 | 45 | %% Tango color scheme 46 | \definecolor{SkyBlue}{HTML}{3465A4} 47 | \definecolor{DarkSkyBlue}{HTML}{204A87} 48 | 49 | \definecolor{Plum}{HTML}{75507B} 50 | 51 | \definecolor{ScarletRed}{HTML}{CC0000} 52 | 53 | \definecolor{Aluminium1}{HTML}{EEEEEC} 54 | \definecolor{Aluminium6}{HTML}{2e3436} 55 | 56 | \definecolor{Black}{HTML}{000000} 57 | 58 | 59 | % Listings 60 | 61 | \usepackage{listings} 62 | 63 | \lstdefinelanguage{JavaScript}{ 64 | keywords = {typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break}, 65 | keywordstyle = \color{SkyBlue}\bfseries, 66 | ndkeywords = {class, export, boolean, throw, implements, import, this}, 67 | ndkeywordstyle = \color{Aluminium6}\bfseries, 68 | identifierstyle = \color{Black}, 69 | sensitive = false, 70 | comment = [l]{//}, 71 | morecomment = [s]{/*}{*/}, 72 | commentstyle = \color{Plum}\ttfamily, 73 | stringstyle = \color{ScarletRed}\ttfamily, 74 | morestring = [b]', 75 | morestring = [b]" 76 | } 77 | 78 | \lstset{ 79 | language = JavaScript, 80 | backgroundcolor = \color{Aluminium1}, 81 | extendedchars = true, 82 | basicstyle = \normalsize\ttfamily, 83 | showstringspaces = false, 84 | showspaces = false, 85 | tabsize = 1, 86 | breaklines = true, 87 | showtabs = false 88 | } 89 | 90 | 91 | % Links 92 | 93 | %% Hyperref 94 | \usepackage[colorlinks, breaklinks, bookmarks, xetex]{hyperref} 95 | 96 | \hypersetup { 97 | linkcolor = DarkSkyBlue, 98 | citecolor = DarkSkyBlue, 99 | filecolor = DarkSkyBlue, 100 | urlcolor = DarkSkyBlue 101 | } 102 | 103 | %% Don’t use Mono font for URLs 104 | \urlstyle{same} 105 | 106 | 107 | % Images 108 | 109 | \usepackage{graphicx} 110 | 111 | 112 | % Pandoc hacks 113 | 114 | %% Normal enumerates processing 115 | \usepackage{enumerate} 116 | 117 | %% Disable section numbers 118 | \setcounter{secnumdepth}{0} 119 | 120 | 121 | \begin{document} 122 | 123 | % Title page 124 | 125 | \thispagestyle{empty} 126 | 127 | \vspace*{\fill} 128 | \begin{center} 129 | \includegraphics[width=0.7\textwidth]{title} 130 | \end{center} 131 | \vspace*{\fill} 132 | 133 | \setcounter{page}{0} 134 | 135 | % Book contents 136 | 137 | $body$ 138 | 139 | \end{document} 140 | -------------------------------------------------------------------------------- /en/mongodb.markdown: -------------------------------------------------------------------------------- 1 | # About This Book # 2 | 3 | ## License ## 4 | The Little MongoDB Book book is licensed under the Attribution-NonCommercial 3.0 Unported license. **You should not have paid for this book.** 5 | 6 | You are basically free to copy, distribute, modify or display the book. However, I ask that you always attribute the book to me, Karl Seguin and do not use it for commercial purposes. 7 | 8 | You can see the full text of the license at: 9 | 10 | 11 | 12 | ## About The Author ## 13 | Karl Seguin is a developer with experience across various fields and technologies. He's an expert .NET and Ruby developer. He's a semi-active contributor to OSS projects, a technical writer and an occasional speaker. With respect to MongoDB, he was a core contributor to the C# MongoDB library NoRM, wrote the interactive tutorial [mongly](http://mongly.com) as well as the [Mongo Web Admin](https://github.com/karlseguin/Mongo-Web-Admin). His free service for casual game developers, [mogade.com](http://mogade.com/), is powered by MongoDB. 14 | 15 | Karl has since written [The Little Redis Book](http://openmymind.net/2012/1/23/The-Little-Redis-Book/) 16 | 17 | His blog can be found at , and he tweets via [@karlseguin](http://twitter.com/karlseguin) 18 | 19 | ## With Thanks To ## 20 | A special thanks to [Perry Neal](http://twitter.com/perryneal) for lending me his eyes, mind and passion. You provided me with invaluable help. Thank you. 21 | 22 | ## Latest Version ## 23 | The latest source of this book is available at: 24 | 25 | . 26 | 27 | # Introduction # 28 | > It's not my fault the chapters are short, MongoDB is just easy to learn. 29 | 30 | It is often said that technology moves at a blazing pace. It's true that there is an ever growing list of new technologies and techniques being released. However, I've long been of the opinion that the fundamental technologies used by programmers move at a rather slow pace. One could spend years learning little yet remain relevant. What is striking though is the speed at which established technologies get replaced. Seemingly overnight, long-established technologies find themselves threatened by shifts in developer focus. 31 | 32 | Nothing could be more representative of this sudden shift than the progress of NoSQL technologies against well-established relational databases. It almost seems like one day the web was being driven by a few RDBMSes, and the next, five or so NoSQL solutions had established themselves as worthy solutions. 33 | 34 | Even though these transitions seem to happen overnight, the reality is that they can take years to become accepted practice. The initial enthusiasm is driven by a relatively small set of developers and companies. Solutions are refined, lessons learned and seeing that a new technology is here to stay, others slowly try it for themselves. Again, this is particularly true in the case of NoSQL where many solutions aren't replacements for more traditional storage solutions, but rather address a specific need in addition to what one might get from traditional offerings. 35 | 36 | Having said all of that, the first thing we ought to do is explain what is meant by NoSQL. It's a broad term that means different things to different people. Personally, I use it very broadly to mean a system that plays a part in the storage of data. Put another way, NoSQL (again, for me), is the belief that your persistence layer isn't necessarily the responsibility of a single system. Where relational database vendors have historically tried to position their software as a one-size-fits-all solution, NoSQL leans towards smaller units of responsibility where the best tool for a given job can be leveraged. So, your NoSQL stack might still leverage a relational database, say MySQL, but it'll also contain Redis as a persistence lookup for specific parts of the system as well as Hadoop for your intensive data processing. Put simply, NoSQL is about being open and aware of alternative, existing and additional patterns and tools for managing your data. 37 | 38 | You might be wondering where MongoDB fits into all of this. As a document-oriented database, Mongo is a more generalized NoSQL solution. It should be viewed as an alternative to relational databases. Like relational databases, it too can benefit from being paired with some of the more specialized NoSQL solutions. MongoDB has advantages and drawbacks, which we'll cover in later parts of this book. 39 | 40 | As you may have noticed, we use the terms MongoDB and Mongo interchangeably. 41 | 42 | # Getting Started # 43 | Most of this book will focus on core MongoDB functionality. We'll therefore rely on the MongoDB shell. While the shell is useful to learn as well as being a useful administrative tool, your code will use a MongoDB driver. 44 | 45 | This does bring up the first thing you should know about MongoDB: its drivers. MongoDB has a [number of official drivers](http://www.mongodb.org/display/DOCS/Drivers) for various languages. These drivers can be thought of as the various database drivers you are probably already familiar with. On top of these drivers, the development community has built more language/framework-specific libraries. For example, [NoRM](https://github.com/atheken/NoRM) is a C# library which implements LINQ, and [MongoMapper](https://github.com/jnunemaker/mongomapper) is a Ruby library which is ActiveRecord-friendly. Whether you choose to program directly against the core MongoDB drivers or some higher-level library is up to you. I point this out only because many people new to MongoDB are confused as to why there are both official drivers and community libraries - the former generally focuses on core communication/connectivity with MongoDB and the latter with more language and framework-specific implementations. 46 | 47 | As you read through this, I encourage you to play with MongoDB to replicate what I demonstrate as well as to explore questions that you might come up with on your own. It's easy to get up and running with MongoDB, so let's take a few minutes now to set things up. 48 | 49 | 1. Head over to the [official download page](http://www.mongodb.org/downloads) and grab the binaries from the first row (the recommended stable version) for your operating system of choice. For development purposes, you can pick either 32-bit or 64-bit. 50 | 51 | 2. Extract the archive (wherever you want) and navigate to the `bin` subfolder. Don't execute anything just yet, but know that `mongod` is the server process and `mongo` is the client shell - these are the two executables we'll be spending most of our time with. 52 | 53 | 3. Create a new text file in the `bin` subfolder named `mongodb.config` 54 | 55 | 4. Add a single line to your mongodb.config: `dbpath=PATH_TO_WHERE_YOU_WANT_TO_STORE_YOUR_DATABASE_FILES`. For example, on Windows you might do `dbpath=c:\mongodb\data` and on Linux you might do `dbpath=/var/lib/mongodb/data`. 56 | 57 | 5. Make sure the `dbpath` you specified exists 58 | 59 | 6. Launch mongod with the `--config /path/to/your/mongodb.config` parameter. 60 | 61 | As an example for Windows users, if you extracted the downloaded file to `c:\mongodb\` and you created `c:\mongodb\data\` then within `c:\mongodb\bin\mongodb.config` you would specify `dbpath=c:\mongodb\data\`. You could then launch `mongod` from a command prompt via `c:\mongodb\bin\mongod --config c:\mongodb\bin\mongodb.config`. 62 | 63 | Feel free to add the `bin` folder to your path to make all of this less verbose. MacOSX and Linux users can follow almost identical directions. The only thing you should have to change are the paths. 64 | 65 | Hopefully you now have MongoDB up and running. If you get an error, read the output carefully - the server is quite good at explaining what's wrong. 66 | 67 | You can now launch `mongo` (without the *d*) which will connect a shell to your running server. Try entering `db.version()` to make sure everything's working as it should. Hopefully you'll see the version number you installed. 68 | 69 | # Chapter 1 - The Basics # 70 | We begin our journey by getting to know the basic mechanics of working with MongoDB. Obviously this is core to understanding MongoDB, but it should also help us answer higher-level questions about where MongoDB fits. 71 | 72 | To get started, there are six simple concepts we need to understand. 73 | 74 | 1. MongoDB has the same concept of a `database` with which you are likely already familiar (or a schema for you Oracle folks). Within a MongoDB instance you can have zero or more databases, each acting as high-level containers for everything else. 75 | 76 | 2. A database can have zero or more `collections`. A collection shares enough in common with a traditional `table` that you can safely think of the two as the same thing. 77 | 78 | 3. Collections are made up of zero or more `documents`. Again, a document can safely be thought of as a `row`. 79 | 80 | 4. A document is made up of one or more `fields`, which you can probably guess are a lot like `columns`. 81 | 82 | 5. `Indexes` in MongoDB function much like their RDBMS counterparts. 83 | 84 | 6. `Cursors` are different than the other five concepts but they are important enough, and often overlooked, that I think they are worthy of their own discussion. The important thing to understand about cursors is that when you ask MongoDB for data, it returns a cursor, which we can do things to, such as counting or skipping ahead, without actually pulling down data. 85 | 86 | To recap, MongoDB is made up of `databases` which contain `collections`. A `collection` is made up of `documents`. Each `document` is made up of `fields`. `Collections` can be `indexed`, which improves lookup and sorting performance. Finally, when we get data from MongoDB we do so through a `cursor` whose actual execution is delayed until necessary. 87 | 88 | Why use new terminology (collection vs. table, document vs. row and field vs. column)? Is it just to make things more complicated? The truth is that while these concepts are similar to their relational database counterparts, they are not identical. The core difference comes from the fact that relational databases define `columns` at the `table` level whereas a document-oriented database defines its `fields` at the `document` level. That is to say that each `document` within a `collection` can have its own unique set of `fields`. As such, a `collection` is a dumbed down container in comparison to a `table`, while a `document` has a lot more information than a `row`. 89 | 90 | Although this is important to understand, don't worry if things aren't yet clear. It won't take more than a couple of inserts to see what this truly means. Ultimately, the point is that a collection isn't strict about what goes in it (it's schema-less). Fields are tracked with each individual document. The benefits and drawbacks of this will be explored in a future chapter. 91 | 92 | Let's get hands-on. If you don't have it running already, go ahead and start the `mongod` server as well as a mongo shell. The shell runs JavaScript. There are some global commands you can execute, like `help` or `exit`. Commands that you execute against the current database are executed against the `db` object, such as `db.help()` or `db.stats()`. Commands that you execute against a specific collection, which is what we'll be doing a lot of, are executed against the `db.COLLECTION_NAME` object, such as `db.unicorns.help()` or `db.unicorns.count()`. 93 | 94 | Go ahead and enter `db.help()`, you'll get a list of commands that you can execute against the `db` object. 95 | 96 | A small side note: Because this is a JavaScript shell, if you execute a method and omit the parentheses `()`, you'll see the method body rather than executing the method. I only mention it because the first time you do it and get a response that starts with `function (...){` you won't be surprised. For example, if you enter `db.help` (without the parentheses), you'll see the internal implementation of the `help` method. 97 | 98 | First we'll use the global `use` method to switch databases, so go ahead and enter `use learn`. It doesn't matter that the database doesn't really exist yet. The first collection that we create will also create the actual `learn` database. Now that you are inside a database, you can start issuing database commands, like `db.getCollectionNames()`. If you do so, you should get an empty array (`[ ]`). Since collections are schema-less, we don't explicitly need to create them. We can simply insert a document into a new collection. To do so, use the `insert` command, supplying it with the document to insert: 99 | 100 | db.unicorns.insert({name: 'Aurora', 101 | gender: 'f', weight: 450}) 102 | 103 | The above line is executing `insert` against the `unicorns` collection, passing it a single argument. Internally MongoDB uses a binary serialized JSON format. Externally, this means that we use JSON a lot, as is the case with our parameters. If we execute `db.getCollectionNames()` now, we'll actually see two collections: `unicorns` and `system.indexes`. The collection `system.indexes` is created once per database and contains the information on our database's index. 104 | 105 | You can now use the `find` command against `unicorns` to return a list of documents: 106 | 107 | db.unicorns.find() 108 | 109 | Notice that, in addition to the data you specified, there's an `_id` field. Every document must have a unique `_id` field. You can either generate one yourself or let MongoDB generate an `ObjectId` for you. Most of the time you'll probably want to let MongoDB generate it for you. By default, the `_id` field is indexed - which explains why the `system.indexes` collection was created. You can look at `system.indexes`: 110 | 111 | db.system.indexes.find() 112 | 113 | What you're seeing is the name of the index, the database and collection it was created against and the fields included in the index. 114 | 115 | Now, back to our discussion about schema-less collections. Insert a totally different document into `unicorns`, such as: 116 | 117 | db.unicorns.insert({name: 'Leto', 118 | gender: 'm', 119 | home: 'Arrakeen', 120 | worm: false}) 121 | 122 | And, again use `find` to list the documents. Once we know a bit more, we'll discuss this interesting behavior of MongoDB, but hopefully you are starting to understand why the more traditional terminology wasn't a good fit. 123 | 124 | ## Mastering Selectors ## 125 | In addition to the six concepts we've explored, there's one practical aspect of MongoDB you need to have a good grasp of before moving to more advanced topics: query selectors. A MongoDB query selector is like the `where` clause of an SQL statement. As such, you use it when finding, counting, updating and removing documents from collections. A selector is a JSON object, the simplest of which is `{}` which matches all documents (`null` works too). If we wanted to find all female unicorns, we could use `{gender:'f'}`. 126 | 127 | Before delving too deeply into selectors, let's set up some data to play with. First, remove what we've put so far in the `unicorns` collection via: `db.unicorns.remove()` (since we aren't supplying a selector, it'll remove all documents). Now, issue the following inserts to get some data we can play with (I suggest you copy and paste this): 128 | 129 | db.unicorns.insert({name: 'Horny', 130 | dob: new Date(1992,2,13,7,47), 131 | loves: ['carrot','papaya'], 132 | weight: 600, 133 | gender: 'm', 134 | vampires: 63}); 135 | db.unicorns.insert({name: 'Aurora', 136 | dob: new Date(1991, 0, 24, 13, 0), 137 | loves: ['carrot', 'grape'], 138 | weight: 450, 139 | gender: 'f', 140 | vampires: 43}); 141 | db.unicorns.insert({name: 'Unicrom', 142 | dob: new Date(1973, 1, 9, 22, 10), 143 | loves: ['energon', 'redbull'], 144 | weight: 984, 145 | gender: 'm', 146 | vampires: 182}); 147 | db.unicorns.insert({name: 'Roooooodles', 148 | dob: new Date(1979, 7, 18, 18, 44), 149 | loves: ['apple'], 150 | weight: 575, 151 | gender: 'm', 152 | vampires: 99}); 153 | db.unicorns.insert({name: 'Solnara', 154 | dob: new Date(1985, 6, 4, 2, 1), 155 | loves:['apple', 'carrot', 156 | 'chocolate'], 157 | weight:550, 158 | gender:'f', 159 | vampires:80}); 160 | db.unicorns.insert({name:'Ayna', 161 | dob: new Date(1998, 2, 7, 8, 30), 162 | loves: ['strawberry', 'lemon'], 163 | weight: 733, 164 | gender: 'f', 165 | vampires: 40}); 166 | db.unicorns.insert({name:'Kenny', 167 | dob: new Date(1997, 6, 1, 10, 42), 168 | loves: ['grape', 'lemon'], 169 | weight: 690, 170 | gender: 'm', 171 | vampires: 39}); 172 | db.unicorns.insert({name: 'Raleigh', 173 | dob: new Date(2005, 4, 3, 0, 57), 174 | loves: ['apple', 'sugar'], 175 | weight: 421, 176 | gender: 'm', 177 | vampires: 2}); 178 | db.unicorns.insert({name: 'Leia', 179 | dob: new Date(2001, 9, 8, 14, 53), 180 | loves: ['apple', 'watermelon'], 181 | weight: 601, 182 | gender: 'f', 183 | vampires: 33}); 184 | db.unicorns.insert({name: 'Pilot', 185 | dob: new Date(1997, 2, 1, 5, 3), 186 | loves: ['apple', 'watermelon'], 187 | weight: 650, 188 | gender: 'm', 189 | vampires: 54}); 190 | db.unicorns.insert({name: 'Nimue', 191 | dob: new Date(1999, 11, 20, 16, 15), 192 | loves: ['grape', 'carrot'], 193 | weight: 540, 194 | gender: 'f'}); 195 | db.unicorns.insert({name: 'Dunx', 196 | dob: new Date(1976, 6, 18, 18, 18), 197 | loves: ['grape', 'watermelon'], 198 | weight: 704, 199 | gender: 'm', 200 | vampires: 165}); 201 | 202 | Now that we have data, we can master selectors. `{field: value}` is used to find any documents where `field` is equal to `value`. `{field1: value1, field2: value2}` is how we do an `and` statement. The special `$lt`, `$lte`, `$gt`, `$gte` and `$ne` are used for less than, less than or equal, greater than, greater than or equal and not equal operations. For example, to get all male unicorns that weigh more than 700 pounds, we could do: 203 | 204 | db.unicorns.find({gender: 'm', 205 | weight: {$gt: 700}}) 206 | //or (not quite the same thing, but for 207 | //demonstration purposes) 208 | db.unicorns.find({gender: {$ne: 'f'}, 209 | weight: {$gte: 701}}) 210 | 211 | The `$exists` operator is used for matching the presence or absence of a field, for example: 212 | 213 | db.unicorns.find({ 214 | vampires: {$exists: false}}) 215 | 216 | Should return a single document. If we want to OR rather than AND we use the `$or` operator and assign it to an array of values we want or'd: 217 | 218 | db.unicorns.find({gender: 'f', 219 | $or: [{loves: 'apple'}, 220 | {loves: 'orange'}, 221 | {weight: {$lt: 500}}]}) 222 | 223 | The above will return all female unicorns which either love apples or oranges or weigh less than 500 pounds. 224 | 225 | There's something pretty neat going on in our last example. You might have already noticed, but the `loves` field is an array. MongoDB supports arrays as first class objects. This is an incredibly handy feature. Once you start using it, you wonder how you ever lived without it. What's more interesting is how easy selecting based on an array value is: `{loves: 'watermelon'}` will return any document where `watermelon` is a value of `loves`. 226 | 227 | There are more available operators than what we've seen so far. The most flexible being `$where` which lets us supply JavaScript to execute on the server. These are all described in the [Advanced Queries](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries) section of the MongoDB website. What we've covered so far though is the basics you'll need to get started. It's also what you'll end up using most of the time. 228 | 229 | We've seen how these selectors can be used with the `find` command. They can also be used with the `remove` command which we've briefly looked at, the `count` command, which we haven't looked at but you can probably figure out, and the `update` command which we'll spend more time with later on. 230 | 231 | The `ObjectId` which MongoDB generated for our `_id` field can be selected like so: 232 | 233 | db.unicorns.find( 234 | {_id: ObjectId("TheObjectId")}) 235 | 236 | ## In This Chapter ## 237 | We haven't looked at the `update` command yet, or some of the fancier things we can do with `find`. However, we did get MongoDB up and running, looked briefly at the `insert` and `remove` commands (there isn't much more than what we've seen). We also introduced `find` and saw what MongoDB `selectors` were all about. We've had a good start and laid a solid foundation for things to come. Believe it or not, you actually know most of what there is to know about MongoDB - it really is meant to be quick to learn and easy to use. I strongly urge you to play with your local copy before moving on. Insert different documents, possibly in new collections, and get familiar with different selectors. Use `find`, `count` and `remove`. After a few tries on your own, things that might have seemed awkward at first will hopefully fall into place. 238 | 239 | # Chapter 2 - Updating # 240 | In chapter 1 we introduced three of the four CRUD (create, read, update and delete) operations. This chapter is dedicated to the one we skipped over: `update`. `Update` has a few surprising behaviors, which is why we dedicate a chapter to it. 241 | 242 | ## Update: Replace Versus $set ## 243 | In its simplest form, `update` takes 2 arguments: the selector (where) to use and what field to update with. If Roooooodles had gained a bit of weight, we could execute: 244 | 245 | db.unicorns.update({name: 'Roooooodles'}, 246 | {weight: 590}) 247 | 248 | (If you've played with your `unicorns` collection and it doesn't have the original data anymore, go ahead and `remove` all documents and re-insert from the code in chapter 1.) 249 | 250 | If this was real code, you'd probably update your records by `_id`, but since I don't know what `_id` MongoDB generated for you, we'll stick to `names`. Now, if we look at the updated record: 251 | 252 | db.unicorns.find({name: 'Roooooodles'}) 253 | 254 | You should discover the first surprise of `update`. No document is found because the second parameter we supply is used to **replace** the original. In other words, the `update` found a document by `name` and replaced the entire document with the new document (the 2nd parameter). This is different than how SQL's `update` command works. In some situations, this is ideal and can be leveraged for some truly dynamic updates. However, when all you want to do is change the value of one, or a few fields, you are best to use MongoDB's `$set` modifier: 255 | 256 | db.unicorns.update({weight: 590}, {$set: { 257 | name: 'Roooooodles', 258 | dob: new Date(1979, 7, 18, 18, 44), 259 | loves: ['apple'], 260 | gender: 'm', 261 | vampires: 99}}) 262 | 263 | This'll reset the lost fields. It won't overwrite the new `weight` since we didn't specify it. Now if we execute: 264 | 265 | db.unicorns.find({name: 'Roooooodles'}) 266 | 267 | We get the expected result. Therefore, the correct way to have updated the weight in the first place is: 268 | 269 | db.unicorns.update({name: 'Roooooodles'}, 270 | {$set: {weight: 590}}) 271 | 272 | ## Update Modifiers ## 273 | In addition to `$set`, we can leverage other modifiers to do some nifty things. All of these update modifiers work on fields - so your entire document won't be wiped out. For example, the `$inc` modifier is used to increment a field by a certain positive or negative amount. For example, if Pilot was incorrectly awarded a couple vampire kills, we could correct the mistake by executing: 274 | 275 | db.unicorns.update({name: 'Pilot'}, 276 | {$inc: {vampires: -2}}) 277 | 278 | If Aurora suddenly developed a sweet tooth, we could add a value to her `loves` field via the `$push` modifier: 279 | 280 | db.unicorns.update({name: 'Aurora'}, 281 | {$push: {loves: 'sugar'}}) 282 | 283 | The [Updating](http://www.mongodb.org/display/DOCS/Updating) section of the MongoDB website has more information on the other available update modifiers. 284 | 285 | ## Upserts ## 286 | One of the more pleasant surprises of using `update` is that it fully supports `upserts`. An `upsert` updates the document if found or inserts it if not. Upserts are handy to have in certain situations and, when you run into one, you'll know it. To enable upserting we set a third parameter to `true`. 287 | 288 | A mundane example is a hit counter for a website. If we wanted to keep an aggregate count in real time, we'd have to see if the record already existed for the page, and based on that decide to run an update or insert. With the third parameter omitted (or set to false), executing the following won't do anything: 289 | 290 | db.hits.update({page: 'unicorns'}, 291 | {$inc: {hits: 1}}); 292 | db.hits.find(); 293 | 294 | However, if we enable upserts, the results are quite different: 295 | 296 | db.hits.update({page: 'unicorns'}, 297 | {$inc: {hits: 1}}, true); 298 | db.hits.find(); 299 | 300 | Since no documents exists with a field `page` equal to `unicorns`, a new document is inserted. If we execute it a second time, the existing document is updated and `hits` is incremented to 2. 301 | 302 | db.hits.update({page: 'unicorns'}, 303 | {$inc: {hits: 1}}, true); 304 | db.hits.find(); 305 | 306 | ## Multiple Updates ## 307 | The final surprise `update` has to offer is that, by default, it'll update a single document. So far, for the examples we've looked at, this might seem logical. However, if you executed something like: 308 | 309 | db.unicorns.update({}, 310 | {$set: {vaccinated: true }}); 311 | db.unicorns.find({vaccinated: true}); 312 | 313 | You'd expect to find all of your precious unicorns to be vaccinated. To get the behavior you desire, a fourth parameter must be set to true: 314 | 315 | db.unicorns.update({}, 316 | {$set: {vaccinated: true }}, 317 | false, true); 318 | db.unicorns.find({vaccinated: true}); 319 | 320 | ## In This Chapter ## 321 | This chapter concluded our introduction to the basic CRUD operations available against a collection. We looked at `update` in detail and observed three interesting behaviors. First, unlike an SQL update, MongoDB's `update` replaces the actual document. Because of this the `$set` modifier is quite useful. Secondly, `update` supports an intuitive `upsert` which is particularly useful when paired with the `$inc` modifier. Finally, by default, `update` only updates the first found document. 322 | 323 | Do remember that we are looking at MongoDB from the point of view of its shell. The driver and library you use could alter these default behaviors or expose a different API. For example, the Ruby driver merges the last two parameters into a single hash: `{:upsert => false, :multi => false}`. Similarly, the PHP driver, merges the last two parameters into an array: `array('upsert' => false, 'multiple' => false)`. 324 | 325 | # Chapter 3 - Mastering Find # 326 | Chapter 1 provided a superficial look at the `find` command. There's more to `find` than understanding `selectors` though. We already mentioned that the result from `find` is a `cursor`. We'll now look at exactly what this means in more detail. 327 | 328 | ## Field Selection ## 329 | Before we jump into `cursors`, you should know that `find` takes a second optional parameter. This parameter is the list of fields we want to retrieve. For example, we can get all of the unicorns' names by executing: 330 | 331 | db.unicorns.find(null, {name: 1}); 332 | 333 | By default, the `_id` field is always returned. We can explicitly exclude it by specifying `{name:1, _id: 0}`. 334 | 335 | Aside from the `_id` field, you cannot mix and match inclusion and exclusion. If you think about it, that actually makes sense. You either want to select or exclude one or more fields explicitly. 336 | 337 | ## Ordering ## 338 | A few times now I've mentioned that `find` returns a cursor whose execution is delayed until needed. However, what you've no doubt observed from the shell is that `find` executes immediately. This is a behavior of the shell only. We can observe the true behavior of `cursors` by looking at one of the methods we can chain to `find`. The first that we'll look at is `sort`. `sort` works a lot like the field selection from the previous section. We specify the fields we want to sort on, using 1 for ascending and -1 for descending. For example: 339 | 340 | //heaviest unicorns first 341 | db.unicorns.find().sort({weight: -1}) 342 | 343 | //by unicorn name then vampire kills: 344 | db.unicorns.find().sort({name: 1, 345 | vampires: -1}) 346 | 347 | As with a relational database, MongoDB can use an index for sorting. We'll look at indexes in more detail later on. However, you should know that MongoDB limits the size of your sort without an index. That is, if you try to sort a large result set which can't use an index, you'll get an error. Some people see this as a limitation. In truth, I wish more databases had the capability to refuse to run unoptimized queries. (I won't turn every MongoDB drawback into a positive, but I've seen enough poorly optimized databases that I sincerely wish they had a strict-mode.) 348 | 349 | ## Paging ## 350 | Paging results can be accomplished via the `limit` and `skip` cursor methods. To get the second and third heaviest unicorn, we could do: 351 | 352 | db.unicorns.find() 353 | .sort({weight: -1}) 354 | .limit(2) 355 | .skip(1) 356 | 357 | Using `limit` in conjunction with `sort`, is a good way to avoid running into problems when sorting on non-indexed fields. 358 | 359 | ## Count ## 360 | The shell makes it possible to execute a `count` directly on a collection, such as: 361 | 362 | db.unicorns.count({vampires: {$gt: 50}}) 363 | 364 | In reality, `count` is actually a `cursor` method, the shell simply provides a shortcut. Drivers which don't provide such a shortcut need to be executed like this (which will also work in the shell): 365 | 366 | db.unicorns.find({vampires: {$gt: 50}}) 367 | .count() 368 | 369 | ## In This Chapter ## 370 | Using `find` and `cursors` is a straightforward proposition. There are a few additional commands that we'll either cover in later chapters or which only serve edge cases, but, by now, you should be getting pretty comfortable working in the mongo shell and understanding the fundamentals of MongoDB. 371 | 372 | # Chapter 4 - Data Modeling # 373 | Let's shift gears and have a more abstract conversation about MongoDB. Explaining a few new terms and some new syntax is a trivial task. Having a conversation about modeling with a new paradigm isn't as easy. The truth is that most of us are still finding out what works and what doesn't when it comes to modeling with these new technologies. It's a conversation we can start having, but ultimately you'll have to practice and learn on real code. 374 | 375 | Out of all NoSQL databases, document-oriented databases are probably the most similar to relational databases - at least when it comes to modeling. The differences which exist are subtle but that doesn't mean they aren't important. 376 | 377 | ## No Joins ## 378 | The first and most fundamental difference that you'll need to get comfortable with is MongoDB's lack of joins. I don't know the specific reason why some type of join syntax isn't supported in MongoDB, but I do know that joins are generally seen as non-scalable. That is, once you start to split your data horizontally, you end up performing your joins on the client (the application server) anyways. Regardless of the reasons, the fact remains that data *is* relational, and MongoDB doesn't support joins. 379 | 380 | Without knowing anything else, to live in a join-less world, we have to do joins ourselves within our application's code. Essentially we need to issue a second query to `find` the relevant data. Setting our data up isn't any different than declaring a foreign key in a relational database. Let's give a little less focus to our beautiful `unicorns` and a bit more time to our `employees`. The first thing we'll do is create an employee (I'm providing an explicit `_id` so that we can build coherent examples) 381 | 382 | db.employees.insert({_id: ObjectId( 383 | "4d85c7039ab0fd70a117d730"), 384 | name: 'Leto'}) 385 | 386 | Now let's add a couple employees and set their manager as `Leto`: 387 | 388 | db.employees.insert({_id: ObjectId( 389 | "4d85c7039ab0fd70a117d731"), 390 | name: 'Duncan', 391 | manager: ObjectId( 392 | "4d85c7039ab0fd70a117d730")}); 393 | db.employees.insert({_id: ObjectId( 394 | "4d85c7039ab0fd70a117d732"), 395 | name: 'Moneo', 396 | manager: ObjectId( 397 | "4d85c7039ab0fd70a117d730")}); 398 | 399 | 400 | (It's worth repeating that the `_id` can be any unique value. Since you'd likely use an `ObjectId` in real life, we'll use them here as well.) 401 | 402 | Of course, to find all of Leto's employees, one simply executes: 403 | 404 | db.employees.find({manager: ObjectId( 405 | "4d85c7039ab0fd70a117d730")}) 406 | 407 | There's nothing magical here. In the worst cases, most of the time, the lack of join will merely require an extra query (likely indexed). 408 | 409 | ## Arrays and Embedded Documents ## 410 | Just because MongoDB doesn't have joins doesn't mean it doesn't have a few tricks up its sleeve. Remember when we quickly saw that MongoDB supports arrays as first class objects of a document? It turns out that this is incredibly handy when dealing with many-to-one or many-to-many relationships. As a simple example, if an employee could have two managers, we could simply store these in an array: 411 | 412 | db.employees.insert({_id: ObjectId( 413 | "4d85c7039ab0fd70a117d733"), 414 | name: 'Siona', 415 | manager: [ObjectId( 416 | "4d85c7039ab0fd70a117d730"), 417 | ObjectId( 418 | "4d85c7039ab0fd70a117d732")] }) 419 | 420 | Of particular interest is that, for some documents, `manager` can be a scalar value, while for others it can be an array. Our original `find` query will work for both: 421 | 422 | db.employees.find({manager: ObjectId( 423 | "4d85c7039ab0fd70a117d730")}) 424 | 425 | You'll quickly find that arrays of values are much more convenient to deal with than many-to-many join-tables. 426 | 427 | Besides arrays, MongoDB also supports embedded documents. Go ahead and try inserting a document with a nested document, such as: 428 | 429 | db.employees.insert({_id: ObjectId( 430 | "4d85c7039ab0fd70a117d734"), 431 | name: 'Ghanima', 432 | family: {mother: 'Chani', 433 | father: 'Paul', 434 | brother: ObjectId( 435 | "4d85c7039ab0fd70a117d730")}}) 436 | 437 | In case you are wondering, embedded documents can be queried using a dot-notation: 438 | 439 | db.employees.find({ 440 | 'family.mother': 'Chani'}) 441 | 442 | We'll briefly talk about where embedded documents fit and how you should use them. 443 | 444 | ## DBRef ## 445 | MongoDB supports something known as `DBRef` which is a convention many drivers support. When a driver encounters a `DBRef` it can automatically pull the referenced document. A `DBRef` includes the collection and id of the referenced document. It generally serves a pretty specific purpose: when documents from the same collection might reference documents from a different collection from each other. That is, the `DBRef` for `document1` might point to a document in `managers` whereas the `DBRef` for `document2` might point to a document in `employees`. 446 | 447 | 448 | ## Denormalization ## 449 | Yet another alternative to using joins is to denormalize your data. Historically, denormalization was reserved for performance-sensitive code, or when data should be snapshotted (like in an audit log). However, with the ever-growing popularity of NoSQL, many of which don't have joins, denormalization as part of normal modeling is becoming increasingly common. This doesn't mean you should duplicate every piece of information in every document. However, rather than letting fear of duplicate data drive your design decisions, consider modeling your data based on what information belongs to what document. 450 | 451 | For example, say you are writing a forum application. The traditional way to associate a specific `user` with a `post` is via a `userid` column within `posts`. With such a model, you can't display `posts` without retrieving (joining to) `users`. A possible alternative is simply to store the `name` as well as the `userid` with each `post`. You could even do so with an embedded document, like `user: {id: ObjectId('Something'), name: 'Leto'}`. Yes, if you let users change their name, you'll have to update each document (which is 1 extra query). 452 | 453 | Adjusting to this kind of approach won't come easy to some. In a lot of cases it won't even make sense to do this. Don't be afraid to experiment with this approach though. It's not only suitable in some circumstances, but it can also be the right way to do it. 454 | 455 | ## Which Should You Choose? ## 456 | Arrays of ids are always a useful strategy when dealing with one-to-many or many-to-many scenarios. It's probably safe to say that `DBRef`s aren't used very often, though you can certainly experiment and play with them. That generally leaves new developers unsure about using embedded documents versus doing manual referencing. 457 | 458 | First, you should know that an individual document is currently limited to 16 megabytes in size. Knowing that documents have a size limit, though quite generous, gives you some idea of how they are intended to be used. At this point, it seems like most developers lean heavily on manual references for most of their relationships. Embedded documents are frequently leveraged, but mostly for small pieces of data which we want to always pull with the parent document. A real world example I've used is to store an `accounts` document with each user, something like: 459 | 460 | db.users.insert({name: 'leto', 461 | email: 'leto@dune.gov', 462 | account: {allowed_gholas: 5, 463 | spice_ration: 10}}) 464 | 465 | That doesn't mean you should underestimate the power of embedded documents or write them off as something of minor utility. Having your data model map directly to your objects makes things a lot simpler and often does remove the need to join. This is especially true when you consider that MongoDB lets you query and index fields of an embedded document. 466 | 467 | ## Few or Many Collections ## 468 | Given that collections don't enforce any schema, it's entirely possible to build a system using a single collection with a mishmash of documents. From what I've seen, most MongoDB systems are laid out similarly to what you'd find in a relational system. In other words, if it would be a table in a relational database, it'll likely be a collection in MongoDB (many-to-many join tables being an important exception). 469 | 470 | The conversation gets even more interesting when you consider embedded documents. The example that frequently comes up is a blog. Should you have a `posts` collection and a `comments` collection, or should each `post` have an array of `comments` embedded within it? Setting aside the 16MB document size limit for the time being (all of *Hamlet* is less than 200KB, so just how popular is your blog?), most developers still prefer to separate things out. It's simply cleaner and more explicit. 471 | 472 | There's no hard rule (well, aside from 16MB). Play with different approaches and you'll get a sense of what does and does not feel right. 473 | 474 | ## In This Chapter ## 475 | Our goal in this chapter was to provide some helpful guidelines for modeling your data in MongoDB. A starting point, if you will. Modeling in a document-oriented system is different, but not too different, than in a relational world. You have a bit more flexibility and one constraint, but for a new system, things tend to fit quite nicely. The only way you can go wrong is by not trying. 476 | 477 | # Chapter 5 - When To Use MongoDB # 478 | By now you should have a feel for where and how it might fit into your existing system. There are enough new and competing storage technologies that it's easy to get overwhelmed by all of the choices. 479 | 480 | For me, the most important lesson, which has nothing to do with MongoDB, is that you no longer have to rely on a single solution for dealing with your data. No doubt, a single solution has obvious advantages, and for a lot projects - possibly even most - a single solution is the sensible approach. The idea isn't that you *must* use different technologies, but rather that you *can*. Only you know whether the benefits of introducing a new solution outweigh the costs. 481 | 482 | With that said, I'm hopeful that what you've seen so far has made you see MongoDB as a general solution. It's been mentioned a couple times that document-oriented databases share a lot in common with relational databases. Therefore, rather than tiptoeing around it, let's simply state that MongoDB should be seen as a direct alternative to relational databases. Where one might see Lucene as enhancing a relational database with full text indexing, or Redis as a persistent key-value store, MongoDB is a central repository for your data. 483 | 484 | Notice that I didn't call MongoDB a *replacement* for relational databases, but rather an *alternative*. It's a tool that can do what a lot of other tools can do. Some of it MongoDB does better, some of it MongoDB does worse. Let's dissect things a little further. 485 | 486 | ## Schema-less ## 487 | An oft-touted benefit of document-oriented database is that they are schema-less. This makes them much more flexible than traditional database tables. I agree that schema-less is a nice feature, but not for the main reason most people mention. 488 | 489 | People talk about schema-less as though you'll suddenly start storing a crazy mishmash of data. There are domains and data sets which can really be a pain to model using relational databases, but I see those as edge cases. Schema-less is cool, but most of your data is going to be highly structured. It's true that having an occasional mismatch can be handy, especially when you introduce new features, but in reality it's nothing a nullable column probably wouldn't solve just as well. 490 | 491 | For me, the real benefit of schema-less design is the lack of setup and the reduced friction with OOP. This is particularly true when you're working with a static language. I've worked with MongoDB in both C# and Ruby, and the difference is striking. Ruby's dynamism and its popular ActiveRecord implementations already reduce much of the object-relational impedance mismatch. That isn't to say MongoDB isn't a good match for Ruby, it really is. Rather, I think most Ruby developers would see MongoDB as an incremental improvement, whereas C# or Java developers would see a fundamental shift in how they interact with their data. 492 | 493 | Think about it from the perspective of a driver developer. You want to save an object? Serialize it to JSON (technically BSON, but close enough) and send it to MongoDB. There is no property mapping or type mapping. This straightforwardness definitely flows to you, the end developer. 494 | 495 | ## Writes ## 496 | One area where MongoDB can fit a specialized role is in logging. There are two aspects of MongoDB which make writes quite fast. First, you can send a write command and have it return immediately without waiting for it to actually write. Secondly, with the introduction of journaling in 1.8, and enhancements made in 2.0, you can control the write behavior with respect to data durability. These settings, in addition to specifying how many servers should get your data before being considered successful, are configurable per-write, giving you a great level of control over write performance and data durability. 497 | 498 | In addition to these performance factors, log data is one of those data sets which can often take advantage of schema-less collections. Finally, MongoDB has something called a [capped collection](http://www.mongodb.org/display/DOCS/Capped+Collections). So far, all of the implicitly created collections we've created are just normal collections. We can create a capped collection by using the `db.createCollection` command and flagging it as capped: 499 | 500 | //limit our capped collection to 1 megabyte 501 | db.createCollection('logs', {capped: true, 502 | size: 1048576}) 503 | 504 | When our capped collection reaches its 1MB limit, old documents are automatically purged. A limit on the number of documents, rather than the size, can be set using `max`. Capped collections have some interesting properties. For example, you can update a document but it can't grow in size. Also, the insertion order is preserved, so you don't need to add an extra index to get proper time-based sorting. 505 | 506 | This is a good place to point out that if you want to know whether your write encountered any errors (as opposed to the default fire-and-forget), you simply issue a follow-up command: `db.getLastError()`. Most drivers encapsulate this as a *safe write*, say by specifying `{:safe => true}` as a second parameter to `insert`. 507 | 508 | ## Durability ## 509 | Prior to version 1.8, MongoDB didn't have single-server durability. That is, a server crash would likely result in lost data. The solution had always been to run MongoDB in a multi-server setup (MongoDB supports replication). One of the major features added to 1.8 was journaling. To enable it add a new line with `journal=true` to the `mongodb.config` file we created when we first setup MongoDB (and restart your server if you want it enabled right away). You probably want journaling enabled (it'll be a default in a future release). Although, in some circumstances the extra throughput you get from disabling journaling might be a risk you are willing to take. (It's worth pointing out that some types of applications can easily afford to lose data). 510 | 511 | Durability is only mentioned here because a lot has been made around MongoDB's lack of single-server durability. This'll likely show up in Google searches for some time to come. Information you find about this missing feature is simply out of date. 512 | 513 | ## Full Text Search ## 514 | True full text search capability is something that'll hopefully come to MongoDB in a future release. With its support for arrays, basic full text search is pretty easy to implement. For something more powerful, you'll need to rely on a solution such as Lucene/Solr. Of course, this is also true of many relational databases. 515 | 516 | ## Transactions ## 517 | MongoDB doesn't have transactions. It has two alternatives, one which is great but with limited use, and the other that is cumbersome but flexible. 518 | 519 | The first is its many atomic operations. These are great, so long as they actually address your problem. We already saw some of the simpler ones, like `$inc` and `$set`. There are also commands like `findAndModify` which can update or delete a document and return it atomically. 520 | 521 | The second, when atomic operations aren't enough, is to fall back to a two-phase commit. A two-phase commit is to transactions what manual dereferencing is to joins. It's a storage-agnostic solution that you do in code. Two-phase commits are actually quite popular in the relational world as a way to implement transactions across multiple databases. The MongoDB website [has an example](http://www.mongodb.org/display/DOCS/two-phase+commit) illustrating the most common scenario (a transfer of funds). The general idea is that you store the state of the transaction within the actual document being updated and go through the init-pending-commit/rollback steps manually. 522 | 523 | MongoDB's support for nested documents and schema-less design makes two-phase commits slightly less painful, but it still isn't a great process, especially when you are just getting started with it. 524 | 525 | ## Data Processing ## 526 | MongoDB relies on MapReduce for most data processing jobs. It has some [basic aggregation](http://www.mongodb.org/display/DOCS/Aggregation) capabilities, but for anything serious, you'll want to use MapReduce. In the next chapter we'll look at MapReduce in detail. For now you can think of it as a very powerful and different way to `group by` (which is an understatement). One of MapReduce's strengths is that it can be parallelized for working with large sets of data. However, MongoDB's implementation relies on JavaScript, which is single-threaded. The point? For processing of large data, you'll likely need to rely on something else, such as Hadoop. Thankfully, since the two systems really do complement each other, there's a [MongoDB adapter for Hadoop](https://github.com/mongodb/mongo-hadoop). 527 | 528 | Of course, parallelizing data processing isn't something relational databases excel at either. There are plans for future versions of MongoDB to be better at handling very large sets of data. 529 | 530 | ## Geospatial ## 531 | A particularly powerful feature of MongoDB is its support for geospatial indexes. This allows you to store x and y coordinates within documents and then find documents that are `$near` a set of coordinates or `$within` a box or circle. This is a feature best explained via some visual aids, so I invite you to try the [5 minute geospatial interactive tutorial](http://tutorial.mongly.com/geo/index), if you want to learn more. 532 | 533 | ## Tools and Maturity ## 534 | You probably already know the answer to this, but MongoDB is obviously younger than most relational database systems. This is absolutely something you should consider, though how much it matters depends on what you are doing and how you are doing it. Nevertheless, an honest assessment simply can't ignore the fact that MongoDB is younger and the available tooling around isn't great (although the tooling around a lot of very mature relational databases is pretty horrible too!). As an example, the lack of support for base-10 floating point numbers will obviously be a concern (though not necessarily a show-stopper) for systems dealing with money. 535 | 536 | On the positive side, drivers exist for a great many languages, the protocol is modern and simple, and development is happening at blinding speeds. MongoDB is in production at enough companies that concerns about maturity, while valid, are quickly becoming a thing of the past. 537 | 538 | ## In This Chapter ## 539 | The message from this chapter is that MongoDB, in most cases, can replace a relational database. It's much simpler and straightforward; it's faster and generally imposes fewer restrictions on application developers. The lack of transactions can be a legitimate and serious concern. However, when people ask *where does MongoDB sit with respect to the new data storage landscape?* the answer is simple: **right in the middle**. 540 | 541 | # Chapter 6 - MapReduce # 542 | MapReduce is an approach to data processing which has two significant benefits over more traditional solutions. The first, and main, reason it was developed is performance. In theory, MapReduce can be parallelized, allowing very large sets of data to be processed across many cores/CPUs/machines. As we just mentioned, this isn't something MongoDB is currently able to take advantage of. The second benefit of MapReduce is that you get to write real code to do your processing. Compared to what you'd be able to do with SQL, MapReduce code is infinitely richer and lets you push the envelope further before you need to use a more specialized solution. 543 | 544 | MapReduce is a pattern that has grown in popularity, and you can make use of it almost anywhere; C#, Ruby, Java, Python and so on all have implementations. I want to warn you that at first this'll seem very different and complicated. Don't get frustrated, take your time and play with it yourself. This is worth understanding whether you are using MongoDB or not. 545 | 546 | ## A Mix of Theory and Practice ## 547 | MapReduce is a two-step process. First you map, and then you reduce. The mapping step transforms the inputted documents and emits a key=>value pair (the key and/or value can be complex). Then, key/value pairs are grouped by key, such that values for the same key end up in an array. The reduce gets a key and this array of values emitted for that key, and produces the final result. We'll look at each step, and the output of each step. 548 | 549 | The example that we'll be using is to generate a report of the number of hits, per day, we get on a resource (say a webpage). This is the *hello world* of MapReduce. For our purposes, we'll rely on a `hits` collection with two fields: `resource` and `date`. Our desired output is a breakdown by `resource`, `year`, `month`, `day` and `count`. 550 | 551 | Given the following data in `hits`: 552 | 553 | resource date 554 | index Jan 20 2010 4:30 555 | index Jan 20 2010 5:30 556 | about Jan 20 2010 6:00 557 | index Jan 20 2010 7:00 558 | about Jan 21 2010 8:00 559 | about Jan 21 2010 8:30 560 | index Jan 21 2010 8:30 561 | about Jan 21 2010 9:00 562 | index Jan 21 2010 9:30 563 | index Jan 22 2010 5:00 564 | 565 | We'd expect the following output: 566 | 567 | resource year month day count 568 | index 2010 1 20 3 569 | about 2010 1 20 1 570 | about 2010 1 21 3 571 | index 2010 1 21 2 572 | index 2010 1 22 1 573 | 574 | The nice thing about this type of approach to analytics is that by storing the output, reports are fast to generate and data growth is controlled (per resource that we track, we'll add at most 1 document per day.) 575 | 576 | For the time being, focus on understanding the concept. At the end of this chapter, sample data and code will be given for you to try on your own. 577 | 578 | The first thing to do is look at the map function. The goal of map is to make it emit a value which can be reduced. It's possible for map to emit 0 or more times. In our case, it'll always emit once (which is common). Imagine map as looping through each document in hits. For each document we want to emit a key with resource, year, month and day, and a simple value of 1: 579 | 580 | function() { 581 | var key = { 582 | resource: this.resource, 583 | year: this.date.getFullYear(), 584 | month: this.date.getMonth(), 585 | day: this.date.getDate() 586 | }; 587 | emit(key, {count: 1}); 588 | } 589 | 590 | `this` refers to the current document being inspected. Hopefully what'll help make this clear for you is to see what the output of the mapping step is. Using our above data, the complete output would is below. The values from `emit` are grouped together, as arrays, by key: 591 | 592 | {resource: 'index', year: 2010, month: 0, 593 | day: 20} => [{count: 1}, 594 | {count: 1}, {count:1}] 595 | {resource: 'about', year: 2010, month: 0, 596 | day: 20} => [{count: 1}] 597 | {resource: 'about', year: 2010, month: 0, 598 | day: 21} => [{count: 1}, {count: 1}, 599 | {count:1}] 600 | {resource: 'index', year: 2010, month: 0, 601 | day: 21} => [{count: 1}, {count: 1}] 602 | {resource: 'index', year: 2010, month: 0, 603 | day: 22} => [{count: 1}] 604 | 605 | Understanding this intermediary step is the key to understanding MapReduce. .NET and Java developers can think of it as being of type `IDictionary>` (.NET) or `HashMap` (Java). 606 | 607 | Let's change our map function in some contrived way: 608 | 609 | function() { 610 | var key = {resource: this.resource, 611 | year: this.date.getFullYear(), 612 | month: this.date.getMonth(), 613 | day: this.date.getDate()}; 614 | if (this.resource == 'index' && 615 | this.date.getHours() == 4) { 616 | emit(key, {count: 5}); 617 | } else { 618 | emit(key, {count: 1}); 619 | } 620 | } 621 | 622 | The first intermediary output would change to: 623 | 624 | {resource: 'index', year: 2010, month: 0, 625 | day: 20} => [{count: 5}, 626 | {count: 1}, {count:1}] 627 | 628 | Notice how each emit generates a new value which is grouped by our key. 629 | 630 | The reduce function takes each of these intermediary results and outputs a final result. Here's what ours looks like: 631 | 632 | function(key, values) { 633 | var sum = 0; 634 | values.forEach(function(value) { 635 | sum += value['count']; 636 | }); 637 | return {count: sum}; 638 | }; 639 | 640 | Which would output: 641 | 642 | {resource: 'index', year: 2010, month: 0, 643 | day: 20} => {count: 3} 644 | {resource: 'about', year: 2010, month: 0, 645 | day: 20} => {count: 1} 646 | {resource: 'about', year: 2010, month: 0, 647 | day: 21} => {count: 3} 648 | {resource: 'index', year: 2010, month: 0, 649 | day: 21} => {count: 2} 650 | {resource: 'index', year: 2010, month: 0, 651 | day: 22} => {count: 1} 652 | 653 | Technically, the output in MongoDB is: 654 | 655 | _id: {resource: 'index', year: 2010, 656 | month: 0, day: 20}, 657 | value: {count: 3} 658 | 659 | Hopefully you've noticed that this is the final result we were after. 660 | 661 | If you've really been paying attention, you might be asking yourself why we didn't use `sum = values.length`. This would seem like an efficient approach when you are summing an array of 1s. This however, is not guaranteed to work. Reduce can be called multiple times with only a portion of the overall values. The purpose of this is to allow the reduce function to be distributed across threads, processes or even computers. The result of two or more reduce functions are fed back into the same reduce function (many times over, for a large data set). 662 | 663 | Going back to our example, reduce might be called once with the following input: 664 | 665 | {resource: 'index', year: 2010, month: 0, 666 | day: 20} => [{count: 1}, 667 | {count: 1}, {count:1}] 668 | 669 | or it might be called twice with the output of *step 1* making up part of the input to *step 2*: 670 | 671 | //STEP 1 672 | {resource: 'index', year: 2010, month: 0, 673 | day: 20} => [{count: 1}, {count: 1}] 674 | //STEP 2 675 | {resource: 'index', year: 2010, month: 0, 676 | day: 20} => [{count: 2}, {count: 1}] 677 | 678 | Using `sum = values.length` would incorrectly return `{count: 2}` from the second step. 679 | 680 | In general, the structure of reduce's output must be the same as its input, and calling reduce multiple times should produce the same result (known as idempotency). 681 | 682 | Finally, we aren't going to cover it here but it's common to chain reduce methods when performing more complex analysis. 683 | 684 | ## Pure Practical ## 685 | With MongoDB we use the `mapReduce` command on a collection. `mapReduce` takes a map function, a reduce function and an output directive. In our shell we can create and pass a JavaScript function. From most libraries you supply a string of your functions (which is a bit ugly). First though, let's create our simple data set: 686 | 687 | db.hits.insert({resource: 'index', 688 | date: new Date(2010, 0, 20, 4, 30)}); 689 | db.hits.insert({resource: 'index', 690 | date: new Date(2010, 0, 20, 5, 30)}); 691 | db.hits.insert({resource: 'about', 692 | date: new Date(2010, 0, 20, 6, 0)}); 693 | db.hits.insert({resource: 'index', 694 | date: new Date(2010, 0, 20, 7, 0)}); 695 | db.hits.insert({resource: 'about', 696 | date: new Date(2010, 0, 21, 8, 0)}); 697 | db.hits.insert({resource: 'about', 698 | date: new Date(2010, 0, 21, 8, 30)}); 699 | db.hits.insert({resource: 'index', 700 | date: new Date(2010, 0, 21, 8, 30)}); 701 | db.hits.insert({resource: 'about', 702 | date: new Date(2010, 0, 21, 9, 0)}); 703 | db.hits.insert({resource: 'index', 704 | date: new Date(2010, 0, 21, 9, 30)}); 705 | db.hits.insert({resource: 'index', 706 | date: new Date(2010, 0, 22, 5, 0)}); 707 | 708 | Now we can create our map and reduce functions (the MongoDB shell accepts multi-line statements, you'll see *...* after hitting enter to indicate more text is expected): 709 | 710 | var map = function() { 711 | var key = {resource: this.resource, 712 | year: this.date.getFullYear(), 713 | month: this.date.getMonth(), 714 | day: this.date.getDate()}; 715 | emit(key, {count: 1}); 716 | }; 717 | 718 | var reduce = function(key, values) { 719 | var sum = 0; 720 | values.forEach(function(value) { 721 | sum += value['count']; 722 | }); 723 | return {count: sum}; 724 | }; 725 | 726 | We can pass our `map` and `reduce` functions to the `mapReduce` command by running: 727 | 728 | db.hits.mapReduce(map, reduce, 729 | {out: {inline:1}}) 730 | 731 | If you run the above, you should see the desired output. Setting `out` to `inline` means that the output from `mapReduce` is immediately streamed back to us. This is currently limited for results that are 16 megabytes or less. We could instead specify `{out: 'hit_stats'}` and have the results stored in the `hit_stats` collections: 732 | 733 | db.hits.mapReduce(map, reduce, 734 | {out: 'hit_stats'}); 735 | db.hit_stats.find(); 736 | 737 | When you do this, any existing data in `hit_stats` is lost. If we did `{out: {merge: 'hit_stats'}}` existing keys would be replaced with the new values and new keys would be inserted as new documents. Finally, we can `out` using a `reduce` function to handle more advanced cases (such an doing an upsert). 738 | 739 | The third parameter takes additional options, for example we could filter, sort and limit the documents that we want analyzed. We can also supply a `finalize` method to be applied to the results after the `reduce` step. 740 | 741 | ## In This Chapter ## 742 | This is the first chapter where we covered something truly different. If it made you uncomfortable, remember that you can always use MongoDB's other [aggregation capabilities](http://www.mongodb.org/display/DOCS/Aggregation) for simpler scenarios. Ultimately though, MapReduce is one of MongoDB's most compelling features. The key to really understanding how to write your map and reduce functions is to visualize and understand the way your intermediary data will look coming out of `map` and heading into `reduce`. 743 | 744 | # Chapter 7 - Performance and Tools # 745 | In this last chapter, we look at a few performance topics as well as some of the tools available to MongoDB developers. We won't dive deeply into either topic, but we will examine the most important aspects of each. 746 | 747 | ## Indexes ## 748 | At the very beginning we saw the special `system.indexes` collection which contains information on all the indexes in our database. Indexes in MongoDB work a lot like indexes in a relational database: they help improve query and sorting performance. Indexes are created via `ensureIndex`: 749 | 750 | // where "name" is the fieldname 751 | db.unicorns.ensureIndex({name: 1}); 752 | 753 | And dropped via `dropIndex`: 754 | 755 | db.unicorns.dropIndex({name: 1}); 756 | 757 | A unique index can be created by supplying a second parameter and setting `unique` to `true`: 758 | 759 | db.unicorns.ensureIndex({name: 1}, 760 | {unique: true}); 761 | 762 | Indexes can be created on embedded fields (again, using the dot-notation) and on array fields. We can also create compound indexes: 763 | 764 | db.unicorns.ensureIndex({name: 1, 765 | vampires: -1}); 766 | 767 | The order of your index (1 for ascending, -1 for descending) doesn't matter for a single key index, but it can have an impact for compound indexes when you are sorting or using a range condition. 768 | 769 | The [indexes page](http://www.mongodb.org/display/DOCS/Indexes) has additional information on indexes. 770 | 771 | ## Explain ## 772 | To see whether or not your queries are using an index, you can use the `explain` method on a cursor: 773 | 774 | db.unicorns.find().explain() 775 | 776 | The output tells us that a `BasicCursor` was used (which means non-indexed), that 12 objects were scanned, how long it took, what index, if any, was used as well as a few other pieces of useful information. 777 | 778 | If we change our query to use an index, we'll see that a `BtreeCursor` was used, as well as the index used to fulfill the request: 779 | 780 | db.unicorns.find({name: 'Pilot'}).explain() 781 | 782 | ## Fire And Forget Writes ## 783 | We previously mentioned that, by default, writes in MongoDB are fire-and-forget. This can result in some nice performance gains at the risk of losing data during a crash. An interesting side effect of this type of write is that an error is not returned when an insert/update violates a unique constraint. In order to be notified about a failed write, one must call `db.getLastError()` after an insert. Many drivers abstract this detail away and provide a way to do a *safe* write - often via an extra parameter. 784 | 785 | Unfortunately, the shell automatically does safe inserts, so we can't easily see this behavior in action. 786 | 787 | ## Sharding ## 788 | MongoDB supports auto-sharding. Sharding is an approach to scalability which separates your data across multiple servers. A naive implementation might put all of the data for users with a name that starts with A-M on server 1 and the rest on server 2. Thankfully, MongoDB's sharding capabilities far exceed such a simple algorithm. Sharding is a topic well beyond the scope of this book, but you should know that it exists and that you should consider it, should your needs grow beyond a single server. 789 | 790 | ## Replication ## 791 | MongoDB replication works similarly to how relational database replication works. Writes are sent to a single server, the master, which then synchronizes itself to one or more other servers, the slaves. You can control whether reads can happen on slaves or not, which can help distribute your load at the risk of reading slightly stale data. If the master goes down, a slave can be promoted to act as the new master. Again, MongoDB replication is outside the scope of this book. 792 | 793 | While replication can improve performance (by distributing reads), its main purpose is to increase reliability. Combining replication with sharding is a common approach. For example, each shard could be made up of a master and a slave. (Technically you'll also need an arbiter to help break a tie should two slaves try to become masters. But an arbiter requires very few resources and can be used for multiple shards.) 794 | 795 | ## Stats ## 796 | You can obtain statistics on a database by typing `db.stats()`. Most of the information deals with the size of your database. You can also get statistics on a collection, say `unicorns`, by typing `db.unicorns.stats()`. Again, most of this information relates to the size of your collection. 797 | 798 | ## Web Interface ## 799 | Included in the information displayed on MongoDB's startup was a link to a web-based administrative tool (you might still be able to see it if you scroll your command/terminal window up to the point where you started `mongod`). You can access this by pointing your browser to . To get the most out of it, you'll want to add `rest=true` to your config and restart the `mongod` process. The web interface gives you a lot of insight into the current state of your server. 800 | 801 | ## Profiler ## 802 | You can enable the MongoDB profiler by executing: 803 | 804 | db.setProfilingLevel(2); 805 | 806 | With it enabled, we can run a command: 807 | 808 | db.unicorns.find({weight: {$gt: 600}}); 809 | 810 | And then examine the profiler: 811 | 812 | db.system.profile.find() 813 | 814 | The output tells us what was run and when, how many documents were scanned, and how much data was returned. 815 | 816 | You can disable the profiler by calling `setProfileLevel` again but changing the argument to `0`. Another option is to specify `1` which will only profile queries that take more than 100 milliseconds. Or, you can specify the minimum time, in milliseconds, with a second parameter: 817 | 818 | //profile anything that takes 819 | //more than 1 second 820 | db.setProfilingLevel(1, 1000); 821 | 822 | ## Backups and Restore ## 823 | Within the MongoDB `bin` folder is a `mongodump` executable. Simply executing `mongodump` will connect to localhost and backup all of your databases to a `dump` subfolder. You can type `mongodump --help` to see additional options. Common options are `--db DBNAME` to back up a specific database and `--collection COLLECTIONNAME` to back up a specific collection. You can then use the `mongorestore` executable, located in the same `bin` folder, to restore a previously made backup. Again, the `--db` and `--collection` can be specified to restore a specific database and/or collection. 824 | 825 | For example, to back up our `learn` database to a `backup` folder, we'd execute (this is its own executable which you run in a command/terminal window, not within the mongo shell itself): 826 | 827 | mongodump --db learn --out backup 828 | 829 | To restore only the `unicorns` collection, we could then do: 830 | 831 | mongorestore --collection unicorns \ 832 | backup/learn/unicorns.bson 833 | 834 | It's worth pointing out that `mongoexport` and `mongoimport` are two other executables which can be used to export and import data from JSON or CSV. For example, we can get a JSON output by doing: 835 | 836 | mongoexport --db learn -collection unicorns 837 | 838 | And a CSV output by doing: 839 | 840 | mongoexport --db learn \ 841 | -collection unicorns \ 842 | --csv -fields name,weight,vampires 843 | 844 | Note that `mongoexport` and `mongoimport` cannot always represent your data. Only `mongodump` and `mongorestore` should ever be used for actual backups. 845 | 846 | ## In This Chapter ## 847 | In this chapter we looked at various commands, tools and performance details of using MongoDB. We haven't touched on everything, but we've looked at the most common ones. Indexing in MongoDB is similar to indexing with relational databases, as are many of the tools. However, with MongoDB, many of these are to the point and simple to use. 848 | 849 | # Conclusion # 850 | You should have enough information to start using MongoDB in a real project. There's more to MongoDB than what we've covered, but your next priority should be putting together what we've learned, and getting familiar with the driver you'll be using. The [MongoDB website](http://www.mongodb.com/) has a lot of useful information. The official [MongoDB user group](http://groups.google.com/group/mongodb-user) is a great place to ask questions. 851 | 852 | NoSQL was born not only out of necessity, but also out of an interest in trying new approaches. It is an acknowledgement that our field is ever-advancing and that if we don't try, and sometimes fail, we can never succeed. This, I think, is a good way to lead our professional lives. 853 | -------------------------------------------------------------------------------- /en/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamano/the-little-mongodb-book/c64fe604bb4222ef907c9c797277f92961674fe7/en/title.png -------------------------------------------------------------------------------- /en/title.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamano/the-little-mongodb-book/c64fe604bb4222ef907c9c797277f92961674fe7/en/title.psd -------------------------------------------------------------------------------- /en/title.txt: -------------------------------------------------------------------------------- 1 | % The Little MongoDB Book 2 | % Karl Seguin -------------------------------------------------------------------------------- /ja/Makefile: -------------------------------------------------------------------------------- 1 | PLATEX=uplatex 2 | PANDOC=pandoc 3 | DVIPDFMX=dvipdfmx 4 | DVIPDFMX_OPT=-f noto 5 | 6 | NAME=the-little-mongodb-book-ja 7 | SRC=mongodb.markdown 8 | TEX=$(NAME).tex 9 | PDF=$(NAME).pdf 10 | EPUB=$(NAME).epub 11 | HTML=$(NAME).html 12 | THUMB=$(NAME).png 13 | 14 | TEX_TEMPLATE=template.tex 15 | EPUB_TEMPLATE=template.html 16 | EPUB_CSS=test.css 17 | 18 | PANDOC_OPT=-f markdown+yaml_metadata_block --toc -N --chapters 19 | PANDOC_TEX_OPT=$(PANDOC_OPT) -t latex --template=$(TEX_TEMPLATE) 20 | PANDOC_EPUB_OPT=$(PANDOC_OPT) -t epub3 --template=$(EPUB_TEMPLATE) 21 | 22 | %.dvi: %.tex 23 | $(PLATEX) $< 24 | $(PLATEX) $< 25 | 26 | %.pdf: %.dvi 27 | $(DVIPDFMX) $(DVIPDFMX_OPT) $^ 28 | 29 | %.png: %.pdf 30 | pdftoppm -png -l 1 $^ > $@ 31 | 32 | all: $(PDF) $(EPUB) $(THUMB) 33 | 34 | clean: 35 | rm -rf *.log *.out *.aux *.toc $(TEX) $(DVI) $(PDF) $(EPUB) $(HTML) $(THUMB) 36 | 37 | $(EPUB): $(SRC) $(EPUB_TEMPLATE) 38 | $(PANDOC) $(PANDOC_EPUB_OPT) -o $@ $< 39 | 40 | $(HTML): $(SRC) 41 | $(PANDOC) -o $@ $< 42 | 43 | $(TEX): $(SRC) $(TEX_TEMPLATE) 44 | $(PANDOC) $(PANDOC_TEX_OPT) $< > $@ 45 | 46 | $(DVI): $(TEX) 47 | 48 | $(PDF): $(DVI) 49 | -------------------------------------------------------------------------------- /ja/hamacro.sty: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `hamacro.sty', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% hamacro.dtx (with options: `package') 8 | %% 9 | %% This is a generated file. 10 | %% 11 | %% Copyright (C) 2004-2015 HAMANO Tsukasa 12 | %% 13 | %% This file is released under the MIT License. 14 | %% 15 | \NeedsTeXFormat{LaTeX2e}[1999/12/01] 16 | \ProvidesPackage{hamacro}[2004/11/05 hamano] 17 | 18 | \usepackage[T1]{fontenc} 19 | \usepackage{newtxmath} 20 | \usepackage{tgtermes} 21 | \usepackage{tgheros} 22 | \renewcommand{\bfdefault}{bx} % 和文がBoldにならない問題回避、tgtermesのバグ? 23 | \usepackage{inconsolata} % only ttdefault 24 | \usepackage[deluxe]{otf} 25 | \usepackage[dvipdfmx]{graphicx} 26 | 27 | \usepackage[dvipdfmx]{color} 28 | \usepackage[dvipdfmx]{xcolor} 29 | \definecolor{dimgray}{HTML}{696969} 30 | \definecolor{lightgray}{HTML}{D3D3D3} 31 | \definecolor{gainsboro}{HTML}{DCDCDC} 32 | \definecolor{gray90}{gray}{0.90} % E5E5E5 33 | \definecolor{gray91}{gray}{0.91} % E8E8E8 34 | \definecolor{gray92}{gray}{0.92} % EBEBEB 35 | \definecolor{gray93}{gray}{0.93} % EDEDED 36 | \definecolor{gray94}{gray}{0.94} % F0F0F0 37 | \definecolor{gray95}{gray}{0.95} % F2F2F2 38 | \definecolor{floralwhite}{HTML}{FFFAF0} 39 | 40 | \definecolor{azure}{HTML}{F0FFFF} 41 | \definecolor{darkblue}{HTML}{00008B} 42 | \definecolor{midnightblue}{HTML}{191970} 43 | \definecolor{lightskyblue}{HTML}{87CEFA} 44 | \definecolor{olivegreen}{cmyk}{0.64,0,0.95,0.40} 45 | \definecolor{cadetblue}{cmyk}{0.62,0.57,0.23,0} 46 | \definecolor{brown}{cmyk}{0,0.81,1,0.60} 47 | 48 | \definecolor{lightgreen}{HTML}{90EE90} 49 | \definecolor{green}{HTML}{008000} 50 | \definecolor{darkgreen}{HTML}{006400} 51 | \definecolor{samuraiblue}{HTML}{1D327D} 52 | 53 | \usepackage[dvipdfm]{hyperref} 54 | \hypersetup{ 55 | setpagesize=false, 56 | bookmarksnumbered=true, 57 | colorlinks=true, 58 | linkcolor=linkcolor, 59 | citecolor=linkcolor, 60 | filecolor=linkcolor, 61 | urlcolor=linkcolor 62 | } 63 | 64 | \usepackage{pxjahyper} 65 | 66 | \usepackage{here} 67 | \usepackage{ctable} 68 | \usepackage{longtable} 69 | \usepackage{ulem} 70 | \usepackage{soul} 71 | 72 | \colorlet{sectionbg}{gray90} 73 | \colorlet{framerule}{dimgray} 74 | \colorlet{framebg}{gray90} 75 | \colorlet{linkcolor}{blue} 76 | 77 | \colorlet{accentcolor}{black} 78 | \colorlet{subcolor}{black} 79 | 80 | \DeclareOption{green}{ 81 | \colorlet{accentcolor}{green} 82 | \colorlet{subcolor}{darkgreen} 83 | } 84 | \DeclareOption{blue}{ 85 | \colorlet{accentcolor}{lightskyblue} 86 | \colorlet{subcolor}{midnightblue} 87 | } 88 | \DeclareOption{samuraiblue}{ 89 | \colorlet{accentcolor}{lightskyblue} 90 | \colorlet{subcolor}{samuraiblue} 91 | } 92 | 93 | \DeclareOption{nomargin}{ 94 | \setlength{\oddsidemargin}{-0.1\paperwidth} 95 | \setlength{\evensidemargin}{-0.1\paperwidth} 96 | \setlength{\marginparwidth}{0pt} 97 | \setlength{\marginparsep}{0pt} 98 | \setlength{\textwidth}{0.9\paperwidth} 99 | \setlength{\topmargin}{-1truein} 100 | \setlength{\textheight}{0.9\paperheight} 101 | } 102 | 103 | \ProcessOptions 104 | 105 | \renewcommand{\footnoterule}{ 106 | \kern -3pt 107 | \hrule width \columnwidth 108 | \kern 2.6pt 109 | } 110 | 111 | 112 | \message{^^J} 113 | \message{***** Layout Info *****^^J} 114 | \message{\hoffset: \the\hoffset^^J} 115 | \message{\voffset: \the\voffset^^J} 116 | \message{\paperwidth: \the\paperwidth^^J} 117 | \message{\paperheight: \the\paperheight^^J} 118 | \message{\textwidth: \the\textwidth^^J} 119 | \message{\textheight: \the\textheight^^J} 120 | \message{\oddsidemargin: \the\oddsidemargin^^J} 121 | \message{\evensidemargin: \the\evensidemargin^^J} 122 | \message{\marginparwidth: \the\marginparwidth^^J} 123 | \message{\marginparsep: \the\marginparsep^^J} 124 | \message{***********************^^J} 125 | 126 | \def\@maketitle{ 127 | \newpage 128 | \null 129 | \begin{center} 130 | {\LARGE\gtfamily\bfseries{\@title}\par} 131 | \end{center} 132 | \begin{flushright} 133 | {\large \@date \par} 134 | {\large \@author \par} 135 | \end{flushright} 136 | \vspace{0.5in} 137 | } 138 | 139 | \usepackage[Conny]{fncychap} 140 | \ChNameVar{\centering\Huge\sffamily\color{black}} 141 | \ChTitleAsIs 142 | \ChTitleVar{\centering\Huge\sffamily\color{black}} 143 | 144 | \renewcommand{\DOCH}{ 145 | \vskip -0.5truein 146 | \color{subcolor} 147 | \mghrulefill{3\RW}\par\nobreak 148 | \vskip -0.5\baselineskip 149 | \mghrulefill{\RW}\par\nobreak 150 | \CNV\FmN{\@chapapp}\space \CNoV\thechapter \postchaptername 151 | \par\nobreak 152 | \vskip -0.5\baselineskip 153 | } 154 | \renewcommand{\DOTI}[1]{ 155 | \color{subcolor} 156 | \vskip 0.5\baselineskip 157 | \CTV\FmTi{#1}\par\nobreak 158 | \vskip -0.4\baselineskip 159 | \color{subcolor} 160 | \mghrulefill{\RW}\par\nobreak 161 | \vskip 1\baselineskip 162 | } 163 | \renewcommand{\DOTIS}[1]{ 164 | \vskip -0.5truein 165 | \color{subcolor} 166 | \mghrulefill{\RW}\par\nobreak 167 | \CTV\FmTi{#1}\par\nobreak 168 | \vskip -0.4\baselineskip 169 | \color{subcolor} 170 | \mghrulefill{\RW}\par\nobreak 171 | %\vskip 1\baselineskip 172 | } 173 | 174 | \usepackage{etex} 175 | \usepackage[explicit,calcwidth]{titlesec} 176 | 177 | %% section formatting 178 | \newcommand\sectionformat[2]{ 179 | \tikz { 180 | \node [ 181 | draw=subcolor, 182 | line width=1.5pt, 183 | fill=sectionbg, 184 | align=left, 185 | rounded corners=6pt, 186 | text width=\columnwidth 187 | ] {\textcolor{subcolor}{#2}\;\textcolor{black}{#1}}; 188 | } 189 | } 190 | 191 | \titleformat{name=\section} 192 | {\sf\gtfamily\bfseries\LARGE}{}{0pt} 193 | {\sectionformat{#1}{\thesection}} 194 | \titleformat{name=\section,numberless} 195 | {\sf\gtfamily\bfseries\LARGE}{}{0pt} 196 | {\sectionformat{#1}{}} 197 | \titlespacing*{\section}{-1em}{3ex}{-5ex} 198 | 199 | \titleformat{name=\subsection}[block] 200 | {\sf\gtfamily\bfseries\Large} 201 | {\textcolor{subcolor}{\thesubsection}} 202 | {0.5em} 203 | {#1} 204 | [{\addtolength{\titlewidth}{0.2em} 205 | \titleline*[l]{\color{subcolor}\titlerule[1.5pt]}}] 206 | \titleformat{name=\subsection,numberless}[block] 207 | {\sf\gtfamily\bfseries\Large} 208 | {} 209 | {0.5em} 210 | {#1} 211 | [{\addtolength{\titlewidth}{0.2em} 212 | \titleline*[l]{\color{subcolor}\titlerule[1.5pt]}}] 213 | \titlespacing*{\subsection}{0em}{1ex}{2ex} 214 | 215 | \titleformat{\subsubsection}[block] 216 | {\sf\gtfamily\bfseries\large} 217 | {\color{subcolor}{\thesubsubsection}} 218 | {0.5em} 219 | {#1} 220 | \titleformat{name=\subsubsection,numberless}[block] 221 | {\sf\gtfamily\bfseries\large} 222 | {} 223 | {0.5em} 224 | {#1} 225 | 226 | %% paragraph formatting 227 | \setcounter{secnumdepth}{4} 228 | \titleformat{name=\paragraph}[block] 229 | {\sf\gtfamily\bfseries\normalsize}{\color{subcolor}{\theparagraph} \color{black}{#1}}{1em}{} 230 | \titleformat{name=\paragraph,numberless}[block] 231 | {\sf\gtfamily\bfseries\normalsize}{#1}{1em}{} 232 | 233 | \let\origemph\emph 234 | \newcommand{\redemph}[1] 235 | {\textcolor{red}{\underline{\origemph{\textcolor{black}{#1}}}}} 236 | 237 | \newcommand{\ulemph}[1] 238 | {\textbf{\Underline{#1}}} 239 | 240 | \newcommand{\bouemph}[1] 241 | {\origemph{\bou{#1}}} 242 | 243 | 244 | \let\origtableofcontents\tableofcontents 245 | \renewcommand{\tableofcontents}{\textsf\origtableofcontents} 246 | 247 | \usepackage[framemethod=TikZ]{mdframed} 248 | \mdfsetup{ 249 | middlelinewidth=2pt, 250 | middlelinecolor=framerule, 251 | backgroundcolor=framebg, 252 | roundcorner=3pt} 253 | 254 | \usepackage{anyfontsize} 255 | \newcommand{\bigquotemark}{ 256 | {\fontsize{3em}{3em}\selectfont \color{accentcolor} \textbf{``}} 257 | \vskip -2.3em 258 | \hskip 1.5zw 259 | } 260 | \renewenvironment{quote}{ 261 | \begin{mdframed} 262 | \parindent 0zw 263 | \vskip -0.5em 264 | {\bigquotemark} 265 | } 266 | { 267 | \smallskip 268 | \end{mdframed} 269 | } 270 | 271 | 272 | \renewenvironment{verbatim}{ 273 | \VerbatimEnvironment 274 | \begin{mdframed}[innerleftmargin=0.5ex] 275 | \tt\fontsize{11truept}{1.2em} 276 | \begin{Verbatim}} 277 | { 278 | \end{Verbatim} 279 | \end{mdframed} 280 | } 281 | 282 | \newenvironment{verbatim60}{ 283 | \VerbatimEnvironment 284 | \begin{mdframed}[innerleftmargin=0.8ex] 285 | \tt\fontsize{14.5truept}{1.5em} 286 | \begin{Verbatim}} 287 | { 288 | \end{Verbatim} 289 | \end{mdframed} 290 | } 291 | 292 | \newenvironment{verbatim40}{ 293 | \VerbatimEnvironment 294 | \begin{mdframed}[innerleftmargin=1ex] 295 | \tt\fontsize{22truept}{1.5em} 296 | \begin{Verbatim}} 297 | { 298 | \end{Verbatim} 299 | \end{mdframed} 300 | } 301 | 302 | \usepackage{fancyvrb} 303 | \newenvironment{Shaded} 304 | { 305 | \begin{mdframed}[innerleftmargin=1ex] 306 | \tt\fontsize{11truept}{1.2em} 307 | }{\end{mdframed}} 308 | \DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}} 309 | \newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}} 310 | \newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{{#1}}} 311 | \newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}} 312 | \newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}} 313 | \newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{{#1}}} 314 | \newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{{#1}}} 315 | \newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}} 316 | \newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}} 317 | \newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}} 318 | \newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{{#1}}} 319 | \newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{{#1}}} 320 | \newcommand{\ImportTok}[1]{{#1}} 321 | \newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{{#1}}}} 322 | \newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{{#1}}}} 323 | \newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}} 324 | \newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}} 325 | \newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{{#1}}} 326 | \newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{{#1}}} 327 | \newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{{#1}}} 328 | \newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{{#1}}}} 329 | \newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{{#1}}} 330 | \newcommand{\BuiltInTok}[1]{{#1}} 331 | \newcommand{\ExtensionTok}[1]{{#1}} 332 | \newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{{#1}}} 333 | \newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{{#1}}} 334 | \newcommand{\RegionMarkerTok}[1]{{#1}} 335 | \newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}} 336 | \newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{{#1}}}}} 337 | \newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}} 338 | \newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{{#1}}}} 339 | \newcommand{\NormalTok}[1]{{#1}} 340 | 341 | \providecommand{\tightlist}{ 342 | \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} 343 | 344 | \usepackage{upquote} 345 | \usepackage{listings} 346 | 347 | 348 | 349 | 350 | \AtBeginDocument{ 351 | \def\lstlistingname{リスト} 352 | } 353 | 354 | \mdfsetup{ 355 | linewidth=1truept, 356 | linecolor=gray, 357 | innerleftmargin=0.5zw, 358 | innerrightmargin=0.5zw, 359 | backgroundcolor=framebg 360 | } 361 | 362 | \BeforeBeginEnvironment{lstlisting}{ 363 | \begin{mdframed} 364 | } 365 | 366 | \AfterEndEnvironment{lstlisting}{ 367 | \end{mdframed} 368 | } 369 | 370 | \lstset{ 371 | frame=single, 372 | rulecolor=\color{framebg}, 373 | backgroundcolor=\color{framebg}, 374 | framesep=0pt, 375 | xleftmargin=-0.1ex, 376 | basicstyle=\fontsize{11truept}{1.2em}\tt, 377 | belowskip=-0.5em, 378 | identifierstyle=\color{black}, 379 | commentstyle=\color{brown}, 380 | keywordstyle=\color{olivegreen}, 381 | ndkeywordstyle=\color{red}, 382 | stringstyle=\color{purple}, 383 | showstringspaces=false, 384 | breaklines=true, 385 | columns=[l]{fullflexible} 386 | } 387 | 388 | \lstdefinestyle{command}{ 389 | language=command, 390 | commentstyle=\color{black} 391 | } 392 | 393 | \lstdefinelanguage{command} 394 | { 395 | keywords={\#, export, if, while, for}, 396 | morekeywords={\$}, 397 | otherkeywords={\#}, 398 | morestring=[b]', 399 | morestring=[b]\" 400 | } 401 | 402 | \lstdefinelanguage{JavaScript}{ 403 | keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break}, 404 | ndkeywords={class, export, boolean, throw, implements, import, this}, 405 | sensitive=false, 406 | comment=[l]{//}, 407 | morecomment=[s]{/*}{*/}, 408 | morestring=[b]', 409 | morestring=[b]\" 410 | } 411 | 412 | \lstdefinelanguage{httpd.conf} 413 | { 414 | morekeywords={VirtualHost, Include, Directory, LogFormat, CustomLog}, 415 | morecomment=[l]{\#} 416 | } 417 | 418 | \lstdefinelanguage{nginx.conf} 419 | { 420 | morekeywords={ 421 | server,server_name,listen,location, 422 | rewrite,proxy_pass, proxy_set_header}, 423 | morecomment=[l]{\#} 424 | } 425 | 426 | \lstdefinelanguage{slapd.conf} 427 | { 428 | morekeywords={ 429 | include,loglevel,timelimit,sizelimit, 430 | database,suffix,rootdn,rootpw,directory, 431 | overlay,index, 432 | }, 433 | morestring=[b]\", 434 | morecomment=[l]{\#} 435 | } 436 | 437 | \endinput 438 | %% 439 | %% End of file `hamacro.sty'. 440 | -------------------------------------------------------------------------------- /ja/mongodb.markdown: -------------------------------------------------------------------------------- 1 | --- 2 | title: MongoDBの薄い本 3 | subtitle: The Little MongoDB Book 4 | author: Karl Seguin 5 | translator: HAMANO Tsukasa 6 | authors: Karl Seguin 著 / 濱野 司 訳 7 | keywords: MongoDB 薄い本 チュートリアル 8 | version: 2.6.0 9 | stylesheet: stylesheet.css 10 | --- 11 | 12 | \frontmatter 13 | \tableofcontents 14 | 15 | # この本について # {-} 16 | 17 | ## ライセンス ## {-} 18 | MongoDBの薄い本はAttribution-NonCommercial 3.0 Unportedに基づいてライセンスされています。***あなたはこの本を読む為にお金を支払う必要はありません。*** 19 | 20 | この本を複製、改変、展示することは基本的に自由です。しかし、この本は常に私(カール・セガン)に帰属するように求めます。そして私はこれを商用目的で使用する事はありません。 21 | 以下にライセンスの全文があります: 22 | 23 | 24 | 25 | ## 著者について ## {-} 26 | カール・セガンは幅広い経験と技術を持った開発者です。 彼は.Netのエキスパートであると同時にRubyの開発者です。彼はOSSプロジェクトのセミアクティブな貢献者であり、テクニカルライターや時々講演を行っています。MongoDBに関して、彼はC#のMongoDBライブラリNoRMの主要な貢献者であり、インタラクティブ・チュートリアル[mongly](http://openmymind.net/mongly/)や[Mongo Web Admin](https://github.com/karlseguin/Mongo-Web-Admin)を書きました。彼のカジュアルなゲーム開発者の為のサービス、[mogade.com](http://mogade.com/)はMongoDBで稼動しています。 27 | 28 | カールは過去に[Redisの薄い本](http://openmymind.net/2012/1/23/The-Little-Redis-Book/)も書いています。 29 | 30 | 彼のブログは 、つぶやきは[\@karlseguin](http://twitter.com/karlseguin)で見つかります。 31 | 32 | ## 謝辞 ## {-} 33 | [Perry Neal](http://twitter.com/perryneal)が私に彼の目と意見と情熱を貸してくれたことに感謝します。ありがとう。 34 | 35 | ## 最新バージョン ## {-} 36 | この本はMongoDB 2.6に対応するようAsya Kamskyによって更新されました。 37 | 38 | 原書の最新のソースはこちら: 39 | 40 | 41 | 42 | ## 訳者より ## {-} 43 | 内容の誤りや誤訳などありましたら[\@hamano](http://twitter.com/hamano)まで連絡下さい。翻訳を手伝ってくれた[\@tamura__246](http://twitter.com/tamura__246)さんと誤字、誤訳を指摘下さった[matsubo](https://github.com/matsubo)さん、[honda0510](https://github.com/honda0510)さん、[\@ponta_](https://twitter.com/ponta_)さん、[ttaka](https://github.com/ttaka)さんに感謝します。 44 | 45 | 翻訳版のソース: 46 | 47 | 48 | \mainmatter 49 | 50 | # 序章 # {-} 51 | > この章が短いことは私の誤りではありません、MongoDBを学ぶ事はとても簡単です。 52 | 53 | 技術は激しい速度で進歩しているとよく言われます。それは新しい技術と技術手法が公開され続けているという点で真実ですが、私の見解ではプログラマによって利用される基礎的な技術の進歩は非常に遅いと考えています。何年もかけて習得した技術には少なからず基礎技術と関連があります。注目すべき点は確立した技術が置きかえられる速度です。長い歴史を持つ技術に対する開発者の関心はあっという間に移り変わる恐れがあります。 54 | 55 | 既に確立されているリレーショナルデータベースに対して発展してきたNoSQLはこの様な急転換の典型的な事例です。いつの日か、RDBMSで運用されるWebは少なくなり、NoSQLが実用に足るソリューションとして確立されるかもしれません。 56 | 57 | たとえこれらの技術が一晩で移り変わったとしても、実際的な実務でこれらが受け入れられるには何年もかかります。初期の段階では比較的小規模な会社や開発者の情熱によって突き動かされます。新しい技術は彼らのような人々の挑戦によってゆっくりと普及しソリューションや教育環境が洗練されていきます。念の為言っておくと、NoSQLは昔ながらのストレージソリューションを置き換える手段ではないという事について大部分は事実です。しかしある特定の分野では従来のものに優る価値を提供します。 58 | 59 | 何よりもまず初めに、NoSQLが何を意味しているのかを説明すべきでしょう。それは人によって異なる意味を持つ広義の用語です。私は個人的にそれをデータストレージの役割を果たすシステムという意味で使用しています。言い換えれば、(私にとっての)NoSQLはあなたが執着する単一のシステムに必ずしも責任を持つようなものではありません。歴史的に見て、リレーショナルデータベースベンダーはどんな規模にも適応する万能なソリューションとしてソフトウェアを位置づけてきました。NoSQLスタックをMySQLといったリレーショナルデータベースの様に利用する事も出来るでしょうが、NoSQLは与えられた仕事を達成する為の最適なツールとしてより小さい単位の役割に向かう傾向があります。そして、Redisもまた参照性能というシステムの特定部位に執着し、同じようにHadoopもデータ処理に集中的です。簡単に言うと、NoSQLはオープンであり、既存のものの代替や付加的なパターンを意識し、データを管理する為のツールです。 60 | 61 | あなたはMongoDBが多くの状況に適応する事に驚くかもしれません。MongoDBはドキュメント指向データベースとしてよりNoSQLソリューションに汎用化されています。それはリレーショナルデータベースの代替の様に見られるでしょうが、リレーショナルデータベースと比較すると、もっと専門化したNoSQLソリューションにおいて恩恵を得ることが出来ます。MongoDBには利点と欠点がありますのでそれには後ほどこの本の中で触れます。 62 | 63 | # はじめよう # {-} 64 | この本の大部分はMongoDBの機能面に注目します。その為に私たちはMongoDBシェルを利用します。MongoDBドライバを利用するようになるまで、MongoDBシェルは学習に役立つだけでなく、便利な管理ツールとなるでしょう。 65 | 66 | MongoDBについてまず最初に知るべきことを取り上げます: それはドライバです。MongoDBは各種プログラミング言語向けに[数多くの公式ドライバ](http://docs.mongodb.org/ecosystem/drivers/)が用意されています。これらのドライバは恐らくあなたがすでに慣れ親しんでいる各種データベースのドライバと似たようなものだと考えて良いでしょう。これらのドライバに加えて、開発コミュニティでは更にプログラミング言語/フレームワーク用のライブラリが開発されています。例えば、[NoRM](https://github.com/atheken/NoRM)はLINQを実装したC#のライブラリで、[MongoMapper](https://github.com/jnunemaker/mongomapper)はActiveRecordと親和性の高いRubyライブラリです。プログラムから直接MongoDBのコアドライバを利用するか、他の高級なライブラリを選択するかはあなた次第です。何故、公式ドライバとコミュニティライブラリの両方が存在するのかについてMongoDBに不慣れな多くの人に混乱があるようなので説明しておきます。前者はMongoDBの中核的な通信と接続性に、後者はプログラミング言語や特定のフレームワークの実装により特化しています。 67 | 68 | これを読みながらあなたは私の実習を実際に反復し、自分自身で疑問を探っていく事を推奨します。MongoDBの準備と実行は簡単です、今から数分の時間をかけてセットアップしてみましょう。 69 | 70 | 1. [公式ダウンロードページ](http://www.mongodb.org/downloads)へ進み、一番上の行からOSを選択してバイナリを手に入れましょう(安定バージョンを推奨)。開発目的であれば32ビット64ビットのどちらを選んでも構いません。 71 | 72 | 2. アーカイブを適当な場所に展開し、`bin`サブフォルダへ移動します。まだ何も実行しないこと、`mongod`がサーバープロセスであり、`mongo`がクライアントシェルであることは知っておいて下さい。その2つはこれから私たちが最も時間を費やす実行ファイルです。 73 | 74 | 3. `bin`サブフォルダの中に`mongodb.config`という名前で新しいテキストファイルを作成します。 75 | 76 | 4. `mongodb.config`に以下の1行を追記します: 77 | 78 | dbpath=データーベースファイルを格納する場所 79 | 80 | 例えば、Windowsでは`bpath=c:\mongodb\data`を指定し、Linuxでは`dbpath=/var/lib/mongodb/data`と指定します。 81 | 82 | 5. 指定した`dbpath`を作成する。 83 | 84 | 6. mongodを`--config /path/to/your/mongodb.config`パラメーターを付けて起動します。 85 | 86 | Windowsユーザーの為の例を示すと、もしダウンロードファイルを`c:\mongodb\`に展開したのなら`c:\mongodb\bin\mongodb.config`に`dbpath=c:\mongodb\data\`を指定すると、`c:\mongodb\data\`が作成されます。次に、コマンドプロンプトから `c:\mongodb\bin\mongod --config c:\mongodb\bin\mongodb.config`を実行して`mongod`を起動します。 87 | 88 | 無駄を少なくする為に、ご自由に`bin`フォルダをパスに追加して下さい。MacOSXとLinuxユーザーはほとんど同じやり方に従うことが出来ます。あなたがすべきことはパスを変更することくらいです。 89 | 90 | うまくいけば今あなたはMongoDBを実行しているでしょう。もしエラーが起こったのならメッセージを注意深く読んで下さい。サーバーは何がおかしいのかを丁寧に説明してくれます。 91 | 92 | あなたは`mongo`(**d**は付かない)を起動し、実行中のサーバーのシェルに接続する事が出来ます。全てが動作しているか確認するために`db.version()`と入力してみて下さい、うまくいっていればインストールされているバージョンを確認することが出来るでしょう。 93 | 94 | # 基礎 95 | MongoDBの動作の基本的な仕組みを知ることからはじめましょう。当然これはMongoDBの核心を理解することです。しかしこれはMongoDBについての高レベルな質問の答えを見つけ出す為にも役立ちます。 96 | 97 | 最初に、私たちは6つの概念を理解する必要があります。 98 | 99 | 1. MongoDBはあなたが既に慣れ親しんでいる**データベース**と同じ概念を持っています(あるいはOracleでいうところのスキーマ)。MongoDBインスタンスの中には0個以上のデータベースを持つことが出来、それぞれは高レベルコンテナの様に作用します。 100 | 101 | 2. データベースは0個以上の**コレクション**を持つことが出来ます。コレクションは従来の**テーブル**とほぼ共通しているので、この2つが同じものだと思っても支障は無いでしょう。 102 | 103 | 3. コレクションは0個以上の**ドキュメント**を作成できます。先ほどと同様にドキュメントを**行**と思って構いません。 104 | 105 | 4. ドキュメントは1つ以上の**フィールド**を作成できます。あなたは恐らくこれを**列**に似ていると推測できるでしょう。 106 | 107 | 5. MongoDBでの**インデックス**機能はRDBMSのものとよく似ています。 108 | 109 | 6. **カーソル**はこれまでの5つの概念とは異なりとても重要で見落とされがちですので詳しく説明する必要があると思います。カーソルについて理解すべき重要な点は、MongoDBにデータを問い合わせるとカーソルと呼ばれる結果データへのポインタを返します。カーソルは実際のデータを取り出す前に、カウントやスキップの様な操作はを行うことが出来ます。 110 | 111 | 要点をまとめると、MongoDBは**データベース**を作成し、その中には**コレクション**を含みます。**コレクション**は**ドキュメント**を作成します。それぞれの**ドキュメント**は**フィールド**を作成します。**コレクション**は**インデックス化**可能であり、これは参照やソートの性能を改善します。最後に、MongoDBからデータを取得する際、**カーソル**を経由して操作を行います。これは実際に実行する際に必要不可欠なものです。 112 | 113 | なぜ新しい専門用語を利用するのでしょうか(コレクションとテーブル、ドキュメントと行、フィールドと列)。物事を複雑にするだけでしょうか?これらの概念がリレーショナルデータベースの機能と良く似ているという点ではその通りですが、これらは全く同じではありません。主要な違いは、リレーショナルデータベースが**テーブル**のレベルに**列**を定義しているのに対し、ドキュメント指向データベースは**ドキュメント**のレベルに**フィールド**を定義している事です。**コレクション**の中の**ドキュメント**はそれ自身の独自の**フィールド**を持つことが出来ると言えます。という訳で、**コレクション**は**テーブル**に比べ、より使い易いコンテナとなり、さらに**ドキュメント**は**行**に比べてより多くの情報を持つようになります。 114 | 115 | これを理解することは重要ですが、もしこれをまだ完全に理解出来ていなくても心配することはありません。この本当の意味を確かめる為にはまだもう少し説明が必要でしょう。突き詰めていくとコレクションの中に何が入るかが厳密ではない事が要点です(スキーマレスの事)。フィールドは特定のドキュメントに対して追従します。あとの章で利点と欠点に気がつくでしょう。 116 | 117 | さあハンズオンをはじめましょう。まだ動かしていないのなら、どうぞ`mongod`サーバーとmongoシェルを起動して下さい。シェルはJavaScriptを実行できます。`help`や`exit`の様な、幾つかのグローバルコマンドを実行することが出来ます。例えば、`db.help()`や`db.stats()`といったコマンドは、現在のデータベース`db`オブジェクトに対して実行します。`db.unicorns.help()`や`db.unicorns.count()`といったコマンドは、`db.COLLECTION_NAME`オブジェクトの様に、指定したそれぞれのコレクションに対して実行します。 118 | 119 | どうぞ、`db.help()`と入力してみて下さい。`db`オブジェクトに対して実行可能なコマンド一覧を得ることが出来るでしょう。 120 | 121 | 余談ですが、あなたが丸カッコ`()`を含めずにメソッドを実行した場合、メソッドの実行ではなくメソッドの本体が表示されます。これはJavaScriptシェルだからです。あなたが最初にこれをやった時、`function (...){`という応答が返ってきても驚かないように、この事に触れておきました。たとえば、丸カッコ無しで`db.help`と入力すると`help`メソッドの内部実装を見ることが出来ます。 122 | 123 | まず私たちはグローバルな`use`ヘルパー使ってデータベースを切り替えます。どうぞ`use learn`と入力してみて下さい。そのデータベースが実際に存在していなくても構いません。最初のコレクションを`learn`に作成しましょう。今あなたはデータベースの中にいて、`db.getCollectionNames()`という様なデータベースコマンドを発行できます。これを実行すると、恐らく空の配列(`[ ]`)が返ってくるでしょう。コレクションはスキーマレスですので、それらを明確に作成する必要はありません。私たちは、単純にドキュメントをコレクションに作成する事が出来ます。それでは、`insert`コマンドを使ってコレクションにドキュメント挿入してみましょう。 124 | 125 | ~~~ {.javascript} 126 | db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450}) 127 | ~~~ 128 | 129 | 上記のコマンドは一つの引数を受け取り`unicorns`コレクションに対して`insert`を行います。 130 | MongoDBの内部ではBSONというバイナリでシリアライズされたJSONフォーマットを利用します。 131 | 外側から見るとJSONをいろいろ使っているように見えます。 132 | ここで`db.getCollectionNames()`を実行すると`unicorns`コレクションが表示されます。 133 | 134 | これで、`unicorns`に対し`find`コマンドを使用してドキュメントのリストを取得出来るようになりました。 135 | 136 | ~~~ {.javascript} 137 | db.unicorns.find() 138 | ~~~ 139 | 140 | あなたが挿入したデータに`_id`フィールドが追加されていることに注目して下さい。全てのドキュメントはユニークな`_id`フィールドを持たなくてはいけません。これは`ObjectID`型の値を自分で生成するかMongoDBに生成させる事になります。ほとんどの場合MongoDBに生成させたいと思うでしょう。`_id`フィールドには既定でインデックスが貼られます。これは`getIndexes`コマンドで確認できます。 141 | 142 | ~~~ {.javascript} 143 | db.unicorns.getIndexes() 144 | ~~~ 145 | 146 | あなたはインデックスを含むフィールドに対して作成されたデータベースやコレクションのインデックス名を確認することが出来ます。 147 | 148 | スキーマレスコレクションの話に戻りましょう。`unicorns`コレクションに以下の様な完全に異なるドキュメントを入れてみます: 149 | 150 | ~~~ {.javascript} 151 | db.unicorns.insert({name: 'Leto', gender: 'm', home: 'Arrakeen', worm: false}) 152 | ~~~ 153 | 154 | 再度`find`を利用してドキュメントを表示してみて下さい。MongoDBの興味深い振る舞いについて前に少しだけ話しました、何故従来の技術がうまく適応しなかったのかが解り始めて来たのではないでしょうか。 155 | 156 | ## セレクターの習得 ## 157 | 次の話題に進む前に、先程説明した6つの概念に加え、MongoDBの実用面でしっかりと理解すべきことがあります。それはクエリーセレクターです。MongoDBのクエリーセレクターはSQL構文の`where`節によく似ています。そういうわけで、これはドキュメントを見つけ出したり、数えたり、更新したり、削除したりする際に使用します。セレクターはJSONオブジェクトです。最も単純な`{}`は全てのドキュメントにマッチします(`null`も同じです)。もしメスのユニコーンを見つけたい場合、`{gender:'f'}`と指定します。 158 | 159 | セレクターについて掘り下げていく前に、演習の為のデータを幾つかセットアップしましょう。まず、これまでに`unicorns`コレクションに入れたドキュメントを`db.unicorns.remove({})`を実行して削除します。さて、以下を実行して演習に必要なデータを挿入しましょう(コピペ推奨): 160 | 161 | ~~~ {.javascript} 162 | db.unicorns.insert({name: 'Horny', dob: new Date(1992,2,13,7,47), 163 | loves: ['carrot','papaya'], weight: 600, 164 | gender: 'm', vampires: 63}); 165 | db.unicorns.insert({name: 'Aurora', dob: new Date(1991, 0, 24, 13, 0), 166 | loves: ['carrot', 'grape'], weight: 450, 167 | gender: 'f', vampires: 43}); 168 | db.unicorns.insert({name: 'Unicrom', dob: new Date(1973, 1, 9, 22, 10), 169 | loves: ['energon', 'redbull'], weight: 984, 170 | gender: 'm', vampires: 182}); 171 | db.unicorns.insert({name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), 172 | loves: ['apple'], weight: 575, 173 | gender: 'm', vampires: 99}); 174 | db.unicorns.insert({name: 'Solnara', dob: new Date(1985, 6, 4, 2, 1), 175 | loves:['apple', 'carrot', 'chocolate'], weight:550, 176 | gender:'f', vampires:80}); 177 | db.unicorns.insert({name:'Ayna', dob: new Date(1998, 2, 7, 8, 30), 178 | loves: ['strawberry', 'lemon'], weight: 733, 179 | gender: 'f', vampires: 40}); 180 | db.unicorns.insert({name:'Kenny', dob: new Date(1997, 6, 1, 10, 42), 181 | loves: ['grape', 'lemon'], weight: 690, 182 | gender: 'm', vampires: 39}); 183 | db.unicorns.insert({name: 'Raleigh', dob: new Date(2005, 4, 3, 0, 57), 184 | loves: ['apple', 'sugar'], weight: 421, 185 | gender: 'm', vampires: 2}); 186 | db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), 187 | loves: ['apple', 'watermelon'], weight: 601, 188 | gender: 'f', vampires: 33}); 189 | db.unicorns.insert({name: 'Pilot', dob: new Date(1997, 2, 1, 5, 3), 190 | loves: ['apple', 'watermelon'], weight: 650, 191 | gender: 'm', vampires: 54}); 192 | db.unicorns.insert({name: 'Nimue', dob: new Date(1999, 11, 20, 16, 15), 193 | loves: ['grape', 'carrot'], weight: 540, 194 | gender: 'f'}); 195 | db.unicorns.insert({name: 'Dunx', dob: new Date(1976, 6, 18, 18, 18), 196 | loves: ['grape', 'watermelon'], weight: 704, 197 | gender: 'm', vampires: 165}); 198 | ~~~ 199 | 200 | さて、データが入りましたのでセレクターを習得しましょう。`{field: value}`は`field`というフィールドが`value`と等しいドキュメントを検索します。`{field1: value1, field2: value2}`は`and`式で検索します。`$lt`、 `$lte`、 `$gt`、 `$gte`、 `$ne`はそれぞれ、未満、以下、より大きい、以上、非等価、を意味する特別な演算子です。例えば、性別がオスで体重が700ポンドより大きいユニコーンを探すにはこのようにします: 201 | 202 | ~~~ {.javascript} 203 | db.unicorns.find({gender: 'm', weight: {$gt: 700}}) 204 | // このセレクターは以下と同等です。 205 | db.unicorns.find({gender: {$ne: 'f'}, weight: {$gte: 701}}) 206 | ~~~ 207 | 208 | `$exists`演算子はフィールドの存在や欠如のマッチに利用します。例えば: 209 | 210 | ~~~ {.javascript} 211 | db.unicorns.find({vampires: {$exists: false}}) 212 | ~~~ 213 | 214 | ひとつのドキュメントが返ってくるはずです。`$in`演算子は配列で渡した値のいずれかにマッチします。例えば: 215 | 216 | ~~~ {.javascript} 217 | db.unicorns.find({loves: {$in:['apple','orange']}}) 218 | ~~~ 219 | 220 | これで`apple`または`orange`が好きなユニコーンが返ります。 221 | 222 | 異なるフィールドに対してANDなどではなくORを利用したい場合、`$or`演算子を利用して、ORをとりたいセレクターの配列を指定します。 223 | 224 | ~~~ {.javascript} 225 | db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, 226 | {weight: {$lt: 500}}]}) 227 | ~~~ 228 | 229 | 上記は全てのメスのユニコーンの中からりんごが好き、もしくは体重が500ポンド未満の条件で検索します。 230 | 231 | 最後2つはとてもイカした用例です。お気づきと思いますが`loves`フィールドは配列です。MongoDBはファーストクラスオブジェクトとしての配列をサポートしています。これはとんでもなく便利な機能です。一度これを使ってしまうと、これ無しでは生活できなくなる恐れがあります。何よりも興味深いのは配列の値に基づいて簡単に選択できることです。`{loves: 'watermelon'}` は`loves`の値に`watermelon`を持つドキュメントを返します。 232 | 233 | これまでに見てきた他に、演算子はまだまだあります。最も柔軟な`$where`は指定したJavaScriptをサーバー上で実行します。MongoDBのドキュメントの[Query Selectors](http://docs.mongodb.org/manual/reference/operator/query/#query-selectors)の項に全てが記載されています。これまでに紹介してきたものはあなたが使い始めるのに必要な基本です。もっと使いこなせるようになるには多くの時間がかかるでしょう。 234 | 235 | これまでセレクターは`find`コマンドで利用できることを見てきました。これらは以前ちょっとだけ利用した`remove`コマンドやまだ使っていない`count`コマンド、後で出てくる`update`コマンドでも利用できます。 236 | 237 | `ObjectId`はMongoDBが生成した`_id`フィールドを選択するために利用します: 238 | 239 | ~~~ {.javascript} 240 | db.unicorns.find({_id: ObjectId("TheObjectId")}) 241 | ~~~ 242 | 243 | ## 章のまとめ ## 244 | 私たちはまだ`update`コマンドや、`find`で出来る幾つかの手の込んだ操作については学んでいませんが、`insert`コマンドと`remove`コマンドについて簡単に学びました(これまで見てきた以上のものはそれほど多くありません)。 245 | 私たちは`find`の紹介とMongoDBの`selectors`というものも見てきました。私たちは順調なスタートと、来たるべき時の為の盤石な基礎を築く事が出来ました。信じようと信じまいと、実際にあなたはMongoDBを使い始めるために知るべき殆どの事を知っています(素早く学習し、簡単に使えるようになるという意味でです)。 246 | 次に移る前に、演習のデータをローカルコピーすることを強く推奨します。恐らく、新しいコレクションで違うドキュメントを挿入したり、セレクターと似たようなものを使います。`find`や`count`や`remove`も使います。いろいろ試しているうちに、なにかマズイ事が起こった場合に最初の状態に戻れるようにしておいた方が良いでしょう。 247 | 248 | 249 | # 更新 250 | 1章ではCRUD(作成、読み込み、更新、削除)の4つのうちの3つの操作を紹介しました。この章では、省略していた`update`に専念します。その理由は、`update`には幾つかの意外な振る舞いがあるからです。 251 | 252 | ## 更新: 置換 と $set ## 253 | 最も単純な形式では`update`に2つの引数を渡します。セレクター(where条件)と更新を適用するフィールドです。もしRoooooodlesの体重を少し増やしたい場合は以下を実行します: 254 | 255 | ~~~ {.javascript} 256 | db.unicorns.update({name: 'Roooooodles'}, {weight: 590}) 257 | ~~~ 258 | 259 | (もし前回の演習で作成した`unicorns`コレクションを残していない場合、1章に戻って、全てのドキュメントを`remove`し、挿入し直して下さい。) 260 | 261 | アップデートされたレコードを確認する場合、以下のようにします: 262 | 263 | ~~~ {.javascript} 264 | db.unicorns.find({name: 'Roooooodles'}) 265 | ~~~ 266 | 267 | あなたは`update`の動作に驚くはずです。 268 | 2番目のパラメータに更新演算子を指定しなかったので元のデータを**置き換える**動作となり、元のドキュメントは見つかりません。 269 | 言い換えると、`update`はドキュメントを`name`で検索し、ドキュメント全体を2番目のパラメータで置き換えます。 270 | SQLの`update`文にこれと同等の機能はありません。 271 | 本当に動的な更新を行いたい場合などの特定の状況で、これは理想的な動作です。 272 | しかし、ひとつか複数のフィールドの値を変更したい場合はMongoDBの`$set`演算子を利用すべきです。 273 | 以下のように実行して消えたフィールドをもとに戻します: 274 | 275 | ~~~ {.javascript} 276 | db.unicorns.update({weight: 590}, {$set: {name: 'Roooooodles', 277 | dob: new Date(1979, 7, 18, 18, 44), 278 | loves: ['apple'], 279 | gender: 'm', 280 | vampires: 99}}) 281 | ~~~ 282 | 283 | このように`weight`を指定しなければ上書きできません。以下を実行します: 284 | 285 | ~~~ {.javascript} 286 | db.unicorns.find({name: 'Roooooodles'}) 287 | ~~~ 288 | 289 | 期待する結果が得られました。従って、最初に行いたかった体重を変更する正しい方法は以下の通りです: 290 | 291 | ~~~ {.javascript} 292 | db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}}) 293 | ~~~ 294 | 295 | ## 更新演算子 ## 296 | `$set`に加え、その他の演算子を利用するともっと粋なことが出来ます。これらの更新演算子は、フィールドに対して作用します。なのでドキュメント全体が消えてしまうことはありません。例えば、`$inc`演算子はフィールドの値を増やしたり、負の値で減らす事が出来ます。もしPilotが`vampire`を倒した数が間違っていて2つ多かった場合、以下のようにして間違いを修正します: 297 | 298 | ~~~ {.javascript} 299 | db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}}) 300 | ~~~ 301 | 302 | もしAuroraが突然甘党になったら、`$push`演算子を使って、`loves`フィールドに値を追加することが出来ます: 303 | 304 | ~~~ {.javascript} 305 | db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}}) 306 | ~~~ 307 | 308 | その他の有効な更新演算子はMongoDBマニュアルの[更新演算子]((http://docs.mongodb.org/manual/reference/operator/update/#update-operators)に情報があります。 309 | 310 | ## Upsert ## 311 | `update`の使い方にはもっと驚く愉快なものがあります。その一つは`upsert`を完全にサポートしている事です。`upsert`はドキュメントが見つかった場合に更新を行い、無ければ挿入を行います。`upsert`は可読性が良く、よくあるシチュエーションで重宝します。`update`の3番目の引数に`{upsert:true}`を渡す事で`upsert`を利用できます。 312 | 313 | 一般的な例はWebサイトのカウンターです。複数のページのカウンターをリアルタイムに動作させたい場合、ページのレコードが既に存在しているか確認し、更新を行うか挿入を行うか決めなければなりません。`upsert`パラメータを省略(もしくはfalseに設定)して実行すると、以下のようにうまくいきません: 314 | 315 | ~~~ {.javascript} 316 | db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}); 317 | db.hits.find(); 318 | ~~~ 319 | 320 | しかし、upsertオプション加えると違った結果になります。 321 | 322 | ~~~ {.javascript} 323 | db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert:true}); 324 | db.hits.find(); 325 | ~~~ 326 | 327 | `page`というフィールドの値が`unicorns`のドキュメントが存在していなければ、新しいドキュメントが挿入されます。2回目を実行すると既存のドキュメントが更新され、`hits`は2に増えます。 328 | 329 | ~~~ {.javascript} 330 | db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert:true}); 331 | db.hits.find(); 332 | ~~~ 333 | 334 | ## 複数同時更新 ## 335 | 最後の驚きは、`update`はデフォルトで一つのドキュメントに対してのみ更新を行う事です。これまでの様に、まず例を見ていきましょう。以下のように実行します: 336 | 337 | ~~~ {.javascript} 338 | db.unicorns.update({}, {$set: {vaccinated: true }}); 339 | db.unicorns.find({vaccinated: true}); 340 | ~~~ 341 | 342 | あなたは全てのかわいいユニコーン達に予防接種を受けさせた(vaccinated)としましょう。これを行うには、`multi`オプションをtrueに設定します: 343 | 344 | ~~~ {.javascript} 345 | db.unicorns.update({}, {$set: {vaccinated: true }}, {multi:true}); 346 | db.unicorns.find({vaccinated: true}); 347 | ~~~ 348 | 349 | ## 章のまとめ ## 350 | この章でコレクションに対して行う基本的なCRUD操作を紹介し終えました。私たちは`update`の詳細を確認し、3つの興味深い振る舞いを観察しました。まず、MongoDBの`update`は更新演算子を使わないと既存のドキュメントを置き換えます。通常は`$set`などの様々な演算子を利用してドキュメントを変更します。次に、`update`は直感的な`upsert`オプションをサポートしています。これはドキュメントが存在するかどうか分からない時に便利です。最後に、デフォルトで`update`は最初にマッチしたドキュメントしか更新しません。マッチしたすべてのドキュメントを更新したい場合は`multi`オプションを指定してください。 351 | 352 | 私たちはシェルの視点からMongoDBを見てきた事を思い出して下さい。ドライバやライブラリの場合でも、デフォルトの振る舞いを切り替えて使用したり、異なるAPIに触れる事になります。 353 | 354 | 例えば、Rubyのドライバでは最後の2つのパラメータを一つのハッシュにまとめています: 355 | 356 | ~~~ {.javascript} 357 | {:upsert => false, :multi => false} 358 | ~~~ 359 | 360 | 同様に、PHPのドライバも最後の2つのパラメータを配列にまとめています。 361 | 362 | ~~~ {.javascript} 363 | array('upsert' => false, 'multiple' => false) 364 | ~~~ 365 | 366 | # 検索の習得 367 | 1章では、`find`コマンドについて簡単に説明しました。ここでは`find`やセレクターについての理解を深めていきます。`find`が**カーソル**を返却することについては既に述べましたので、もっと正確な意味を見ていきましょう。 368 | 369 | ## フィールド選択 ## 370 | **カーソル**について学ぶ前に、`find`に任意で設定出来る"projection"という2番目のパラメータについて知る必要があります。このパラメーターは取得、もしくは除外したいフィールドのリストです。例えば、以下の様に実行して、全てのユニコーンの名前をその他のフィールドを除外して取得します。 371 | 372 | ~~~ {.javascript} 373 | db.unicorns.find({}, {name: 1}); 374 | ~~~ 375 | 376 | デフォルトで、`_id`フィールドは常に返却されます。明示的に`{name:1, _id: 0}`を指定する事でそれを除外する事が出来ます。 377 | 378 | `_id`に関する余談ですが、包含条件と排他条件を混ぜることが出来るのかどうか、気になるかもしれません。あなたはフィールドを含めるか除くかのどちらかを選択することが出来ます。 379 | 380 | ## 順序 ## 381 | これまでに何度か`find`が必要な時に遅延して実行されるカーソルを返すと説明しました。しかしシェルは`find`を即座に処理を実行しているように見えるます。これはシェルだけの動作です。カーソルの本当の動作は`find`にメソッドをひとつ連結することで観測出来ます。まずはソートを見てみましょう。JSONドキュメントのフィールドを昇順でソートを行いたい場合はフィールドと1を指定し、降順で行いたい場合は-1を指定します。例えば: 382 | 383 | ~~~ {.javascript} 384 | //重いユニコーンの順 385 | db.unicorns.find().sort({weight: -1}) 386 | 387 | //ユニコーンの名前とvampiresの多い順: 388 | db.unicorns.find().sort({name: 1, vampires: -1}) 389 | ~~~ 390 | 391 | リレーショナルデータベースと同様に、MongoDBもソートの為にインデックスを利用出来ます。インデックスの詳細は後で詳しく見ていきますが、MongoDBにはインデックスを使用しない場合にソートのサイズ制限があることを知っておく必要があります。すなわち、もし巨大な結果に対してソートを行おうとするとエラーが返ってきます。実際の話、その他のデータベースにも、最適化されていないクエリーを拒否する機能があった方が良いと考えています。(私はこの動作をMongoDBの欠点とは考えていませんし、データベースの最適化が下手な人達にこの機能を使って欲しいと強く願っています。) 392 | 393 | ## ページング ## 394 | ページングはcursorの`limit`メソッドや`skip`メソッドを利用して実現できます。2番目と3番目に重いユニコーンを得るにはこうやります: 395 | 396 | ~~~ {.javascript} 397 | db.unicorns.find().sort({weight: -1}).limit(2).skip(1) 398 | ~~~ 399 | 400 | `limit`と`sort`を組み合わせると、インデックス化されていないフィールドでソートする場合の問題を避けることができます。 401 | 402 | ## カウント ## 403 | シェルではcollectionに対して直接`count`を呼び出す事が出来ます。例えば: 404 | 405 | ~~~ {.javascript} 406 | db.unicorns.count({vampires: {$gt: 50}}) 407 | ~~~ 408 | 409 | 実際には`count`は`cursor`のメソッドであり、シェルは単純なショートカットを提供しているだけです。この様なショートカットを提供しないドライバでは以下の様に実行する必要があります(これはシェルでも動きます): 410 | 411 | ~~~ {.javascript} 412 | db.unicorns.find({vampires: {$gt: 50}}).count() 413 | ~~~ 414 | 415 | ## 章のまとめ ## 416 | `find`や`cursor`の率直な使われ方を見てきました。これらの他に、あとの章で触れるコマンドや特別な状況で使われるコマンドが存在しますが、あなたは既にMongoDBの基礎を理解し、mongoシェルを安心して触れるようになったでしょう。 417 | 418 | # データモデリング 419 | さて、MongoDBのもっと抽象的な話題に移っていきましょう。幾つかの新しい用語や、些細な機能の新しい文法について説明していきます。新しいパラダイムであるモデリングについての話題は簡単ではありません。モデリングに関する新しい技術について、大抵の人々はまだ何が役に立ち、役に立たないのかをよく知りません。まずは講話から始めますが、最終的には実際のコードで学び、実践を行っていきます。 420 | 421 | モデリングに関して言うと、NoSQLデータベースの中でドキュメント指向データベースはリレーショナルデータベースと多くの部分で共通しています。しかし重要な違いがあります。 422 | 423 | ## Joinがありません ## 424 | まず最初に、最も根本的な違いであるMongoDBにJoinが存在しない事に対して安心する必要があるでしょう。MongoDBが何故joinの文法をサポートしていないのか、特別な理由を私は知りませんが、Joinがスケーラブルでない事は一般的に知られています。すなわち、一度データの水平分割を行うと、最終的にクライアント(アプリケーションサーバー)側でJoinを行う事になります。理由はどうあれ、データはリレーショナルである事に変り在りませんが、MongoDBはJoinをサポートしていません。 425 | 426 | とにかく、Join無しの世界で生活するためにはアプリケーションのコード内でJoinを行わなくてはなりません。それには基本的に2度の`find`クエリーを発行して2つ目のコレクション内の関連データを取得する必要があります。これから準備するデータはリレーショナルデータベースの外部キーと違いはありません。しばらく素敵な`unicorns`コレクションから視点を外して、`employees`コレクションに注目してみましょう。まず最初に、社員を作成します。(分かり易く説明する為に、`_id`フィールドを明示的に指定しています) 427 | 428 | ~~~ {.javascript} 429 | db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d730"), name: 'Leto'}) 430 | ~~~ 431 | 432 | さて、`Leto`がマネージャーとなる様に設定した社員を何人か追加してみましょう: 433 | 434 | ~~~ {.javascript} 435 | db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d731"), name: 'Duncan', 436 | manager: ObjectId("4d85c7039ab0fd70a117d730")}); 437 | db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d732"), name: 'Moneo', 438 | manager: ObjectId("4d85c7039ab0fd70a117d730")}); 439 | ~~~ 440 | 441 | (上記に倣って、`_id`はユニークになる必要があります。 442 | ここで実際に指定した`ObjectId`を、以降も同じ様に使用する事になります。) 443 | 444 | 言うまでもなく、Letoの社員を検索するには単純に以下を実行します: 445 | 446 | ~~~ {.javascript} 447 | db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")}) 448 | ~~~ 449 | 450 | これは何の変哲もありません。 451 | 最悪の場合、joinの欠如はただ単に余分なクエリーが多くの時間を占めるかもしれません 452 | (恐らくインデックス化されているでしょうが)。 453 | 454 | ### 配列と埋め込みドキュメント ### 455 | MongoDBがjoinを持たないからといって、切り札が無いという意味ではありません。MongoDBのドキュメントがファーストクラスオブジェクトとしての配列をサポートしている事を簡単に確認したのを思い出してください。これは、多対一、多対多の関係を表現する際にとても器用に役立つ事が分かります。簡単な例として、社員が複数のマネージャーを持つ場合、単純にこれらを配列で格納する事が出来ます: 456 | 457 | ~~~ {.javascript} 458 | db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona', 459 | manager: [ObjectId("4d85c7039ab0fd70a117d730"), 460 | ObjectId("4d85c7039ab0fd70a117d732")]}) 461 | ~~~ 462 | 463 | 特に興味深い事は、ドキュメントはスカラ値であっても構わないし、配列であっても構わないという点です。最初の`find`クエリーはどちらであっても動作します: 464 | 465 | ~~~ {.javascript} 466 | db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")}) 467 | ~~~ 468 | 469 | これによって、多対多のjoinテーブルよりもっと便利に素早く配列の値を見つけることが出来ます。 470 | 471 | 配列に加えて、MongoDBは埋め込みドキュメントをサポートしています。次に進んで入れ子になったドキュメントを挿入してみてください: 472 | 473 | ~~~ {.javascript} 474 | db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', 475 | family: {mother: 'Chani', 476 | father: 'Paul', 477 | brother: ObjectId("4d85c7039ab0fd70a117d730")}}) 478 | ~~~ 479 | 480 | 驚くでしょうが、埋め込みドキュメントはクエリーにドット表記を使用できます: 481 | 482 | ~~~ {.javascript} 483 | db.employees.find({'family.mother': 'Chani'}) 484 | ~~~ 485 | 486 | 埋め込みドキュメントがどの様な場所に適合し、どの様に使用するかを簡単に説明します。 487 | 488 | 2つのコンセプトを組み合わせて埋め込みドキュメントの配列を埋め込むこともできます。 489 | 490 | ~~~ {.javascript} 491 | db.employees.insert({_id: ObjectId( 492 | "4d85c7039ab0fd70a117d735"), 493 | name: 'Chani', 494 | family: [ {relation:'mother',name: 'Chani'}, 495 | {relation:'father',name: 'Paul'}, 496 | {relation:'brother', name: 'Duncan'}]}) 497 | ~~~ 498 | 499 | 500 | ### 非正規化 ### 501 | joinを代替するもうひとつの方法はデータを非正規化する事です。従来、非正規化はパフォーマンス特化の為やスナップショットデータ(監査ログの様な)の為に利用されてきました。ところが、NoSQLの人気が高まるにつれて、joinを行わないことや非正規化は次第に一般的なモデリング手法として認められるようになってきました。これは各ドキュメントで各情報の断片を重複させろという意味ではありません。データの重複を恐れながら設計するのではなく、データがどの情報に基づいていてどのドキュメントに属しているかをよく考えてモデリングしましょう。 502 | 503 | 例えば、掲示板のWEBアプリケーションを作っているとします。伝統的な方法では`posts`テーブルに`ユーザーID`を持たせて**ユーザー**と**投稿**を関連付けます。この様なモデルでは`users`テーブルを検索(もしくはjoin)しなければ**投稿**を表示することが出来ません。有効な代替案は各投稿エントリに単純にユーザーIDに対応するユーザー名を保持することです。埋め込みドキュメントでは以下のようにしします。 504 | 505 | ~~~ {.javascript} 506 | `user: {id: ObjectId('Something'), name: 'Leto'}` 507 | ~~~ 508 | 509 | もちろんこうした場合、ユーザーが名前を変更すると全てのドキュメントを更新しなければなりません(1回のマルチアップデートで済みます)。 510 | 511 | この種の取り組みを調整する事はそれほど簡単ではありません。多くの場合、この様な調整は非常識で効果が無いかもしれません。しかし、この取り組みへの実験を恐れないでください。状況によっては適切ではありませんが、その取り組みが効果的で最良の対処になることもあるでしょう。 512 | 513 | ### どちらを選ぶ? ### 514 | 1対多や多対多の関係のシナリオでIDを配列にする事は有用な戦略です。しかし一般的に新しい開発者は埋め込みドキュメントを利用するか手動で参照を行うか悩んでしまうでしょう。 515 | 516 | まず、各ドキュメントのサイズは16MByteまでに制限されていることを知らなければなりません。ドキュメントのサイズに制限があるとわかった所で、気前よく替りにどの様にすれば良いかのアイディアを提供しましょう。現在の所、開発者が巨大なリレーションを行いたい場合、大抵は手動で参照しなければならない様に思われます。埋め込みドキュメントは頻繁に利用されますが、ほとんどの場合親ドキュメントと同時に取得したい小さなデータです。以下は各ユーザーのアカウントに住所を持たせる実例です: 517 | 518 | ~~~ {.javascript} 519 | db.users.insert({name: 'leto', 520 | email: 'leto@dune.gov', 521 | addresses: [{street: "229 W. 43rd St", 522 | city: "New York", state:"NY",zip:"10036"}, 523 | {street: "555 University", 524 | city: "Palo Alto", state:"CA",zip:"94107"}]}) 525 | ~~~ 526 | 527 | これを単に手っ取り早く書き込むための短縮記法だと過小評価してはいけません。直接ドキュメントを持つ事は、データモデルをより単純にし、多くの場合Joinの必要性を無くします。これは特に埋め込みドキュメントや配列のインデックスフィールドのクエリーに適応します。 528 | 529 | ## 少ないコレクションと多いコレクション ## 530 | コレクションがスキーマを強制しないのであれば、単一のコレクションに様々なドキュメントをごちゃ混ぜにしたシステムを作ることも出来ますが、こんなことをしてはいけません。多くのMongoDBシステムはよく見るリレーショナルデータベースと同じ様に設計されますが、それよりもコレクションは少なくなります。 531 | 言い換えると、リレーショナルデータベースのテーブルは多くはMongoDBのコレクションで置き換えることが可能です。(多対多のJoinは重要な例外です)。 532 | 533 | 埋め込みドキュメントの懸念について面白い話題があります。よくある例はブログシステムです。`posts`コレクションと`comments`コレクションを別々に持つべきか、`post`ドキュメントにコメントの配列を埋め込むべきでしょうか?多くの開発者はまだコレクションを分割することを好むようですが、16MByteの制限はひとまず考えないようにしてみてはどうでしょうか(**ハムレット**の全文は200KByte以下です、あなたのブログはこれより有名なのですか?)。 534 | それはより単純明快で、より良いパフォーマンスが得られます。 535 | MongoDBの柔軟なスキーマはこの2つのアプローチを組み合わせて、別々のコレクションに分けたまま、少ない数のコメントを投稿に埋め込む事が出来ます。これは1回のクエリーで欲しいデータをまとめて取得するという原則に従っています。 536 | 537 | 16MByteの制限はそれほど難しいルールではありません。ぜひ今までと違った手法を試してみて、何が上手く行って何がうまく行かないのかを自分で確かめてみて下さい。 538 | 539 | ## 章のまとめ ## 540 | この章の目標はMongoDBでデータをモデリングする為のガイドラインを示すことでした。ドキュメント指向システムのモデリングはリレーショナルデータベースの世界と異なりますが、それほど多くの違いはありません。あなたは多くの柔軟性と一つの制約を知りましたが、新しいシステムであれば上手く適合させることが出来るでしょう。誤った方向に進む唯一の方法は挑戦を行わない事です。 541 | 542 | # どんな時MongoDBを利用するか 543 | そろそろ、MongoDBを何処にどの様にして既存のシステムに適合させる為の感覚をつかむ必要があるでしょう。MongoDBにはその他多くの選択肢を簡単に凌駕する十分新しい競合ストレージ技術があります。 544 | 545 | 私にとって最も重要な教訓はMongoDBとは関係がありません。それはあなたがデータを扱う際に単一の解決手段に頼らなくてもよい様にすることです。たしかに、単一の解決手段を利用することは利点があります。多くのプロジェクトで単一の解決手段に限定することは場合によっては賢明な選択でしょう。異なるテクノロジを使用*しなければならない*と言うことではなく、異なるテクノロジを使用*できる*という発想です。あなただけが、新しいソリューションを導入することの利益がコストを上回るかどうかを知っています。 546 | 547 | そんな訳で、これまで見てきたMongoDBの機能は一般的な解決手段として見なすことを期待しています。ドキュメント指向データベースがリレーショナルデータベースと共通する所が多い点については既に何度か言及してきました。そのために、MongoDBが単純にリレーショナルデータベースの代替になると言い切る事を慎重に扱って来ました。Luceneが全文検索インデックスによってリレーショナルデータベースを強化し、Redisが永続的なKey-Valueストアと見なすことが出来るように、MongoDBはデータの中央レポジトリとして見なすことが出来ます。 548 | 549 | MongoDBはリレーショナルデータベースを*そのまま置き換える*様なものではなく、どちらかというと*別の代替手段*であると言っていることに注意して下さい。それはその他のツールと同様にツールなのです。MongoDBに向いている事もあれば、向いていない事もあります。それではもう少し詳しく分析してみましょう。 550 | 551 | ## 動的スキーマ ## 552 | ドキュメント指向データベースの利点としてよくもてはやされるのは動的スキーマです。これは従来のデータベーステーブルに比べてはるかに柔軟性をもたらします。私は動的スキーマを素晴らしい機能だと認めますが、それが主な理由でない事に多くの人は言及しません。 553 | 554 | 人々はスキーマレスについて、突然狂った様にごちゃ混ぜなデータを格納し始めるのではないか、という様な事を話します。たしかにリレーショナルデータベースと同様のモデルで実際に痛みを伴うデータセットと領域が存在しますが、特殊なケースでしょう。スキーマレスは凄いのですが、殆んどのデータは高度に構造化されてしまいます。特に新しい機能を導入する場合、時々不整合を起こしやすいのは確かです。しかしNULLカラムが実際に上手く解決出来ない様な問題となる事は無いでしょう。 555 | 556 | 私にとって動的スキーマの本当の利点はセットアップの省略とオブジェクト指向プログラミングとの摩擦の低減です。これは、静的型付け言語を利用している場合に特に当てはまります。私はこれまでC#とRubyでMongoDBを利用してきましたが、違いは顕著です。Rubyのダイナミズムと有名なActiveRecord実装は既にオブジェクトとリレーショナルDBの摩擦を十分低減しています。それは実際にMongoDBがRubyと上手く適合していないと言っているわけではありません。どちらかというと多くのRuby開発者はMongoDBを追加の改善として見ると思います。一方、C#やJava開発者はこれらのデータ相互作用を根本的な変化として見るでしょう。 557 | 558 | ドライバ開発者の視点で考えてみて下さい。オブジェクトを保存する際にJSON(正確にはBSONだけど大体同じ)にシリアライズしてMongoDBに送信します。プロパティマッピングや、型マッピングもありません。アプリケーション開発者が単純明快に実装できます。 559 | 560 | ## 書き込み ## 561 | MongoDBが適合する専門的な役割のひとつはロギングです。MongoDBには書き込みを速くする2つの性質があります。1つ目は、送信した書き込み命令は実際の書き込み完了を待たず即座に戻ってくるオプションがあります。2つ目は、データの耐久性に関する動作を制御できる事です。これらの設定は何台のサーバーに書き込んだら成功とするかを設定することで書き込み性能と耐久性を絶妙にコントロール出来ます。 562 | 563 | パフォーマンスに加え、ログデータはスキーマレスの利点を活かす事が出来るデータセットの一つです。最後に、MongoDBの[Cappedコレクション](http://docs.mongodb.org/manual/core/capped-collections/)と呼ばれる機能を紹介します。これまで作成してきたコレクションは暗黙的に普通のコレクションを作成してきました。cappedコレクションは`db.createCollection`コマンドにフラグを指定して作成します: 564 | 565 | ~~~ {.javascript} 566 | // このCappedコレクションを1Mbyteで制限します 567 | db.createCollection('logs', {capped: true, size: 1048576}) 568 | ~~~ 569 | 570 | Cappedコレクションが1MByteの上限に達すると、古いドキュメントは自動的に削除されます。`max`オプションを指定することでドキュメントのサイズではなく数で制限できます。Cappedコレクションは幾つかの興味深い性質を持っています。例えば、ドキュメントの更新を行なってもサイズは増えません。挿入した順序を維持するので時間でソートする為のインデックスを貼る必要はありません。Unixのtail -f <ファイル名>コマンドのように、1回のクエリーでCappedコレクションをtailできます。 571 | 572 | データを時間で有効期限切れにしたい場合、TTL(有効期間)インデックスを利用できます。 573 | 574 | ## 耐久性 ## 575 | MongoDBバージョン1.8までは1台構成のサーバーに耐久性はありませんでした。サーバーがクラッシュした場合データを失う可能性が高かったのです。 576 | 解決策はMongoDBを複数台で構成するしかありませんでした(MongoDBはレプリケーションをサポートしています)。 577 | ジャーナリングは1.8で追加された重要な機能のひとつです。 578 | MongoDBバージョン2.0以降ジャーナリングは既定で有効になっているのでクラッシュや電源喪失からの迅速な復旧が可能です。 579 | 580 | ここで耐久性に関して触れたのは、MongoDBに関する多くの情報の中で単一サーバーの耐久性に関する情報が不足していたからです。ググってみるとMongoDBにはジャーナリングが無いという古い情報が見つかります。 581 | 582 | ## 全文検索 ## 583 | 正真正銘の全文検索機能は最近のMongoDBに追加されました。 584 | 15の言語でステミング(語幹抽出)とストップワードに対応しています。 585 | MongoDBは配列と全文検索に対応していますので、もっと強力な検索機能が必要な場合のみ、その他の全文検索エンジンと組み合わせる必要があるでしょう。 586 | 587 | ## トランザクション ## 588 | MongoDBはトランザクションを持っていません。2つの代替手段を持っていて、1つめは素晴らしいのですが利用に制限があります、もうひとつの方法は柔軟性は高いのですが面倒です。 589 | 590 | 1つめの方法はアトミック操作です。それは素晴らしく実際の問題に適合します。既に`$inc`や`$set`などの単純な例を見てきました。`findAndModify`というドキュメントの更新と削除をアトミックに行うコマンドもあります。 591 | 592 | 2つめは、アトミック操作が不十分で二層コミットをフォールバックする際に利用します。二層コミットはJoinに手動参照するトランザクションです。それはコードの中で行うストレージから独立した解決手段です。二層コミットは複数のデータベースにまたがってトランザクションを行う為の方法としてリレーショナルデータベースの世界ではとても有名です。MongoDBのWebサイトに一般的なシナリオを解説した[例があります](http://www.mongodb.org/display/DOCS/two-phase+commit)(銀行口座)。基本的な考え方は、実際のドキュメントの中にトランザクションの状態を格納し、init-pending-commit/rollbackの段階を手動で行います。 593 | 594 | MongoDBがサポートしている入れ子のドキュメントとスキーマレスな設計は二層コミットの痛みを少し和らげます。しかし初心者にとってはまだあまり良い方法では無いでしょう。 595 | 596 | ## データ処理 ## 597 | バージョン2.2より前のMongoDBはデータ処理の殆どの仕事をMapReduceに頼っていました。 598 | バージョン2.2以降、アグリゲーション・フレームワークやパイプラインと呼ばれる強力な機能が追加されましたので、MapReduceを使う必要があるのはパイプラインでサポートしていない複雑な関数での集約が必要な稀なケースに限られます。 599 | アグリゲーション・パイプラインとMapReduceの詳細については次の章で説明します。 600 | いまの所これらはグループ化する為の機能豊富な方法がいくつかある、と思っていてください。(控えめな言い方ですが) 601 | 巨大なデータを並列処理するにはHadoopの様なものと連携する必要があるでしょう。 602 | ありがたい事に、お互いに補完し合う2つのシステムをつなげるためのMongoDBコネクタがHadoopにはあります。 603 | 604 | もちろん並列データ処理はリレーショナルデータベースもそれほど得意とするものでもありません。MongoDBの将来のバージョンでは巨大なデータセットをもっと上手く扱えるようにする計画があります。 605 | 606 | ## 位置情報 ## 607 | 非常に強力な機能としてMongoDBは[位置情報インデックス](http://docs.mongodb.org/manual/applications/geospatial-indexes/)をサポートしています。geoJSONまたはxとyの座標をドキュメントに格納し、`$near`で指定した座標で検索したり、`$within`で指定した四角や円で検索を行えます。この機能は図で説明したほうが分かりやすいので[5分間位置情報チュートリアル](http://mongly.openmymind.net/geo/index)を試すことをお勧めします。 608 | 609 | ## ツールと成熟度 ## 610 | 既に知っていると思いますが、MongoDBはリレーショナルデータベースより新しいシステムです。何をどの様に行いたいかに依りますが、この事はよく理解しておくべきでしょう。それにしても率直に評価するとMongoDBは新しく、あまり良いツールが在るとは言えません。(とはいえ、成熟したリレーショナルデータベースのツールにも怖ろしく酷いものはあります!)例を挙げると、10進数での浮動小数点の欠如はお金を扱うシステムでは明らかな懸念点です。(それほど致命的ではありませんが) 611 | 612 | 良い面を挙げると、多くの有名な言語のドライバが存在します。プロトコルは単純で現代的で目まぐるしい速度で開発されています。MongoDBは多くの企業の製品に利用され、成熟度に関する懸念は急速に過去のものになりつつあります。 613 | 614 | ## 章のまとめ ## 615 | この章で伝えたかったことは、MongoDBは大抵の場合リレーショナルデータベースを置き換えられるということです。もっと率直に言えば、それは速さの代わりに幾つかの制約をアプリケーション開発者に課します。トランザクションの欠如は正当で重要な懸念です。また、人々は尋ねます **「MongoDBは新しいデータストレージ分野の何処に位置するのでしょうか?」** 答えは単純です: **「ちょうど真ん中あたりだよ」** 616 | 617 | # データの集計 618 | 619 | ## アグリゲーション・パイプライン ## 620 | アグリゲーション・パイプラインはコレクション内のドキュメントを変換・結合する方法です。 621 | ドキュメントをパイプラインに渡すという方法は出力データを別のコマンドに渡すUnixの「パイプ」とちょっと似ています。 622 | 623 | 最も単純なアグリゲーション(集約)はたぶん知っていると思いますがSQLの`GROUP BY`です。 624 | 既に`count()`関数を説明しましたが、ユニコーンをオスとメスで別々に集約したい場合はどうやるのでしょうか? 625 | 626 | ~~~ {.javascript} 627 | db.unicorns.aggregate([{$group:{_id:'$gender', 628 | total: {$sum:1}}}]) 629 | 630 | ~~~ 631 | 632 | シェル内ではパイプライン演算子を配列で渡す`aggregate`ヘルパー関数があります。 633 | 何かを単純にグループ化して集約するには`$group`というオペレーターを利用します。 634 | これはSQLの`GROUP BY`と似たようなもので、`_id`で指定したフィールドでグループ化(ここではgender)とその他フィールドの集約結果(ここでは特定の性別にマッチするドキュメントの合計数)で新しいドキュメントを生成します。 635 | 多分気がついていると思いますが`_id`フィールドには`gender`でなく`$gender`を渡しています。 636 | フィールド名の前の`$`はドキュメントのフィールド値に置き換えられます。 637 | 638 | 他にはどんなパイプライン演算子があるのでしょうか。 639 | `$match`演算子は`$group`演算子の前後でよく使われます。 640 | これは`find`メソッドと同じ様に条件にマッチする、あるいはマッチしないドキュメントの部分集合を集約します。 641 | 642 | ~~~ {.javascript} 643 | db.unicorns.aggregate([{$match: {weight:{$lt:600}}}, 644 | {$group: {_id:'$gender', total:{$sum:1}, 645 | avgVamp:{$avg:'$vampires'}}}, 646 | {$sort:{avgVamp:-1}} ]) 647 | ~~~ 648 | 649 | 650 | ここで紹介したパイプライン演算子`$sort`はたぶんあなたの期待通りに動作します。 651 | もちろん`$skip`や`$limit`もあります。 652 | また`$group`演算子で`$avg`を使用しています。 653 | 654 | MongoDBの配列は強力でDBに格納されているデータはなんでも集約できます。 655 | これらを全て正しく数え上げるにはデータを「平ら」にしてやる必要があります。 656 | 657 | ~~~ {.javascript} 658 | db.unicorns.aggregate([{$unwind:'$loves'}, 659 | {$group: {_id:'$loves', total:{$sum:1}, 660 | unicorns:{$addToSet:'$name'}}}, 661 | {$sort:{total:-1}}, 662 | {$limit:1} ]) 663 | ~~~ 664 | 665 | これは最も多くのユニコーン達に好まれている食べ物と各好物を好きなユニコーンの名前のリストを表示します。 666 | `$sort`と`$limit`の組み合わせはいわゆる「トップN」のランキングを集計します。 667 | 668 | findで指定する"projection"とよく似た[`$project`](http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project)という強力なパイプライン演算子があります。 669 | これは特定のフィールドを含めるだけでなく、既存のフィールドの値に基づいて新しいフィールドを作成・計算することができます。 670 | たとえば、算術演算子を利用して複数のフィールドを足し合わせて平均値を求めたり、文字列演算子を利用して既存のフィールドの文字列を結合した新しいフィールドを生成できます。 671 | 672 | ここで説明したのはアグリゲーションで出来ることのほんの一部です。 673 | 2.6でアグリゲーションは更に強力になりました。集約命令の結果をカーソルに戻したり`$out`パイプライン演算子を利用して結果を新しいコレクションに書き込むことが出来ます。 674 | サポートされている更に多くのパイプラインと式演算子の利用例は[MongoDBマニュアル](http://docs.mongodb.org/manual/core/aggregation-pipeline/)を参照してください。 675 | 676 | ## MapReduce ## 677 | MapReduceは2段階のステップに分かれています。最初にmapを行い、次にreduceを行います。mappingの段階で入力されたドキュメントを変換し、key=>valueのペアを出力します(キーと値は複合できます)。次にkey/valueペアをkey毎にグループ化します。reduceの段階で出力されたキーと値の配列から最終的な結果を生成します。mapとreduce関数はJavaScriptで記述します。 678 | 679 | MongoDBでは、コレクションに対して`mapReduce`命令を呼び出してMapReduceを行います。 680 | `mapReduce`の引数にはmap関数とreduce関数、そして出力ディレクティブを引き渡します。 681 | mongodbのシェルではJavaScriptの関数を定義して引数に指定します。 682 | 多くのライブラリでは関数を文字列で渡してやります(ちょっとカッコ悪いけど)。 683 | 3番目の引数にはドキュメント解析するためのフィルターやソート、リミットなどの追加のオプションを指定します。 684 | さらに、`reduce`段階の後に評価される`finalize`メソッドを指定することも出来ます。 685 | 686 | 恐らくあなたは殆どのケースでMapReduceを使う必要はないでしょう。 687 | もしその必要があった場合は[私のブログ](http://openmymind.net/2011/1/20/Understanding-Map-Reduce/)や[MongoDBマニュアル](http://docs.mongodb.org/manual/core/map-reduce/)を参照にしてください。 688 | 689 | ## 章のまとめ ## 690 | この章ではMongoDBの[アグリゲーション機能](http://docs.mongodb.org/manual/aggregation/)を紹介しました。 691 | アグリゲーションパイプラインはデータをグループ化する為の強力な手段であり、データ構造を把握していれば比較的単純に記述できます。 692 | MapReduceはより理解するのが難しいですがJavaScriptで記述できるのでその能力は際限がありません。 693 | 694 | # パフォーマンスとツール 695 | 最後の章では、パフォーマンスに関する話題とMongoDB開発者に有効な幾つかのツールを紹介します。どちらの話題にも深くは追求しませんがそれぞれの最も重要な部分を分析します。 696 | 697 | ## インデックス ## 698 | まず最初に`getIndexes`コマンドで表示されるコレクション内のインデックス情報を見ていきましょう。 699 | MongoDBのインデックスはリレーショナルデータベースのインデックスと同じように動作します。すなわち、これらはクエリーやソートのパフォーマンスを改善するのに役立ちます。インデックスは`ensureIndex`を呼んで作成されます: 700 | 701 | ~~~ {.javascript} 702 | // この "name" はフィールド名です 703 | db.unicorns.ensureIndex({name: 1}); 704 | ~~~ 705 | 706 | そして、`dropIndex`を呼んで削除します: 707 | 708 | ~~~ {.javascript} 709 | db.unicorns.dropIndex({name: 1}); 710 | ~~~ 711 | 712 | 2番目のパラメーターに`{unique: true}`に設定することでユニークインデックスを作成できます。 713 | 714 | ~~~ {.javascript} 715 | db.unicorns.ensureIndex({name: 1}, {unique: true}); 716 | ~~~ 717 | 718 | インデックスは埋めこまれたフィールドと配列フィールドに対して作成できます。 719 | 複合インデックスも作成できます: 720 | 721 | ~~~ {.javascript} 722 | db.unicorns.ensureIndex({name: 1, vampires: -1}); 723 | ~~~ 724 | 725 | インデックスの順序(1は昇順、-1は降順)は単一キーのインデックスでは影響ありませんが、複合キーで複数のインデックスフィールドをソートする際に違いがあります。 726 | 727 | インデックスに関する詳しい情報は[indexes page](http://docs.mongodb.org/manual/indexes/)にあります。 728 | 729 | ## Explain ## 730 | インデックスを使用しているかに関わらず、カーソルに対し`explain`メソッドを使うことが出来ます: 731 | 732 | ~~~ {.javascript} 733 | db.unicorns.find().explain() 734 | ~~~ 735 | 736 | 出力は`BasicCursor`が利用され(インデックスを使用していない事を意味します)、あの12個のオブジェクトをスキャンしてどれくらいの時間がかかったのかなど、その他の便利な情報も教えてくれます。 737 | 738 | もしインデックスを利用するように変更した場合`BtreeCursor`が利用されていることを確認できます。この場合、インデックスはうまく利用できているでしょう: 739 | 740 | ~~~ {.javascript} 741 | db.unicorns.find({name: 'Pilot'}).explain() 742 | ~~~ 743 | 744 | ## レプリケーション ## 745 | 746 | MongoDBのレプリケーションはリレーショナルデータベースとよく似た仕組みで動作します。 747 | 理想的に、本番環境には同じデータを持つ3台以上のレプリカセットを配備してください。 748 | 単一のプライマリサーバーに対し書き込みが行われると、すべてのセカンダリサーバへ非同期に複製されます。 749 | あなたはセカンダリサーバに対して読み込みリクエストを許可するかを制御できます。これは古いデータを読み込むリスクを低減させるのに役立ちます。 750 | マスターが落ちた場合、セカンダリサーバの1つが新しいプライマリサーバに昇格します。 751 | MongoDBのレプリケーションもこの本の主題の範囲外です。 752 | 753 | ## シャーディング ## 754 | 755 | MongoDBは自動シャーディングをサポートしています。シャーディングはデータを複数のサーバーやクラスターに分割してスケーラビリティを高める手法です。単純な実装ではデータの名前がA〜Mで始まるものをサーバー1に、残りをサーバー2に格納するでしょう。有り難いことに、MongoDBのシャーディング能力はその単純なアルゴリズムを上回ります。シャーディングの話題はこの本では取り上げませんが、単一レプリカセットのデータが限界まで増えた時、あなたはシャーディングの存在を思い出して利用することを検討してください。 756 | 757 | レプリケーションは時間の掛かる参照リクエストをセカンダリサーバに分散することでパフォーマンスの向上にも役立ちますが、主要な目的は信頼性の向上です。 758 | シャーディングはMongoDBクラスタのスケーラビリティを向上させる事が主要な目的です。 759 | レプリケーションとシャーディングを組み合わせることはスケーラビリティと高可用性を向上させるための常套手段です。 760 | 761 | ## 統計 ## 762 | あなたは`db.stats()`とタイプすることでデータベースの統計を取得できます。データベースのサイズは最もよく扱う情報です。`db.unicorns.stats()`とタイプすることで`unicorns`というコレクションの統計を取得することも出来ます。同様にこのコレクションのサイズに関する情報も有用です。 763 | 764 | ## Webインターフェース ## 765 | MongoDBを起動すると、Webベースの管理ツールに関する情報が含まれています(`mongod`を起動した時点までターミナルウィンドウをスクロールすればその様子を確認できるでしょう)。あなたはブラウザで を開いてアクセス出来ます。設定ファイルに`rest=true`を追加して`mongod`プロセスを再起動すると、さらにこれを有効に活用出来るでしょう。このWebインターフェースはサーバの現在の状態についての洞察を与えてくれます。 766 | 767 | ## プロファイラ ## 768 | 以下を実行してMongoDBプロファイラを有効にします: 769 | 770 | ~~~ {.javascript} 771 | db.setProfilingLevel(2); 772 | ~~~ 773 | 774 | 有効にした後に、以下のコマンドを実行します: 775 | 776 | ~~~ {.javascript} 777 | db.unicorns.find({weight: {$gt: 600}}); 778 | ~~~ 779 | 780 | そして、プロファイラを観察して下さい: 781 | 782 | ~~~ {.javascript} 783 | db.system.profile.find() 784 | ~~~ 785 | 786 | この出力は何がいつどれ程のドキュメントを走査し、どれ程のデータが返却されたかを教えてくれます。 787 | 788 | 再度、`setProfilingLevel`の引数を`0`に変えて呼び出すとプロファイラが無効化されます。 789 | 最初の引数に`1`を指定すると100ミリ秒以上掛かるクエリーをプロファイリングします。 790 | 100ミリ秒は既定のしきい値です。2番目の引数に最小のしきい値を指定できます。 791 | 792 | ~~~ {.javascript} 793 | // 1秒以上のクエリーをプロファイルする 794 | db.setProfilingLevel(1, 1000); 795 | ~~~ 796 | 797 | ## バックアップとリストア ## 798 | MongoDBには`bin`の中に`mongodump`という実行ファイルが付属しています。単純に`mongodump`を実行すると、ローカルホストに接続して全てのデータベースを`dump`サブフォルダ以下にバックアップします。`mongodump --help`とタイプすると追加のオプションを確認できます。指定したデータベースをバックアップする`--db DBNAME`と、指定したコレクションをバックアップする`--collection COLLECTIONNAME`は共通です。次に、同じ`bin`フォルダにある`mongorestore`実行ファイルを使うことで、以前のバックアップをリストアできます。同じように、`--db`と`--collection`はオプションはリストアするデータベースとコレクションを指定します。 799 | `mongodump`と`mongorestore`はBSONというMongoDBの独自フォーマットを操作します。 800 | 801 | 例えば、`learn`データベースを`backup`フォルダにバックアップするには以下を実行します(これは実行ファイルですのでmongoシェルではなく、ターミナルウィンドウでコマンドを実行します): 802 | 803 | ~~~ {.bash} 804 | mongodump --db learn --out backup 805 | ~~~ 806 | 807 | `unicorns`コレクションのみをリストアするにはこの様に実行します: 808 | 809 | ~~~ {.bash} 810 | mongorestore --db learn --collection unicorns backup/learn/unicorns.bson 811 | ~~~ 812 | 813 | `mongoexport`と`mongoimport`という2つの実行ファイルはJSONまたはCSV形式でエクスポートとインポートできることを指摘しておきます。例えばJSON形式で出力するには以下のようにします: 814 | 815 | ~~~ {.bash} 816 | mongoexport --db learn --collection unicorns 817 | ~~~ 818 | 819 | そして、CSV形式での出力はこうします: 820 | 821 | ~~~ {.bash} 822 | mongoexport --db learn --collection unicorns \ 823 | --csv --fields name,weight,vampires 824 | ~~~ 825 | 826 | `mongoexport`と`mongoimport`はデータを完全に表現できないことに注意して下さい。`mongodump`と`mongorestore`のみを実際のバックアップでは利用すべきです。 827 | 詳細はMongoDBマニュアルの[バックアップの選択肢](http://docs.mongodb.org/manual/core/backups/)を読んでください。 828 | 829 | ## 章のまとめ ## 830 | この章では、MongoDBで利用する様々なコマンドやツールやパフォーマンスの詳細を見てきました。全てに触れることは出来ませんでしたが一般的なものをいくつか紹介しました。MongoDBのインデックス化がリレーショナルデータベースのインデックス化とよく似ているのと同様に、ツールの多くも同じです。しかしMongoDBの方がわかり易くて単純に利用できるものが多いでしょう。 831 | 832 | # まとめ # {-} 833 | 実際のプロジェクトでMongoDBを使い始める為には十分な情報を持つべきです。 834 | ここで紹介してきた事の他にも、MongoDBの情報はまだまだあります。しかし、あなたが次に優先すべき事は、これまでに学んできた事を組み合わせて、これから利用するドライバに慣れる事です。 835 | [MongoDBのウェブサイト](http://www.mongodb.org/)には数多くの役立つ情報があります。 836 | 公式な[MongoDBユーザーグループ](http://groups.google.com/group/mongodb-user)は質問を尋ねるには最適な場所です。 837 | 838 | NoSQLは必要性によってのみ生み出されただけではなく、新しいアプローチへの興味深い試みでもあります。 839 | 有難いことにこれらの分野は常に進展しており、私たちが時々失敗し、挑戦し続けなければ成功はありえません。 840 | 思うにこれは、私たちがプロとして活躍するための賢明な方法となるでしょう。 841 | 842 | 848 | -------------------------------------------------------------------------------- /ja/stylesheet.css: -------------------------------------------------------------------------------- 1 | /* This defines styles and classes used in the book */ 2 | body { margin: 5%; text-align: justify; font-size: medium; } 3 | code { font-family: monospace; } 4 | h1 { text-align: left; } 5 | h2 { text-align: left; } 6 | h3 { text-align: left; } 7 | h4 { text-align: left; } 8 | h5 { text-align: left; } 9 | h6 { text-align: left; } 10 | h1.title { } 11 | h2.author { } 12 | h3.date { } 13 | ol.toc { padding: 0; margin-left: 1em; } 14 | ol.toc li { list-style-type: none; margin: 0; padding: 0; } 15 | 16 | pre { 17 | background-color: gainsboro; 18 | } 19 | -------------------------------------------------------------------------------- /ja/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $pagetitle$ 8 | $if(quotes)$ 9 | 12 | $endif$ 13 | $if(highlighting-css)$ 14 | 17 | $endif$ 18 | $for(css)$ 19 | 20 | $endfor$ 21 | 22 | 23 | $if(titlepage)$ 24 |
25 |

$title$

26 | $if(subtitle)$ 27 |

$subtitle$

28 | $endif$ 29 | $for(author)$ 30 |

$author$ [著]

31 | $endfor$ 32 | $if(translator)$ 33 |

$translator$ [訳]

34 | $endif$ 35 |
36 | $else$ 37 | $body$ 38 | $endif$ 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ja/template.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4j,oneside,11ptj,uplatex]{jsbook} 2 | \usepackage[green]{hamacro} 3 | 4 | \hypersetup{ 5 | pdfstartview={FitV}, 6 | pdftitle={$title$}, 7 | pdfsubject={$subtitle$}, 8 | pdfauthor={$authors$}, 9 | pdfkeywords={$keywords$}, 10 | } 11 | 12 | % title 13 | \title{$title$} 14 | \author{$author$} 15 | \date{\today} 16 | 17 | \makeatletter 18 | % jsbookで目次をブックマークするための修正 19 | % chapterのスターを消しただけ 20 | \renewcommand{\tableofcontents}{% 21 | \settowidth\jsc@tocl@width{\headfont\prechaptername\postchaptername}% 22 | \settowidth\@tempdima{\headfont\appendixname}% 23 | \ifdim\jsc@tocl@width<\@tempdima \setlength\jsc@tocl@width{\@tempdima}\fi 24 | \ifdim\jsc@tocl@width<2zw \divide\jsc@tocl@width by 2 \advance\jsc@tocl@width 1zw\fi 25 | \if@twocolumn 26 | \@restonecoltrue\onecolumn 27 | \else 28 | \@restonecolfalse 29 | \fi 30 | \chapter{\contentsname}% 31 | \@mkboth{\contentsname}{}% 32 | \@starttoc{toc}% 33 | \if@restonecol\twocolumn\fi 34 | } 35 | \makeatother 36 | 37 | % document 38 | \begin{document} 39 | %\maketitle 40 | \begin{titlepage} 41 | \begin{flushright} 42 | %{\sf 第$version$版} 43 | \includegraphics[height=1.5truecm]{update.eps}\\ 44 | \end{flushright} 45 | \vspace*{1em} 46 | \begin{center} 47 | {\HUGE\gt\sf\textbf{$title$}} 48 | \bigskip\\ 49 | {\LARGE\it{$subtitle$}} 50 | \bigskip\\ 51 | {\LARGE\gt\sf{$authors$}}\\ 52 | \vspace{6em} 53 | \includegraphics{title.eps} 54 | \end{center} 55 | 56 | \end{titlepage} 57 | 58 | \clearpage 59 | 60 | $body$ 61 | 62 | \clearpage 63 | \thispagestyle{empty} 64 | \vspace*{\fill} 65 | 66 | {\noindent\Large\gt\sf{$title$}} \\ 67 | \rule[8pt]{14cm}{1pt} \\ 68 | {\noindent 69 | \today $version$版発行 70 | \noindent 71 | } 72 | \bigskip\\ 73 | \begin{tabular}{ll} 74 | 著者 & $author$ \\ 75 | 翻訳 & $translator$ \\ 76 | \end{tabular} 77 | \bigskip\\ 78 | \rule[0pt]{14cm}{1pt} \\ 79 | 80 | \end{document} 81 | -------------------------------------------------------------------------------- /ja/title.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%Creator: cairo 1.12.2 (http://cairographics.org) 3 | %%CreationDate: Mon Dec 16 20:01:41 2013 4 | %%Pages: 1 5 | %%DocumentData: Clean7Bit 6 | %%LanguageLevel: 3 7 | %%BoundingBox: 10 23 134 298 8 | %%EndComments 9 | %%BeginProlog 10 | save 11 | 50 dict begin 12 | /q { gsave } bind def 13 | /Q { grestore } bind def 14 | /cm { 6 array astore concat } bind def 15 | /w { setlinewidth } bind def 16 | /J { setlinecap } bind def 17 | /j { setlinejoin } bind def 18 | /M { setmiterlimit } bind def 19 | /d { setdash } bind def 20 | /m { moveto } bind def 21 | /l { lineto } bind def 22 | /c { curveto } bind def 23 | /h { closepath } bind def 24 | /re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto 25 | 0 exch rlineto 0 rlineto closepath } bind def 26 | /S { stroke } bind def 27 | /f { fill } bind def 28 | /f* { eofill } bind def 29 | /n { newpath } bind def 30 | /W { clip } bind def 31 | /W* { eoclip } bind def 32 | /BT { } bind def 33 | /ET { } bind def 34 | /pdfmark where { pop globaldict /?pdfmark /exec load put } 35 | { globaldict begin /?pdfmark /pop load def /pdfmark 36 | /cleartomark load def end } ifelse 37 | /BDC { mark 3 1 roll /BDC pdfmark } bind def 38 | /EMC { mark /EMC pdfmark } bind def 39 | /cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def 40 | /Tj { show currentpoint cairo_store_point } bind def 41 | /TJ { 42 | { 43 | dup 44 | type /stringtype eq 45 | { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse 46 | } forall 47 | currentpoint cairo_store_point 48 | } bind def 49 | /cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore 50 | cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def 51 | /Tf { pop /cairo_font exch def /cairo_font_matrix where 52 | { pop cairo_selectfont } if } bind def 53 | /Td { matrix translate cairo_font_matrix matrix concatmatrix dup 54 | /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point 55 | /cairo_font where { pop cairo_selectfont } if } bind def 56 | /Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def 57 | cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def 58 | /g { setgray } bind def 59 | /rg { setrgbcolor } bind def 60 | /d1 { setcachedevice } bind def 61 | %%EndProlog 62 | %%Page: 1 1 63 | %%BeginPageSetup 64 | %%PageBoundingBox: 10 23 134 298 65 | %%EndPageSetup 66 | q 10 23 124 275 rectclip q 67 | q 68 | 70.297 296.438 m 69.539 296.559 71.855 292.055 66.301 285.02 c 60.316 277.438 69 | 12.145 246.27 10.645 162.289 c 9.625 105.082 54.82 65.141 66.309 57.696 70 | c 66.309 57.696 70.543 42.985 70.684 25.625 c 78.273 23.649 l 77.367 41.496 71 | 77.105 51.813 82.266 59.688 c 89.496 63.094 135.07 102.098 133.125 162.411 72 | c 130.809 234.254 90.465 265.411 78.988 280.391 c 74.023 286.864 71.898 73 | 296.301 70.297 296.438 c h 74 | W n 75 | [0.6935 0 0 -0.6935 672.392726 660.252592] concat 76 | /CairoFunction 77 | << /FunctionType 3 78 | /Domain [ 0 1 ] 79 | /Functions [ 80 | << /FunctionType 2 81 | /Domain [ 0 1 ] 82 | /C0 [ 0.890196 0.886275 0.807843 ] 83 | /C1 [ 0.807843 0.807843 0.709804 ] 84 | /N 1 85 | >> 86 | << /FunctionType 2 87 | /Domain [ 0 1 ] 88 | /C0 [ 0.807843 0.807843 0.709804 ] 89 | /C1 [ 0.584314 0.588235 0.435294 ] 90 | /N 1 91 | >> 92 | ] 93 | /Bounds [ 0.450806 ] 94 | /Encode [ 1 1 2 { pop 0 1 } for ] 95 | >> 96 | def 97 | << /ShadingType 2 98 | /ColorSpace /DeviceRGB 99 | /Coords [ -856.059448 917.140747 -897.812805 890.359009 ] 100 | /Extend [ true true ] 101 | /Function CairoFunction 102 | >> 103 | shfill 104 | Q 105 | q 106 | 70.297 296.438 m 74.473 282.934 73.699 221.43 74.602 162.411 c 75.422 108.883 107 | 65.359 67.344 82.266 59.688 c 89.496 63.094 135.07 102.098 133.125 162.411 108 | c 130.809 234.254 90.465 265.411 78.988 280.391 c 74.023 286.864 71.898 109 | 296.301 70.297 296.438 c h 110 | W n 111 | [0.6935 0 0 -0.6935 672.392726 660.252592] concat 112 | /CairoFunction 113 | << /FunctionType 2 114 | /Domain [ 0 1 ] 115 | /C0 [ 0.0980392 0.513726 0.196078 ] 116 | /C1 [ 0.141176 0.576471 0.231373 ] 117 | /N 1 118 | >> 119 | def 120 | << /ShadingType 2 121 | /ColorSpace /DeviceRGB 122 | /Coords [ -845.729492 857.263733 -845.859558 536.137329 ] 123 | /Extend [ true true ] 124 | /Function CairoFunction 125 | >> 126 | shfill 127 | Q 128 | q 129 | 70.305 296.442 m 69.547 296.559 71.848 292.055 66.297 285.02 c 60.312 277.438 130 | 12.141 246.27 10.641 162.289 c 9.621 105.082 54.828 65.149 66.316 57.704 131 | c 69.477 59.153 70.988 74.063 72.363 91.336 c 72.465 109.649 75.023 134.368 132 | 74.594 162.399 c 73.75 217.731 74.379 275.258 71.02 293.364 c 71.012 293.411 133 | 71.008 293.446 70.996 293.493 c 70.77 294.477 70.543 295.457 70.305 296.442 134 | c h 135 | W n 136 | [0.6935 0 0 -0.6935 672.392726 660.252592] concat 137 | /CairoFunction 138 | << /FunctionType 2 139 | /Domain [ 0 1 ] 140 | /C0 [ 0.0588235 0.607843 0.211765 ] 141 | /C1 [ 0.392157 0.733333 0.4 ] 142 | /N 1 143 | >> 144 | def 145 | << /ShadingType 2 146 | /ColorSpace /DeviceRGB 147 | /Coords [ -1031.014038 698.59906 -871.292603 549.738647 ] 148 | /Extend [ true true ] 149 | /Function CairoFunction 150 | >> 151 | shfill 152 | Q 153 | Q Q 154 | showpage 155 | %%Trailer 156 | end restore 157 | %%EOF 158 | -------------------------------------------------------------------------------- /ja/title.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 17 | 19 | 23 | 27 | 28 | 30 | 34 | 38 | 39 | 41 | 45 | 49 | 53 | 54 | 56 | 60 | 64 | 65 | 68 | 73 | 79 | 84 | 89 | 95 | 96 | 99 | 104 | 110 | 115 | 120 | 126 | 127 | 129 | 133 | 137 | 138 | 139 | 142 | 145 | 146 | 149 | 157 | 159 | 162 | 163 | 165 | 168 | 169 | 171 | 174 | 175 | 177 | 181 | 185 | 186 | 188 | 191 | 192 | 194 | 198 | 202 | 203 | 205 | 208 | 209 | 211 | 214 | 215 | 217 | 221 | 222 | 232 | 241 | 243 | 251 | 252 | 260 | 268 | 276 | 277 | 279 | 280 | 282 | image/svg+xml 283 | 285 | 286 | 287 | 288 | 289 | 293 | 297 | 301 | 305 | 309 | 310 | 311 | 315 | 356 | 357 | -------------------------------------------------------------------------------- /ja/title.txt: -------------------------------------------------------------------------------- 1 | % The Little MongoDB Book 2 | % Karl Seguin --------------------------------------------------------------------------------