Showing posts with label NoSQL. Show all posts
Showing posts with label NoSQL. Show all posts

Friday, July 12, 2013

Getting Started with MongoDB – Part 2

In the previous post we explored the basics of MongoDB. In this post we going to dig deeper in MongoDB.

Indexing

Whenever a new collection is created, MongoDB automatically creates an index by the _id field. These indexes can be found in the system.indexes collection. You can show all indexes in the database using db.system.indexes.find() . Most queries will include more fields than just the _id, so we need to make indexes on those fields.

Before creating more indexes, let’s see what is the performance of a sample query without creating any indexes other than the automatically created one for _id. Create the following function to generate random phone numbers.

function (area,start,stop) {
for(var i=start; i < stop; i++) {
var country = 1 + ((Math.random() * 8) << 0);
var num = (country * 1e10) + (area * 1e7) + i;
db.phones.insert({
_id: num,
components: {
country: country,
area: area,
prefix: (i * 1e-4) << 0,
number: i
},
display: "+" + country + " " + area + "-" + i
});

Run the function with a three-digit area code (like 800) and a range of seven digit numbers (5,550,000 to 5,650,000)

populatePhones( 800, 5550000, 5650000 )

Now we expecting to see a new index created for our new collection.

> db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "newdb.towns", "name" : "_id_" }
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "newdb.countries", "name" : "_id_" }
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "newdb.phones", "name" : "_id_" }

Now let’s check the query without an index. The explain() method is used to output details of a given operation and can help us here.

> db.phones.find( { display : "+1 800-5650001" } ).explain()
{
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 100000,
        "nscanned" : 100000,
        "nscannedObjectsAllPlans" : 100000,
        "nscannedAllPlans" : 100000,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 134,
        "indexBounds" : {

        },
        "server" : "ESOLIMAN:27017"
}

Just to make things simple, we will look at the millis field only which gives the milliseconds needed to complete the query. Now it is 134.

Now we going to create an index and see how it improves our query execution time. We create an index by calling ensureIndex(fields,options) on the collection. The fields parameter is an object containing the fields to be indexed against. The options parameter describes the type of index to make. On production environments, creating an index on a large collection can be slow and resource-intensive, you should create them in off-peak times. In our case we going to build a unique index on the display field and we will drop duplicate entries.

> db.phones.ensureIndex(
... { display : 1 },
... { unique : true, dropDups : true }
... )

lets try explain() of find() and see the new value for millis field. Query execution time improved, from 134 down to 16.

> db.phones.find( { display : "+1 800-5650001" } ).explain()
{
        "cursor" : "BtreeCursor display_1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 0,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 16,
        "indexBounds" : {
                "display" : [
                        [
                                "+1 800-5650001",
                                "+1 800-5650001"
                        ]
                ]
        },
        "server" : "ESOLIMAN:27017"
}

Notice the cursor changed from a Basic to a B-tree cursor. MongoDB is no longer doing
a full collection scan but instead walking the tree to retrieve the value.

Mongo can build your index on nested values: db.phones.ensureIndex({ "components.area": 1 }, { background : 1 })

Aggregations

count() counts the number of matching documents. It takes a query and returns a number.

> db.phones.count({'components.number': { $gt : 5599999 } })
100000

distinct() returns each matching value where one or more exists.

> db.phones.distinct('components.number', {'components.number': { $lt : 5550005 } })
[ 5550000, 5550001, 5550002, 5550003, 5550004 ]

group() groups documents in a collection by the specified keys and performs simple aggregation functions such as computing counts and sums. It is similar to GROUP BY in SQL. It accepts the following parameters

  • key – Specifies one or more document fields to group by.
  • reduce – Specifies a function for the group operation perform on the documents during the grouping operation, such as compute a sum or a count. The aggregation function takes two arguments: the current document and the aggregate result for the previous documents in the group.
  • initial – Initializes the aggregation result document.
  • keyf – Optional. Alternative to the key field. Specifies a function that creates a “key object” for use as the grouping key. Use the keyf instead of key to group by calculated fields rather than existing document fields. Like HAVING in SQL.
  • cond – Optional. Specifies the selection criteria to determine which documents in the collection to process. If you omit the cond field, db.collection.group() processes all the documents in the collection for the group operation.
  • finalize – Optional. Specifies a function that runs each item in the result set before db.collection.group() returns the final value. This function can either modify the result document or replace the result document as a whole.

> db.phones.group({
... initial : { count : 0 },
... reduce : function(phone, output) { output.count++; },
... cond : { 'components.number' : { $gt : 5599999 } },
... key : { 'components.area' : true }
... })
[
        {
                "components.area" : 800,
                "count" : 50000
        },
        {
                "components.area" : 855,
                "count" : 50000
        }
]

The first thing we did here was set an initial object with a field named count set to 0—fields created here will appear in the output. Next we describe what to do with this field by declaring a reduce function that adds one for every document we encounter. Finally, we gave group a condition restricting which documents to reduce over.

Server-Side Commands

All queries and operations we did till now, execute on the client side. The db object provides a command named eval(), which passes the given function to the server. This dramatically reduces the communication between client and server. It is similar to stored procedures in SQL.

There is a also a set of prebuilt commands that can be executed on the server. Use db.listCommands() to get a list of these commands. To run any command on the server use db.runCommand() like db.runCommand({ "count" : "phones" })

Although it is not recommended, you can store a JavaScript function on the server for later reuse.

MapReduce

MapReduce is a framework for parallelizing problems. Generally speaking the parallelization happens on two steps:

  • "Map" step: The master node takes the input, divides it into smaller sub-problems, and distributes them to worker nodes. A worker node may do this again in turn, leading to a multi-level tree structure. The worker node processes the smaller problem, and passes the answer back to its master node.
  • "Reduce" step: The master node then collects the answers to all the sub-problems and combines them in some way to form the output – the answer to the problem it was originally trying to solve.

To show the MapReduce framework in action, let’s build on the phones collections that we created previously. Let’s generate a report that counts all phone numbers that contain the same digits for each country.

First we create a helper function that extracts an array of all distinct numbers (this step is not a MapReduce step).

> distinctDigits = function(phone) {
... var
... number = phone.components.number + '',
... seen = [],
... result = [],
... i = number.length;
... while(i--) {
...  seen[+number[i]] = 1;
...  }
... for (i=0; i<10; i++) {
...  if (seen[i]) {
...   result[result.length] = i;
...   }
...  }
... return result;
... }

> db.eval("distinctDigits(db.phones.findOne({ 'components.number' : 5551213 }))")
[ 1, 2, 3, 5 ]

Now let’s find find distinct numbers of each country. Since we need to query by country later, we will add the distinct digits array and country as compound key. For each distinct digits array in each country, we will add a count field that hold the value 1.

> map = function() {
... var digits = distinctDigits(this);
... emit( { digits : digits, country : this.components.country } , { count : 1 } );
... }

The reducer function will all these 1s that have been emitted from the map function.

>reduce = function(key, values) {
... var total = 0;
... for(var i=0; i<values.length; i++) {
...  total += values[i].count;
...  }
...  return { count : total };
... }

Now it is time to put all pieces together and start the whole thing (the input collection, map function, reduce function, output collection).

> results = db.runCommand({
... mapReduce : 'phones',
... map : map,
... reduce : reduce,
... out : 'phones.report'
... })
{
        "result" : "phones.report",
        "timeMillis" : 21084,
        "counts" : {
                "input" : 200000,
                "emit" : 200000,
                "reduce" : 48469,
                "output" : 3489
        },
        "ok" : 1
}

Now you can query the output collection like any other collection

> db.phones.report.find()
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 1 }, "value" : { "count" : 37 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 2 }, "value" : { "count" : 23 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 3 }, "value" : { "count" : 17 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 4 }, "value" : { "count" : 29 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 5 }, "value" : { "count" : 34 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 6 }, "value" : { "count" : 35 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 7 }, "value" : { "count" : 33 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 8 }, "value" : { "count" : 32 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 1 }, "value" : { "count" : 5 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 2 }, "value" : { "count" : 7 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 3 }, "value" : { "count" : 3 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 4 }, "value" : { "count" : 6 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 5 }, "value" : { "count" : 5 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 6 }, "value" : { "count" : 10 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 7 }, "value" : { "count" : 5 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 8 }, "value" : { "count" : 7 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6 ], "country" : 1 }, "value" : { "count" : 95 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6 ], "country" : 2 }, "value" : { "count" : 104 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6 ], "country" : 3 }, "value" : { "count" : 108 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6 ], "country" : 4 }, "value" : { "count" : 113 } }
Type "it" for more

or

> db.phones.report.find({'_id.country' : 8})
{ "_id" : { "digits" : [  0,  1,  2,  3,  4,  5,  6 ], "country" : 8 }, "value" : { "count" : 32 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5 ], "country" : 8 }, "value" : { "count" : 7 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6 ], "country" : 8 }, "value" : { "count" : 127 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6,  7 ], "country" : 8 }, "value" : { "count" : 28 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6,  8 ], "country" : 8 }, "value" : { "count" : 27 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  6,  9 ], "country" : 8 }, "value" : { "count" : 29 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  7 ], "country" : 8 }, "value" : { "count" : 10 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  8 ], "country" : 8 }, "value" : { "count" : 7 } }
{ "_id" : { "digits" : [  0,  1,  2,  3,  5,  9 ], "country" : 8 }, "value" : { "count" : 8 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5 ], "country" : 8 }, "value" : { "count" : 3 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  6 ], "country" : 8 }, "value" : { "count" : 121 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  6,  7 ], "country" : 8 }, "value" : { "count" : 25 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  6,  8 ], "country" : 8 }, "value" : { "count" : 27 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  6,  9 ], "country" : 8 }, "value" : { "count" : 17 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  7 ], "country" : 8 }, "value" : { "count" : 4 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  8 ], "country" : 8 }, "value" : { "count" : 4 } }
{ "_id" : { "digits" : [  0,  1,  2,  4,  5,  9 ], "country" : 8 }, "value" : { "count" : 7 } }
{ "_id" : { "digits" : [  0,  1,  2,  5 ], "country" : 8 }, "value" : { "count" : 14 } }
{ "_id" : { "digits" : [  0,  1,  2,  5,  6 ], "country" : 8 }, "value" : { "count" : 162 } }
{ "_id" : { "digits" : [  0,  1,  2,  5,  6,  7 ], "country" : 8 }, "value" : { "count" : 95 } }
Type "it" for more

The unique emitted keys are under the field _id, and all of the data returned from the reducers are
under the field value. If you prefer that the mapreducer just output the results, rather than outputting to a collection, you can set the out value to { inline : 1 }, but bear in mind there is a limit to the size of a result you can output (16 MB).

In some situations you may need to feed the reducer function’s output into another reducer function. In these situations we need to carefully handle both cases: either map’s output or another reduce’s output.

MongoDB have so many features that we didn’t even mentioned here. In later posts will continue working on them.

Getting Started with MongoDB – Part 1


MongoDB (from "humongous") is an open source document-oriented database system developed and supported by 10gen (founded by Dwight Merriman). First publicly released in 2009, and since then it have been a rising star in the NoSQL world. MongoDB stores structured data as JSON-like documents with dynamic schemas (technically data is stored in a binary form of JSON known as BSON), making the integration of data in certain types of applications easier and faster.

Installation

  1. Download the latest mongoDB version from here.
  2. Extract the archive to your preferred location (in my case C:\mongodb). MongoDB is self-contained and does not have any other system dependencies. You can run MongoDB from any folder you choose. You may install MongoDB in any directory.
  3. MongoDB requires a data folder to store its files (default is C:\data\db ). You may specify a different path with the dbpath setting when lunching mongod.exe.

Starting the Server

To start MongoDB, open the command prompt window, and run mongod.exe from the bin directory (specify the data path if needed)
CWindowssystem32cmd.exe - mongod.exe  --dbpath Cmongodbdata_2013-07-09_16-10-49The waiting for connections message in the console output indicates that the mongod.exe process is running successfully and waiting for connections on port 27017

Connecting to the Server

To connect to the server, open another command prompt window and run mongo.exe from the bin directory.
CWindowssystem32cmd.exe - mongo_2013-07-09_16-27-37

Run MongoDB as a Windows Service

  1. Create a log file for MongoDB (in my case c:\mongodb\log\mongo.log ).
  2. Create a data directory for MongoDB (in my case c:\mongodb\data ).
  3. Open the command prompt window as an administrator.
  4. Run the following command C:\mongodb\bin>mongod.exe –install –rest –master --logpath "c:\mongodb\log\mongo.log"
  5. Run regedit from start menu.
  6. Go to HKEY_LOCAL_MACHINE >> SYSTEM >> CurrentControlSet >> services
  7. Find the MongoDB directory & edit the ImagePath key.
  8. Set value as c:\mongodb\bin\mongod --service  --rest  --master  --logpath=C:\mongodb\log\mongo.log  --dbpath=C:\mongodb\data
  9. Save and exit registry editor
  10. Open ComponentServices from Start menu >> Run
  11. Locate the Mongo DB service, and reigh click >> Properties.
  12. Set the Startup Type to Automatic. Then start the service.
    1. To run the MongoDB service from command window, use net start MongoDB
  13. Check at http://localhost:28017/ to see , MongoDB should return stats.
In case you want to remove MongoDB service C:\mongodb\bin\mongod.exe –remove

Data Model

Data in MongoDB has a flexible schema.
  • Database consists on a set of collections.
  • Collections consists of a set of documents.
    • Documents in the same collection do not need to have the same set of fields or structure.
    • Common fields in a collection’s documents can hold different types of data.
Based on what we mentioned, you could say that MongoDB is schema-less. But you may refer to this data modeling article before doing real work on MongoDB.

CRUD operations

  • When you start the mongo shell, it connect to test database by default. To create a new database or switch to another database, use use newdb
  • To show a list of databases, use show dbs (databases are created when you insert the first values in it. This means that any database have been created but no values inserted in it, it really doesn’t exist).
  • To confirm the current session database, use db
  • To create a collection, just insert an initial record to it. Since Mongo is schema-less, there is no need to define anything up front. The following code creates/inserts a towns collection:
db.towns.insert({
name: "New York",
population: 22200000,
last_census: ISODate("2009-07-31"),
famous_for: [ "statue of liberty", "food" ],
mayor : {
name : "Michael Bloomberg",
party : "I"
}
})

    • brackets like {...} denote an object with key-value pairs.
    • brackets like [...] denote an array.
    • You can nest these values to any depth.
  • To show a list of collections, use show collections.
  • To list the contents of a collection, use db.towns.find()
    • You will see a system generated field _id ( composed of a timestamp, client machine ID, client process ID, and a 3-byte incremented counter) (you can override this system generated
  • MongoDB commands are JavaScript functions.
    • db is a JavaScript object that contains information about the current database. Try typeof db
    • db.x is a JavaScript object that represent a collection named x within the current database. Try typeof db.towns
    • db.x.help() will list available functions related to the given object. Try typeof db.towns.insert
    • If you want to inspect the source code of a function, call it without parameter or parentheses.
  • Functions You can create JavaScript functions and call them on the mongo shell like:
function insertCity( name, population, last_census, famous_for, mayor_info) {
db.towns.insert({
name:name,
population:population,
last_census: ISODate(last_census),
famous_for:famous_for,
mayor : mayor_info
});
}

insertCity("Punxsutawney", 6200, '2008-31-01', ["phil the groundhog"], { name : "Jim Wehrle" } )
insertCity("Portland", 582000, '2007-20-09', ["beer", "food"], { name : "Sam Adams", party : "D" } )

Now have three towns in our collection
Command Prompt - mongo_2013-07-11_14-35-39
  • To get a specific document, we only need _id passed to find() function in type ObjectId(findOne() retrieves only one matching document). String can be converted to ObjectId using ObjectId(str) function.
 db.towns.find({ "_id" : ObjectId("51def56c1cf66f4c40bb7f4a") })
    • The find() function also accepts an optional second parameter: a fields object we can use to filter which fields are retrieved. If we want only the town name (along with _id), pass in name with a value resolving to 1 (or true).
db.towns.find({ "_id" : ObjectId("51def56c1cf66f4c40bb7f4a") }, { name : 1})
    • To retrieve all fields except name, set name to 0 (or false or null).
db.towns.find({ "_id" : ObjectId("51def56c1cf66f4c40bb7f4a") }, { name : 0})
    • You can retrieve documents based on criteria other than _id. You can use regular expressions or any operator.
db.towns.find( { name : /^P/, population : { $lt : 10000 } }, { name : 1, population : 1 } )
We said before that the query language is JavaScript, which means we can construct operations as we would construct objects. In the following query, we build a criteria where the population must be between 10.000 and 1 million. Ranges work also on dates.
> var population_range = {}
> population_range['$lt'] = 100000
100000
> population_range['$gt'] = 10000
10000
> population_range['$lt'] = 1000000
1000000
> population_range['$gt'] = 10000
10000
> db.towns.find( {name : /^P/, population : population_range }, {name: 1})
{ "_id" : ObjectId("51df08e72476b99608460870"), "name" : "Portland" }

    • You can also query based on values in nested arrays, either matching exact values or matching partial values or all matching values or the lack of matching values.
> db.towns.find( { famous_for : 'food' }, { _id : 0, name : 1, famous_for : 1 } )
{ "name" : "New York", "famous_for" : [  "statue of liberty",  "food" ] }
{ "name" : "Portland", "famous_for" : [  "beer",  "food" ] }
> db.towns.find( { famous_for : /statue/ }, { _id : 0, name : 1, famous_for : 1 } )
{ "name" : "New York", "famous_for" : [  "statue of liberty",  "food" ] }
> db.towns.find( { famous_for : { $all : ['food', 'beer'] } }, { _id : 0, name:1, famous_for:1 } )
{ "name" : "Portland", "famous_for" : [  "beer",  "food" ] }
> db.towns.find( { famous_for : { $nin : ['food', 'beer'] } }, { _id : 0, name : 1, famous_for : 1 } )
{ "name" : "Punxsutawney", "famous_for" : [  "phil the groundhog" ] }

    • You can query a sub-document by giving the field name as a string separating nested layers with a dot.
> db.towns.find( { 'mayor.party' : 'I' }, { _id : 0, name : 1, mayor : 1 } )
{ "name" : "New York", "mayor" : { "name" : "Michael Bloomberg", "party" : "I" } }

    • To query the nonexistence of a field value
> db.towns.find( { 'mayor.party' : { $exists : false } }, { _id : 0, name : 1, mayor : 1 } )
{ "name" : "Punxsutawney", "mayor" : { "name" : "jim Wehrle" } }

  • elemMatch

$elemMatch helps us specify if a document or a nested document matches all of our criteria, the document counts as a match and returned. We can use any advanced operators within this criteria. To show it in action, let’s insert some data into a new collection countries
> db.countries.insert({ _id : "us",
... name : "United States",
... exports : {
...  foods : [
...   { name : "bacon", tasty : true },
...   { name : "burgers" }
...          ]
...            }
... })
> db.countries.insert({ _id : "ca",
... name : "Canada",
... exports : {
...  foods : [
...   { name : "bacon", tasty : false },
...   { name : "syrup", tasty : true }
...          ]
...             }
... })
> db.countries.insert({ _id : "mx",
... name : "Mexico",
... exports : {
...  foods : [
...   { name : "salsa", tasty : true, condiment : true }
...          ]
...           }
... })
> print( db.countries.count() )
3

Now if we need to select countries that export tasty bacon, we should $elemMatch in a query like:
> db.countries.find(
... {
...   'exports.foods' : {
...    $elemMatch : {
...      name : "bacon",
...      tasty : true
...     }
...   }
... },
... { _id : 0, name : 1 }
... )
{ "name" : "United States" }

If we didn’t used $elemMatch and wrote a query like the following, it will return countries that export bacon or tasty food, not tasty bacon
> db.countries.find(
... { 'exports.foods.name' : 'bacon' , 'exports.foods.tasty' : true },
... { _id : 0, name : 1 }
... )
{ "name" : "United States" }
{ "name" : "Canada" }

  • Boolean Operators

$or a prefix for criteria to return document that match either condition1 or condition2. There is a a lot of operators you can use.
> db.countries.find({
... $or : [ { _id : "mx" } , { name : "United States" } ] },
... {_id : 1} )
{ "_id" : "us" }
{ "_id" : "mx" }

Update

The find() function toke two parameters, criteria and list of fields to return. The update() function works the same way. The first parameter is a criteria (the same way you will use to retreive the document through find()). The second parameter is an object whose fields will replace the matched document(s) or a modifier operation ($set to set a field value, $unset to delete a field, $inc to increment a field value by a number).
The following query will set the field state with the string OR for the matching document.
db.towns.update( { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { $set : { "state" : "OR" } } )
but the following query will replace the matching document with a new document { state : “OR”}
db.towns.update( { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { state : "OR" } )

References

Although MongoDB is schema-less but you can make one document reference another document using a construct like { $ref : “collection_name”, $id : “reference_id” }. In the following query we linking New York town with the country US. Notice the display of the new country field in the New York town document.
> db.towns.update(
... { _id : ObjectId("51def56c1cf66f4c40bb7f4a") },
... { $set : { country : { $ref : "countries", $id : "us" } } }
... )
> db.towns.find( { _id : ObjectId("51def56c1cf66f4c40bb7f4a") } )
{ "_id" : ObjectId("51def56c1cf66f4c40bb7f4a"), "country" : DBRef("countries", "us"), "famous_for" : [  "statue of liberty",  "food" ], "last_census" : ISODate("2009-07-31T00:00:00Z"), "mayor" : { "na
me" : "Michael Bloomberg", "party" : "I" }, "name" : "New York", "population" : 22200000 }
Now we can retrieve New york from towns collection, then use it to retrieve its country
> var NY = db.towns.findOne( { _id : ObjectId("51def56c1cf66f4c40bb7f4a") } )
> db.countries.findOne( { _id : NY.country.$id })
{
        "_id" : "us",
        "name" : "United States",
        "exports" : {
                "foods" : [
                        {
                                "name" : "bacon",
                                "tasty" : true
                        },
                        {
                                "name" : "burgers"
                        }
                ]
        }
}

or in a different way > db[ NY.country.$ref ].findOne( { _id : NY.country.$id} )

Delete

Removing documents from a collections is simple, use your criteria with a call to remove() function and all matching documents will be removed. It’s a recommended practice to build your criteria in an object, use that criteria to ensure that matching documents are the expected ones, pass this criteria to remove() function.
> var bad_bacon = { 'exports.foods' : {
... $elemMatch : { name : 'bacon', tasty : false }
... } }
> db.countries.find ( bad_bacon)
{ "_id" : "ca", "name" : "Canada", "exports" : { "foods" : [    {       "name" : "bacon",       "tasty" : false },      {       "name" : "syrup",       "tasty" : true } ] } }
> db.countries.remove( bad_bacon)
> db.countries.count()
2

Reading with Code

You could ask MongoDb to run a decision function across documents.
db.towns.find(  function() {
return this.population > 6000 && this.population < 600000;
} )
or in a short-hand format db.towns.find("this.population > 6000 && this.population < 600000")
or combine custom code with other criteria using $where
db.towns.find( {
$where : "this.population > 6000 && this.population < 600000",
famous_for : /groundhog/
} )
Custom code should be your last resort due to the following:
  1. Custom code queries run slower than regular queries.
  2. Custom code queries can’t be indexed.
  3. MongoDb can’t optimize custom code queries.
  4. If your custom code assume the existence of a particular field and this field is missing in a single, the entire query will fail.
In this post we explored the basics of MongoDB, a rising star in the NoSQL family and the most common document database. We saw how to install and configure it. We saw how we can store nested structured data as JSON objects and query that data at any depth. We How we can update and delete data. In the next post we going to dig deep into MongoDB.

Tuesday, July 9, 2013

Getting Started with Apache Cassandra

Apache Cassandra is “an open source, distributed, decentralized, elastically scalable, highly available, fault-tolerant, tuneably consistent, column-oriented database that bases its distribution design on Amazon’s Dynamo and its data model on Google’s Bigtable” (source: “Cassandra: The Definitive Guide,” O’Reilly Media, 2010, p. 14).

Cassandra is built to store lots of data across a variety of machines arranged in a ring, in other words scaling horizontally, rather than vertically.

Data Model

Cassandra is based on a key-value model and it is organized according to the following concepts:

  • Column is a key-value pair.

Cassandra_DataModel_CheatSheet.pdf - Adobe Reader_2013-07-03_15-59-51

  • Column Family is a set of key-value pairs (columns in Cassandra’s terminology). They are sorted by their keys. Families are referenced and sorted by row keys.

Cassandra_DataModel_CheatSheet.pdf - Adobe Reader_2013-07-03_16-03-39

  • Super Column the value of a key-value pair can be a sequence of key-value pairs as well. In this case, the outer column would be called super column.

Cassandra_DataModel_CheatSheet.pdf - Adobe Reader_2013-07-03_16-39-29

  • Columns and Super Columns can equally be used within Column Families

Cassandra_DataModel_CheatSheet.pdf - Adobe Reader_2013-07-03_16-44-41

  • Columns or Super Columns are stored ordered by names within their Column Families

For a better understanding of Cassandra’s data model, refer to this article by Maxim Grinev.

 

Installation

  1. Download the latest Cassandra version from here (I got version 1.2.6).
  2. Extract the archive (I extracted it to C:\apache-cassandra-1.2.6 )
  3. If you don’t have Java installed on your machine, go and get it installed.
  4. Add environment variables
    1. Reight-click the my Computer icon on your desktop or start menu.
    2. Click the Advanced tab (or the Advanced System Settings)
    3. Under System Variables, click New (adjust for your own directories)
      1. Variable Name :  CASSANDRA_HOME
      2. Variable Value : C:\apache-cassandra-1.2.6
      3. click OK
    4. Under System Variables, click New (adjust for your own directories)
      1. Variable Name : JAVA_HOME
      2. Variable Value : C:\Program Files\Java\jre7
      3. click OK
  5. Now, open command window and navigate to your bin directory inside Cassandra directory (C:\apache-cassandra-1.2.6\bin in my case).
  6. Launch Cassandra by executing the comand cassandra –f (the “-f” causes it to run in the foreground). You will see lots of messages coming out. If everything goes fine, it will end up with something like this:

CWindowssystem32cmd.exe - cassandra  -f_2013-07-03_11-18-19

Now we have a running Cassandra server is expecting incoming connections on port 9160.

Once Cassandra is up and running on your machine, we can connect to the running instance using the Cassandra command-line interface, launched by running “cassandra-cli.bat”, from the Cassandra “bin” directory.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-03_12-16-36

Commads

  • show api version; to show the current api version.
  • describe cluster; to show a description of the current cluster.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_12-59-46

  • create keyspace TestKS; to create a keyspace and it have to be a unique name.
  • use TestKS; to switch to keyspace TestKS.
  • create column family TestCF; to create a column family TestCF within the current keyspace.
    • No other schema definition is required, the column family is a collection of name/value pairs.
  • set TestCF[ascii('TestKey')][ascii('column1')]=ascii('TestValue'); to insert the TestKey/TestValue key/value pair into the column named column1 within the column family TestCF. You can use the with ttl = x setting at the end of the set command to make the column self-delete aft x seconds of the insertion time.
    • by default Cassandra treats data as byte arrays but you can convert to other types such as Long, int, integer,…. Also timeuuid() generates new UUID. For full information about set command: type help set;
  • get TestCF[ascii("TestKey")]; to retrieve the value stored in the key TestKey within the column family TestCF

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_11-15-32

  • del TestCF[ascii(‘TestKey’)][ascii(‘column2’)]; rows and columns can be deleted by specifying the row key and/or the column name
    with the del (delete) command.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_15-29-44

  • list TestCF; list the data inside a column family

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_16-05-31

  • drop column family TestCF; removes a column family.
  • drop keyspace TestKS; removes a key space.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_16-25-05

==> You can insert to super columns much like inserting to normal columns. They can be read with get, written with set, and deleted with del. The super column version of these commands uses an extra ['xxx'] to represent the extra sub-index level.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_17-07-44

  • assume TestCF comparator as ascii; it decodes and helps display results of get and list requests inside the command-line interface. It can be used in the same way to set the validator and keys. By default, columns with no metadata are displayed in a hex format. This is done because row keys, column names, and column values are byte arrays. After using assume the column and value will be displayed rather than the hex code.

CWindowssystem32cmd.exe - cassandra-cli.bat_2013-07-05_17-29-07

  • Type Enforcement : Cassandra is designed to store and retrieve simple byte arrays but it also have support for built-in types. When creating or updating a column family, the user can supply column metadata that instructs the CLI Cassandra client on how to display data and help the server enforce types during insertion operations.
    create column family User with comparator = UTF8Type;
    update column family User with
            column_metadata =
            [
            {column_name: first, validation_class: AsciiType},
            {column_name: last, validation_class: AsciiType},
            {column_name: age, validation_class: IntegerType, index_type: KEYS}
            ];
  • Querying data :

set User[ascii('jsmith')][ascii('first')] = ascii('John');
set User[ascii('jsmith')][ascii('last')] = ascii('Smith');
set User[ascii('jsmith')][ascii('age')] = '38';

get User where age = '38';

  • Update is the same as set.

set User[ascii('jsmith')][ascii('first')] = ascii('Jack');

  • Quite the client;

quit;

 

In this post we just touched the Cassandra’s iceberg. In later posts we will dig more in it and how to write .net programs against it.

Wednesday, July 3, 2013

Redis 101– Part 2

We introduced many of the Redis’s fundamental concepts and commands in the last post. In this post we going to introduce some advanced features.

Publish-Subscribe

Previously we queued data that could be read by a blocking pop command. Using that queue, we made a very basic publish-subscribe model. Any number of messages could be pushed to this queue, and a single queue reader would pop messages as they were available. This is powerful but limited. Redis provides some specialized publish-subscribe (or pub-sub) commands.

  • SUBSCRIBE subscribe a client to a key, known as a channel in pub-sub terminology; client will block until messages are available.

Redis Client_2013-07-01_16-25-11

  • PUBLISH will push message into the channel returning how many subscribers have received it
    • Publisher window Redis Client_2013-07-01_16-27-41
    • Subscriber window receives : the string “message”, the channel name, the published message value Redis Client_2013-07-01_16-28-53
  • UNSUBSCRIBE unsubscribe or disconnect from the specified channel. If no channel name provided, it will disconnect from all channels.

Server Information

  • INFO returns a list of server data, including version, process ID, memory used, and uptime.

In order to change any of the Redis’s default configurations, you need to edit redis.config file at C:\Program Files\Redis\conf . It is fairly self-explanatory.

Durability

Redis has a few persistence options:

  • No persistence at all, which simply keeps all values in main memory.
  • Forced save
    • Use command SAVE to force server to save database to disk.
    • Use command BGSAVE to force server to save database to disk asynchronously in the background.
    • Use command LASTSAVE to get a timestamp of the last time a Redis disk write succeeded (also provided through the last_save_time field in the server INFO output).

Snapshotting

By default Redis saves snapshots of the dataset on disk, in a binary file called dump.rdb. You can configure Redis to have it save the dataset every N seconds if there are at least M changes in the dataset, or you can manually force it by calling the SAVE or BGSAVE commands as we said before. For example, the following configuration will make Redis automatically dump the dataset to disk every 60 seconds if at least 1000 keys changed (This strategy is known as snapshotting.):

save 60 1000 

 

Append-only file

Snapshotting is not very durable. If your computer running Redis stops, the latest data written on Redis will get lost. While this may not be a big deal for some applications, there are use cases for full durability, and in these cases Redis was not a viable option.

The append-only file is an alternative, fully-durable strategy for Redis. It became available in version 1.1. You can turn on the AOF in your configuration file:

appendonly yes

Then we must decide how often a command is appended to the file. Setting always is the more durable, since every command is saved. By default everysec is enabled, which saves up and writes commands only once a second. This is a decent trade-off, since it’s fast enough, and worst case you’ll lose only the last one second of data. Finally, no is an option, which just lets the OS handle flushing. It can be fairly infrequent, and not recommended.

appendfsync always

# appendfsync everysec

# appendfsync no

From now on, every time Redis receives a command that changes the dataset (e.g. SET) it will append it to the AOF. When you restart Redis it will re-play the AOF to rebuild the state. Append-only has more detailed parameters, you could refer to the online documentation for more details.

 

Security

Redis provides command-level security through obscurity, by allowing you to hide or suppress commands. This will rename the FLUSHALL command (remove all keys from the system) into some hard-to-guess value like c283d93ac9528f986023793b411e4ba2:

rename-command FLUSHALL c283d93ac9528f986023793b411e4ba2

If we attempt to execute FLUSHALL against this server, we’ll be hit with an error. We can also disable the command entirely by setting it to a blank string.

rename-command FLUSHALL ""

You can set any number of commands to a blank string to allow only a customized subset of the Redis’s commands.

Benchmarking

Redis provides an excellent benchmarking tool. It connects locally to port 6379 by default and issues 10,000 requests using 50 parallel clients. Tool test many commands and have a long output report. Tool is in C:\Program Files\Redis\

CWindowssystem32cmd.exe_2013-07-02_09-15-52

Replication

Redis supports master-slave replication. One server is the master by default if you don’t set it as a slave of anything. Data will be replicated to any number of slave servers. Configuring master-slave setting is the same as running another instance with the slaveof option in the salve’s conf file set to the master IP and port. Refer to our previous post for more details.

This concludes out Redis 101 tutorial, we tried to touch all basic and moderate features in it. Later we going to show how to write C# programs against Redis.

Tuesday, July 2, 2013

Running multiple Redis instances on the same server

Redis runs as a background process that listens to a specific port (6379 by default) for incoming requests from clients. Running multiple instances requires a separate conf file and a new init script. The conf file specifies details for the new instance, and the init script handles starting/stopping of the instance background process.

  1. Make a copy of the redis.conf file at C:\Program Files\Redis\Conf and give it a new name redis-s1.conf. Leave both files in the same directory C:\Program Files\Redis\Conf
  2. Open the redis-s1.conf with your favorite text editor (e.g. notepad ) and change the following:
    1. PID File
      • pidfile /var/run/redis-s1.pid
    2. Port
      • port 6380
    3. Log File
      • logfile "C:/Program Files/Redis/logs/redis-s1.log
    4. Data Directory (don’t forget to create that directory)
      • dir "C:/Program Files/Redis/data2"
    5. For replication only : If you planning to use this instance as a slave in master-slave replication setting (where 127.0.0.1 is the master instance IP, 6379 in the master instance port)
      • slaveof 127.0.0.1 6379
  3. To start the new instance
    1. Open a new command window
    2. Navigate to C:\Program Files\Redis
    3. Run redis-server conf\redis-s1.conf
  4. To connect to the new instance
    1. Open a new command window
    2. Navigate to C:\Program Files\Redis
    3. Run redis-cli –h 127.0.0.1 –p 6380

Now you can have multiple Redis instances on the same server like any other DBMS.

Wednesday, June 26, 2013

Redis 101

Redis (REmote DIctionary Service) is an open-source networked in-memory key-value data store. First released in 2009, currently sponsored by VMware, and since then it have been ranked the most popular key-value store by DB-Engine. Redis creator Salvatore Sanfilippo refers to his project as a “data structure server” to capture its unique handling of complex data types and other features. Interested ? Enough talking, let’s get down to business.

Download Redis installer from here.

Installation is straight forward:

Setup - Redis_2013-06-27_11-42-13

Setup - Redis_2013-06-27_11-42-21

Setup - Redis_2013-06-27_11-42-25

Setup - Redis_2013-06-27_11-42-29

Setup - Redis_2013-06-27_11-42-32

Setup - Redis_2013-06-27_11-42-36

Setup - Redis_2013-06-27_11-45-05

Start the Server go to Control Panel >> Administrative Tools >> Services , right click Redis Server and select Start.

Services_2013-06-27_11-49-50

Start the Client look for Redis Client (with red cube icon) in your start menu and open it. As you see it listens to port 6379 by default. You could ping your server by writing PING, if everything is good you will get PONG in return.

Redis Client_2013-06-27_11-56-46

GET & SET

As we said before, Redis is a key-value store. We store value inside a key and retrieve it later using that key. We can use the command SET to store the value "fido" at key "server:name" (server replies with Ok if everything is good) (Note that we used colons [:] within our key. This is a valid character that often logically separates a key into segments. It’s merely a matter of convention, with no deeper meaning in Redis.)

 SET server:name "fido"
To retrieve our value, we use GET command (server replies with “fido”)
 GET server:name 
You could also delete a given key and its associated value using DEL command and you can check for key existence using EXISTS (1 exists, 0 otherwise). We get and set multiple values in one command using MSET & MGET
 MSET 1 valueA 2 valueB
 MGET 1 2

Although Redis stores strings, it recognizes integers and provides some simple operations for them. If we want to keep a running total of something, we can create a count and then increment it with the INCR command (gives error for noninteger values).

 SET count 2
 INCR count
 GET count
Redis Client_2013-06-27_13-01-40

You can also increment by any integer (INCRBY) or decrement (DECR, DECRBY).


Redis Client_2013-06-27_13-04-08


It is good to know that these simple operations are atomic in Radis.


Expire


You can tell Radis to hold a key only for a certain period of time (in seconds) using EXPIRE command. You can test how long a key will exist for with the TTL command. It returns the number of seconds until it will be deleted (-1 means the key will never expire). All keys are permanent by default.


Redis Client_2013-06-27_13-40-01 


Setting and expiring keys is so common that Redis provides a shortcut command called SETEX. Also at any moment before the key expires, you can remove the timeout by running PERSIST KeyName.


A common trick for keeping only recently used keys is to update the expire time whenever you retrieve a value. This will ensure that your most recently used keys will remain in Redis, while the least recently used keys will just expire as normal.


Transactions


Redis like most DBMSs supports transactions. We begin the transaction with the MULTI command and execute it with EXEC command. So, wrapping two operations like SET and INCR in a single block will complete either successfully or not at all.


Redis Client_2013-06-27_13-17-56


As you may guess from the server response inside the transaction, commands are not executed instantly. Instead, they are queued and then executed in sequence.


Similar to ROLLBACK in SQL, you can stop a transaction with the DISCARD command, which will clear the transaction queue. Unlike ROLLBACK, it won’t revert the database; it will simply not run the transaction at all. The effect is identical, although the underlying concept is a different mechanism (transaction rollback vs. operation cancellation).


Complex Data Types


Radis can store lists, hashes, sets, and sorted sets natively. These collection data types can contain a huge number of values (up to 2^32 elements or more than 4 billion) per key. That’s more than enough for all Facebook accounts to live as a list under a single key.


List


A list is a series of ordered values that can act both as queues (first value in, first value out) and as stacks (last value in, first value out). Some of the important commands for interacting with lists are RPUSH, LPUSH, LLEN, LRANGE, LPOP, and RPOP. You can immediately begin working with a key as a list, as long as it doesn't already exist as a different type.



  • RPUSH puts the new value at the end (right) of the list.

  • LPUSH puts the new value at the start (left) of the list.

Redis 101 - Windows Live Writer_2013-06-27_14-42-33



  • LRANGE gives a subset of the list. It takes the index of the first element you want to retrieve as its first parameter and the index of the last element you want to retrieve as its second parameter. A value of -1 for the second parameter means to retrieve all elements in the list.

Redis Client_2013-06-27_14-46-47



  • LLEN returns the current length of the list.

  • LPOP removes the first (left) element from the list and returns it.

  • RPOP removes the last (right) element from the list and returns it.


    • Use LPUSH/RPOP to make List act like a queue.

    • Use LPUSH/LPOP to make List act like a stack.

  • LREM removes (some or all) matching elements from the list. It also requires a number to know how many matches to remove. Setting the count to 0 as we do here just removes them all. Setting the count greater than 0 will remove only that number of matches, and setting the count to a negative number will remove that number of
    matches but scan the list from the end (right side).

Redis Client_2013-06-27_14-53-25



  • RPOPLPUSH a single command for popping values from the tail of one
    list (right pop) and pushing to the head of another (left push). Surprisingly, there is no commands for RPOPRPUSH, LPOPLPUSH, or LPOPRPUSH.

Blocking Lists


It really interesting to know that Redis provides message queuing APIs natively. So, assume you want to write a simple messaging system where multiple clients can push data to one side of the queue and one or more listeners pop data from the other side of the queue. Listeners should just listen for new data and pop them as they arrive.



  • BRPOP the blocking version of RPOP, it blocks the connection when there are no elements to pop from any of the given lists.

Redis Client_2013-06-28_11-04-25


now open another Redis client window and push a message into messages list


Redis Client_2013-06-28_11-04-53


if you switched back to the blocking BRPOP command window, you will find it return the key, the popped value, and the time spent blocking


Redis Client_2013-06-28_11-05-11



  • BLPOP the blocking version of left pop LPOP

  • BRPOPLPUSH the blocking version of right pop left push BRPOPLPUSH

Set


Sets are unordered collections with no duplicate values and are an excellent choice for performing complex operations between two or more key values, such as unions or intersections.



  • SADD adds the given value to the set (duplication is not allowed).

  • SREM removes the given value from the set.

Redis Client_2013-06-27_16-41-05



  • SISMEMBER tests if the given value is in the set.

  • SMEMBERS returns a list of all the members of this set.

  • SUNION combines two or more sets and returns the list of all elements.

  • SINTER returns the intersection of two or more lists.

  • SDIFF returns the elements that exist in the first list only but not in the second list.

Redis 101 - Windows Live Writer_2013-06-27_17-00-54



  • SUNIONSTORE / SIMTERUNION / SDIFF stores the result of operation done on the second and third parameters into the first parameter.

Redis Client_2013-06-27_17-09-00



  • SMOVE moves one element from one set to another.

  • SCARD counts the set elements (set cardinality).

Redis Client_2013-06-27_17-21-00



  • SPOP returns a random element from a set and removes it.

  • SRANDMEMBER returns a random element from a set but does not remove it

Sorted Sets


Sorted set is similar to a regular set, but now each value has an associated score. This score is used to sort the elements in the set. Sorted sets take something from each of the other collections data types. They are ordered like lists and are unique like sets. They have field-value pairs like hashes, but rather than string fields, they are instead numeric scores that denote the order of the values. Internally, sorted sets keep values in order, so inserts can take log(N) time to insert (where N is the size of the set), rather than the constant time complexity of hashes or lists.



  • ZADD add a value with a specified score.

  • ZRANGE returns the elements between start index (zero-based) and end index (-1 means to the end of the sorted set. Put WITHSCORES at the end of command to get elements and scores, not only the elements. To get them in reverse, insert the word REV, as in ZREVRANGE.

Redis Client_2013-06-28_08-51-02



  • ZRANGEBYSCORE since we are using a sorted set, it’s more likely to get elements by score ranges instead of index ranges (x <= score <= y). We can make a score number exclusive by prefixing it with an opening parenthesis ( x < score <= y).

Redis Client_2013-06-28_11-49-34



  • -inf / inf negative / positive infinities. can be used in retrieval (by index or by score). We can also range by both positive and negative values.

  • ZREMRANGEBYRANK / ZREMRANGEBYSCORE remove values by rank or score.

  • ZINCRBY Increments the score of member in the sorted set stored at key by increment. If member does not exist in the sorted set, it is added with increment as its score (as if its previous score was 0.0). If key does not exist, a new sorted set with the specified member as its sole member is created. It is possible to provide a negative value to decrement the score.

Redis Client_2013-06-28_08-58-48



  • ZUNIONSTORE Computes the union of numkeys sorted sets given by the specified keys, and stores the result in destination set. It is mandatory to provide the number of input keys (numkeys) before passing the input keys and the other (optional) arguments. Parameters needed:


    • destination ==> a set to hold the union operation result

    • numkeys ==> number of keys you are going to join.

    • weight ==> optional number(s) to multiply each score of the relative key by (if you have two keys, you can have two weights and son on)

    • aggregate ==> optional rule to specify how the results of the union are aggregated. This option defaults to SUM, where the score of an element is summed across the inputs where it exists. When this option is set to either MIN or MAX, the resulting set will contain the minimum or maximum score of an element across the inputs where it exists.

Redis 101 - Windows Live Writer_2013-06-28_12-29-14


In the above example we prepared two sorted sets, sset1 with elements (1 “one” 2 “two”) , sset2 with elements (1 “one” 2 “two” 3 “three”). Then tell Redis to merge these two sets with the following parameters:




    • store the operation result in sorted set named out

    • we going to merge 2 sorted sets, then provide them sset1 sset2.

    • we going to use WEIGHTS, then provide these weights 2 3.

Hash


Hash is a collections object that can hold any number of key-value pairs.



  • HMSET store the key-value pairs into the hash (first parameter.

  • HVALS returns the values of a hash.

  • HKEYS returns the keys of a hash.

  • HGET returns the value of of a single key in a hash.

Redis Client_2013-06-28_10-09-24



  • HDEL deletes a key-value pair from a hash.

  • HSET updates or adds a value at a specified key in a hash.

  • HINCRBY increments an integer field value by some count in a hash. It is possible to provide a negative value to decrement.

  • HLEN returns the number of fields in a hash.

Redis Client_2013-06-28_10-17-28





Database Namespaces


You may have been wondering, which database we working on. So far, we have interacted only with a single namespace. In Redis’s terminology, a namespace is called a database and is keyed by a number. So far, we’ve always interacted with the default namespace 0 (also known as database 0).


  • Use SELECT command to switch to different database and provide it a DB number. Setting a value to a key in one database will not affect any other key in other databases.

clip_image001



  • Since all databases are running in the same server instance, Redis lets us move keys from one database to another with the MOVE command.

clip_image002


This can be useful for running different applications against a single Redis server but still allow these multiple applications to trade data between each other.

More commands



  • RENAME rename a key

  • TYPE return the type of a key’s value

  • DEL deletes a key-value pair

  • FLUSHDB removes all keys in database

  • FLUSHALL removes all keys from all databases on this server instance.

Redis’s data types and the complex queries it can perform make it much more than a standard key-value store. It can act as a stack, queue, or priority queue; can be an object store (via hashes); and even can perform complex set operations such as unions, intersections, and subtractions (diff). It provides many atomic commands, and for those multistep commands, it provides a transaction mechanism. It has a built-in ability to expire keys, which is useful as a cache.




In this post we just introduced Redis as a data structure server. In later post we going to cover the advanced features of Redis.