Search This Blog

2018/09/30

Express.js : Mongo as session store


    Inside app.js from express.js

    just after

        var express = require('express');
    add
        var session = require('express-session');
        const mongoose = require('mongoose');
        const MongoStore = require('connect-mongo')(session);

        mongoose.connect('mongodb://localhost/sessionstore', {
           
        });

        mongoose.Promise = global.Promise;
        const db = mongoose.connection
    then at first app.use add
        app.use(session({
          secret: 'my-secret',
          resave: false,
          saveUninitialized: true,
          store: new MongoStore({ mongooseConnection: db })
        }));

    inside some route try to save session

        req.session.city = 'Mumbai';
      req.session.username = 'sangram';

    inside another route try to retreive saved value

              console.log(req.session.city)
       console.log(req.session.username)

    to check data is saved in mongo ,connect to mongo and switch database and check

    > show collections
    sessions

    > db.sessions.find().pretty()
    {
        "_id" : "yAuqRI1gIxFOHUbxpxjGasiK61ZvrWT-",
        "session" : "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"city\":\"Mumbai\",\"username\":\"sagar\"}",
        "expires" : ISODate("2018-10-14T05:32:38.869Z")
    }

Code of article can be viewed at https://github.com/gitsangramdesai/mongo-sessionstore-express

2018/09/23

Node.js:Global objects

Global objects in node.js are accessible in all module without need to include them.those can be
modules, functions, strings and object.

a) __filename:absolute path of the current module file

b) _dirname:absolute path of the dir in which executable resides

    for test.js inside /home/sangram/workspace/node/global folder.
        console.log(__filename);
        console.log(__dirname);


    sangram@sangram-HP-Laptop-15-bs0xx:~/workspace/node/global$ node test.js
    /home/sangram/workspace/node/global/test.js
    /home/sangram/workspace/node/global

c) setTimeout(cb, ms):its a global function used to run callback after at least said milliseconds.A timer cannot span more than 24.8 days

    inside our test.js
        console.log(__filename);
        console.log(__dirname);

        function Hello(){
               console.log( "Hello, World!");
            }

        setTimeout(Hello, 5000);
        console.log("Reached End of File");

    running test.js
        sangram@sangram-HP-Laptop-15-bs0xx:~/workspace/node/global$ node test.js
        /home/sangram/workspace/node/global/test.js
        /home/sangram/workspace/node/global
        Reached End of File
        Hello, World!

d) clearTimeout(t): global function used to stop a timer that was previously created with setTimeout()

        inside test.js

        console.log(__filename);
        console.log(__dirname);

        function Hello(){
               console.log( "Hello, World!");
            }

        timer = setTimeout(Hello, 5000);
        clearTimeout(timer)

        console.log("Reached End of File");

    running test.js

        sangram@sangram-HP-Laptop-15-bs0xx:~/workspace/node/global$ node test.js
        /home/sangram/workspace/node/global/test.js
        /home/sangram/workspace/node/global
        Reached End of File

    we can see "Hello World!" is not printed on console as settimeout timer is cleared in.

e) setInterval:global function used to run callback cb repeatedly after at least ms milliseconds.timer cannot span more than 24.8 days.
   
    inside test.js
        console.log(__filename);
        console.log(__dirname);

        function Hello(){
               console.log( "Hello, World!");
            }

        //timer = setTimeout(Hello, 1);
        //clearTimeout(timer)

        setInterval(Hello,2000)
        console.log("Reached End of File");

    running test.js
        sangram@sangram-HP-Laptop-15-bs0xx:~/workspace/node/global$ node test.js
        /home/sangram/workspace/node/global/test.js
        /home/sangram/workspace/node/global
        Reached End of File
        Hello, World!
        Hello, World!
        Hello, World!
        Hello, World!
        ^C
f) clearInterval: global function used to stop a timer that was previously created with setInterval()

    inside test.js
        console.log(__filename);
        console.log(__dirname);

        function Hello(){
               console.log( "Hello, World!");
            }

        //timer = setTimeout(Hello, 1);
        //clearTimeout(timer)


        timer2 = setInterval(Hello,2000)
        clearInterval(timer2)

        console.log("Reached End of File");

    running test.js
        sangram@sangram-HP-Laptop-15-bs0xx:~/workspace/node/global$ node test.js
        /home/sangram/workspace/node/global/test.js
        /home/sangram/workspace/node/global
        Reached End of File

    no "Hello World!" is printed on console.
 
Global Objects:
    Console, Process & Buffer are commonly used global objects in node.js.Console used to print message on stdout and stderr.Process used to get information on current process.
    Console has three methods console.log(),console.error() & console.warn() for printing output.

Global modules:

    a) OS:
        Provides basic operating-system related utility functions.
     b) Path:
        Provides utilities for handling and transforming file paths.
    c) Net :
        Provides both servers and clients as streams. Acts as a network wrapper.
     d) DNS :
        Provides functions to do actual DNS lookup as well as to use underlying operating system name resolution functionalities.
    e)Domain:
        Provides ways to handle multiple different I/O operations as a single group.

Exploring redis commands - part 2

Redis Lists are simply lists of strings, sorted by insertion order. You can add elements in Redis lists in the head or the tail of the list.
Max length of list is (2^32 - 1).

    127.0.0.1:6379> auth sangram
    OK

    LPUSH creates list where last pushed element is at index 0.

        127.0.0.1:6379> LPUSH subjects english
        (integer) 1
        127.0.0.1:6379> LPUSH subjects marathi
        (integer) 2
        127.0.0.1:6379> LPUSH subjects hindi
        (integer) 3
        127.0.0.1:6379> LPUSH subjects history
        (integer) 4
        127.0.0.1:6379> LPUSH subjects geography
        (integer) 5
        127.0.0.1:6379> LPUSH subjects economics
        (integer) 6
        127.0.0.1:6379> LPUSH subjects maths
        (integer) 7
        127.0.0.1:6379> LPUSH subjects science
        (integer) 8
        127.0.0.1:6379> LLEN subjects
        (integer) 8


    LINDEX get element at specified index in list.

        127.0.0.1:6379> LINDEX subjects 0
        "science"
        127.0.0.1:6379> LINDEX subjects 1
        "maths"
        127.0.0.1:6379> LINDEX subjects 3
        "geography"

    BLPOP command removes the first element in a list it returns the first element, if available, or blocks the client for specific time to execute any command.
        127.0.0.1:6379> BLPOP subjects 100
        1) "subjects"
        2) "science"
        127.0.0.1:6379> LLEN subjects
        (integer) 7

    BRPOP removes element from bottom of list.

        127.0.0.1:6379> BRPOP subjects 100
        1) "subjects"
        2) "english"
        127.0.0.1:6379> LLEN subjects
        (integer) 6

    LRANGE prints  values in list based on start Index & end Index

        127.0.0.1:6379> LRANGE subjects 0 20
         1) "mathsssds"
         2) "mathss"
         3) "maths"
         4) "maths"
         5) "maths"
         6) "english"
         7) "maths"
         8) "economics"
         9) "geography"
        10) "history"
        11) "hindi"
        12) "marathi"

    LPUSHX insert push at head of list only if list is already defined.Here "subjectss" is not defined to insertion fails at 1st attempts then succeed.
        127.0.0.1:6379> LPUSHX subjectss mathsssds
        (integer) 0
        127.0.0.1:6379> LPUSH subjectss mathsssds
        (integer) 1
        127.0.0.1:6379> LPUSHX subjectss mathsssds
        (integer) 2

        RPUSH -insert at bottom of list
        127.0.0.1:6379> RPUSH subjects geometry
        (integer) 13
        127.0.0.1:6379> LRANGE subjects 0 20
         1) "mathsssds"
         2) "mathss"
         3) "maths"
         4) "maths"
         5) "maths"
         6) "english"
         7) "maths"
         8) "economics"
         9) "geography"
        10) "history"
        11) "hindi"
        12) "marathi"
        13) "geometry"

      below LREM removes 2 occurances "maths" from list starting search from top to bottom.if instead 2 its negative value say -2 them search start from bottom to top,if instead of 2 its 0 then remove     all occurances of search string in list.


        127.0.0.1:6379> LRANGE subjects 0 20
         1) "mathsssds"
         2) "mathss"
         3) "maths"
         4) "maths"
         5) "maths"
         6) "english"
         7) "maths"
         8) "economics"
         9) "geography"
        10) "history"
        11) "hindi"
        12) "marathi"
        13) "geometry"
        127.0.0.1:6379> LREM subjects 2 maths
        (integer) 2
        127.0.0.1:6379> LRANGE subjects 0 20
         1) "mathsssds"
         2) "mathss"
         3) "maths"
         4) "english"
         5) "maths"
         6) "economics"
         7) "geography"
         8) "history"
         9) "hindi"
        10) "marathi"
        11) "geometry"

    RPUSHX command inserts the value at the bottom of the list  only if the list already exists

        127.0.0.1:6379> RPUSHX s math
        (integer) 0
        127.0.0.1:6379> RPUSHX subject math
        (integer) 0
        127.0.0.1:6379> RPUSHX subjects math
        (integer) 8

       RPOP removes and return last element from bottom in list

    127.0.0.1:6379> LRANGE subjects 0 20
    1) "english"
    2) "economics"
    3) "geography"
    4) "history"
    5) "hindi"
    6) "marathi"
    7) "geometry"
    8) "math"

    127.0.0.1:6379> RPOP subjects
    "math"

    127.0.0.1:6379> LRANGE subjects 0 20
    1) "english"
    2) "economics"
    3) "geography"
    4) "history"
    5) "hindi"
    6) "marathi"
    7) "geometry"

    LPOP removes and returns the first element in top list.

        127.0.0.1:6379> LRANGE subjects 0 20
        1) "english"
        2) "economics"
        3) "geography"
        4) "history"
        5) "hindi"
        6) "marathi"
        7) "geometry"
        127.0.0.1:6379> LPOP subjects
        "english"

    RPOPLPUSH -removes element from bottom of list and insert into new list


    127.0.0.1:6379> LRANGE subjects 0 20
    1) "economics"
    2) "geography"
    3) "history"
    4) "hindi"
    5) "marathi"
    6) "geometry"

    127.0.0.1:6379> RPOPLPUSH subjects s
    "geometry"

    127.0.0.1:6379> LRANGE subjects 0 20
    1) "economics"
    2) "geography"
    3) "history"
    4) "hindi"
    5) "marathi

    127.0.0.1:6379> LRANGE s 0 20
    1) "geometry"

2018/09/22

Exploring redis commands - part 1

The first thing to do in order to check if Redis is working properly is sending a PING command using redis-cli:

    127.0.0.1:6379> ping
    PONG

saving key & retreving saved value

    127.0.0.1:6379> set mykey myvalue
    OK

    127.0.0.1:6379> get mykey
    "myvalue"
    127.0.0.1:6379>

setting password
    open "/etc/redis/redis.conf" and look for "requirepass" commented line,uncomment it set password as
    requirepass sangram

    then we need to restart service

         sudo service redis-server restart .now we need to open redis-cli again
on redis-cli we will check password is enforced or not,lets try to set some random key value as follows

    127.0.0.1:6379> set mykey test
    (error) NOAUTH Authentication required.

Now we will keyin password as follows
    127.0.0.1:6379> auth sangram
    OK
after getting authenticated we are able to save key

    127.0.0.1:6379> set mykey test
    OK
Getting config value
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) "sangram"

Overrride key
    127.0.0.1:6379> set mykey test2
    OK
    127.0.0.1:6379> get mykey
    "test2"
Deleteing key:
    127.0.0.1:6379> DEL mykey
    (integer) 1
List keys by pattern:
    127.0.0.1:6379> set mykey1 test2
    OK
    127.0.0.1:6379> set mykey2 test2
    OK
    127.0.0.1:6379> KEYS my*
    1) "mykey2"
    2) "mykey"
    3) "mykey1

List All Keys:
    127.0.0.1:6379> KEYS *
    1) "mykey2"
    2) "mykey"
    3) "mykey1"

Expiry:
    Redis Expire command is used to set the expiry of a key. After the expiry time, the key will not be available in Redis.It returns 1, if timeout is set for the key else 0.

    127.0.0.1:6379>  EXPIRE mykey 10
    (integer) 1
     unit of expiry time is in seconds

    checking if key expired
    127.0.0.1:6379> KEYS *
    1) "mykey1"
    2) "mykey2"
    3) "\xe2\x80\x9ca-test\xe2\x80\x9d"
    4) "a-test"

     no occurance of mykey found.

     setting key in milli seconds

    127.0.0.1:6379> set mykey3 test3 PX 5000
    OK


we can also set expiry at the time of adding key

    127.0.0.1:6379> set mykey3 test3 EX 10
    OK

Check if key exist:returns 1 if key exist else 0.
    127.0.0.1:6379> EXISTS mykey3
    (integer) 0

    127.0.0.1:6379> EXISTS mykey1
    (integer) 1

Store Javascript objects in Redis

    127.0.0.1:6379> HSET "a-test" "name.first" "Kyle"
    (integer) 1
    127.0.0.1:6379> HSET "a-test" "name.family" "Davis"
    (integer) 1
    127.0.0.1:6379> HSET "a-test" "address" "123 Main Street"
    (integer) 1

    Get value for saved key:

        127.0.0.1:6379> HGET "a-test" "name.family"
        "Davis"
        127.0.0.1:6379> HGET "a-test" "address"
        "123 Main Street"

         Get value for saved keys:
        127.0.0.1:6379> HMGET "a-test" "name.first" "address"
        1) "Kyle"
        2) "123 Main Street"


     this is same as saving below json in javascript world

    var aTest = {
        "name.first" : ‘Kyle’,
        "name.family" : ‘Davis’,
        "address" : ‘123 Main Street’
      }

    Storing multiple key at in one go:

        127.0.0.1:6379> HMSET adrees city "mumbai" zip "400074"
        OK
        127.0.0.1:6379> HGET adrees city
        "mumbai"

    add key only if key does not exist do not override existing key value

        127.0.0.1:6379> HSETNX adrees city "pune"
        (integer) 0
        127.0.0.1:6379> HSETNX adrees state "maharshtra"
        (integer) 1

    confirming insertion:
        127.0.0.1:6379> HGETALL adrees
        1) "city"
        2) "mumbai"
        3) "zip"
        4) "400074"
        5) "state"
        6) "maharshtra"

       removing key:

        127.0.0.1:6379> HDEL "a-test" "name.family"
        (integer) 1

       view complete hash:

        127.0.0.1:6379> HGETALL "a-test"
        1) "name.first"
        2) "Kyle"
        3) "address"
        4) "123 Main Street"

    list all keys only

        127.0.0.1:6379> HKEYS "a-test"
        1) "name.first"
        2) "address"

    list all values:
        127.0.0.1:6379> HVALS a-test
        1) "Kyle"
        2) "123 Main Street"

    total keys:
        127.0.0.1:6379> HLEN "a-test"
        (integer) 2

    check key exist in hash:
        127.0.0.1:6379> HEXISTS "a-test"  address
        (integer) 1
        127.0.0.1:6379> HEXISTS "a-test"  addresss
        (integer) 0

  
Database:
    In Redis the number of Redis databases is fixed, and set in the configuration file. By default, you have 16 databases. Each database is identified by a number (not a name).

    You can use the following command to know the number of databases:

    127.0.0.1:6379> CONFIG GET databases
    1) "databases"
    2) "16"

       selecting database of specific number

        127.0.0.1:6379> select 1
        OK
        127.0.0.1:6379[1]> set mykey3 test3
        OK
        127.0.0.1:6379[1]> KEYS *
        1) "mykey3"

      on redis-cli switching between databases the key persist.


You can use the following command to list the databases for which some keys are defined:

    127.0.0.1:6379> INFO keyspace
    # Keyspace
    db0:keys=4,expires=0,avg_ttl=0

To get redis backup dir
    127.0.0.1:6379> CONFIG get dir
    1) "dir"
    2) "/var/lib/redis"

Redis SAVE command is used to create a backup of the current Redis database.

    127.0.0.1:6379> SAVE
    OK

    This command will create a dump.rdb file in your Redis directory.

    root@sangram-HP-Laptop-15-bs0xx:/home/sangram/workspace/node/hoisting# ls -lth /var/lib/redis/dump.rdb
    -rw-rw---- 1 redis redis 358 Sep 22 20:24 /var/lib/redis/dump.rdb

       Notice time file has been created.


    BGSAVE command will start the backup process and run this in the background.

    127.0.0.1:6379> BGSAVE
    Background saving started

To restore Redis data, move Redis backup file (dump.rdb) into your Redis backup directory and start the server.

Object.assign in javascipt

    
    Object.assign is used for merging object & cloning purpose.

    Merging Objects in JavaScript


        var o1 = { a: 1 };
        var o2 = { b: 2,a:5 };
        var o3 = { c: 3 };

       var obj = Object.assign(o1, o2, o3);

        console.log(obj); // { a: 5, b: 2, c: 3 }
        console.log(o1);  // { a: 5, b: 2, c: 3 }, target object itself is changed.
        console.log(o2); // {b: 2,a:5 }
        console.log(o3); // { c: 3 }

    Here we are merging o1,o2,o3 object during merge o1 also get merged object value along with returned object "obj". 'a' happens to be in o1 & o2 so in merged object a get value from last object  with a defined in it based on sequence in which they comes in Object.assign.

        Copying Object
              Though object.assign copies object deep clone is not happening.

        var obj2 = Object.assign({ p: 67 }, { q: 09 }, { r: 34 });
        console.log(obj2);

        let obj3 = {person: 'Thor Odinson',adr:{city:"mumbai"}};
        let clone = Object.assign({}, obj3);

        obj3.person ="test"
        obj3.adr.city="kolkota"

        console.log(clone);
        console.log(obj3);


         obj3 adr-->city in copied to "clone" by reference unlike key "person".so deep coping is not happening ,change in source adr-->city will modify in cloned object also.

        Deep Clone:
        let obj4 = {person: 'Thor Odinson',adr:{city:"mumbai"}};
        let deep_clone = JSON.parse(JSON.stringify(obj4))

        obj4.adr.city="pune"
        console.log(deep_clone);
        console.log(obj4);


    Deep clone is possible using JSON.parse(JSON.stringify()) call on object.

 Shallow Copy Example:

var employeeDetailsOriginal = {  name: 'Manjula', age: 25, Profession: 'Software Engineer' };
var employeeDetailsDuplicate = employeeDetailsOriginal; //Shallow copy!
employeeDetailsDuplicate.name = 'NameChanged';

console.log(employeeDetailsOriginal);

console.log(employeeDetailsDuplicate);

Output:
{ name: 'NameChanged', age: 25, Profession: 'Software Engineer' }
{ name: 'NameChanged', age: 25, Profession: 'Software Engineer' }


     Ellipses in Javascript:


           var obj33 =[{a:4,b:7},{p:6,q:5}]
           var obj5 = [... obj33];
           obj33[0].a =44
           console.log(obj5);
           console.log(obj33);


     object array copied using ellipses syntax also not deep clone.

   Copying property by looping:

    var person = {
            name: 'John',
            age: 28,
            adr:{state:"maha"}
    };

    var newPerson = new Object();
    for(prop in person){
      newPerson[prop] = person[prop];
    }

    newPerson.adr.state="karnataka"
    newPerson.age = 30
    console.log(newPerson === person)
    console.log(person)
 
   copying property by looping in object also do not deep clone.

2018/09/21

Quering Mongo


Example collection "maxdemo" is as follows:
{
"_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
"name" : "sangram",
"marks" : 76.0
}
{
"_id" : ObjectId("5ba49f73a3b0f1fce83f3f40"),
"name" : "sagar",
"marks" : 77.0
}
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0
}
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0
}
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0
}
{
"_id" : ObjectId("5ba49fa0a3b0f1fce83f3f44"),
"name" : "shashank",
"marks" : 79.0
}
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0
}
{
"_id" : ObjectId("5ba49fcaa3b0f1fce83f3f46"),
"name" : "sujay",
"marks" : 64.0
}
{
"_id" : ObjectId("5ba49fe4a3b0f1fce83f3f47"),
"name" : "subodh",
"marks" : 74.0
}
{
"_id" : ObjectId("5ba49ff6a3b0f1fce83f3f48"),
"name" : "sunit",
"marks" : 60.0
}




Finding 3rd max of marks

db.maxdemo.find({}).sort({"marks":-1}).skip(2).limit(1)

Finding nth max of marks
db.maxdemo.find({}).sort({"marks":-1}).skip(n-1).limit(1)


Updating our collection to below using

db.maxdemo.update({"name" : "vineet"},{$set: { "subject": "maths" }})

by changing subject & name field values.

{
"_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
"name" : "sangram",
"marks" : 76.0,
"subject" : "english"
}
{
"_id" : ObjectId("5ba49f73a3b0f1fce83f3f40"),
"name" : "sagar",
"marks" : 77.0,
"subject" : "marathi"
}
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
}
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0,
"subject" : "geography"
}
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0,
"subject" : "maths"
}
{
"_id" : ObjectId("5ba49fa0a3b0f1fce83f3f44"),
"name" : "shashank",
"marks" : 79.0,
"subject" : "maths"
}
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0,
"subject" : "geography"
}
{
"_id" : ObjectId("5ba49fcaa3b0f1fce83f3f46"),
"name" : "sujay",
"marks" : 64.0,
"subject" : "marathi"
}
{
"_id" : ObjectId("5ba49fe4a3b0f1fce83f3f47"),
"name" : "subodh",
"marks" : 74.0,
"subject" : "english"
}
{
"_id" : ObjectId("5ba49ff6a3b0f1fce83f3f48"),
"name" : "sunit",
"marks" : 60.0,
"subject" : "english"
}
{
"_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
"name" : "vivek",
"marks" : 85.0,
"subject" : "marathi"
}
{
"_id" : ObjectId("5ba4a205ffaf9599885ab185"),
"name" : "vijay",
"marks" : 85.0
}
{
"_id" : ObjectId("5ba4a20affaf9599885ab186"),
"name" : "vineet",
"marks" : 85.0,
"subject" : "maths"
}

Now lets play with this collection.

for sql query
select subject,max(marks) from maxdemo_table group by subject

equivalent mongo query is

db.maxdemo.aggregate([
{$group : {_id : "$subject", maximum_marks : {$max : "$marks"}}}
])

for sql query
select max(marks) from maxdemo_table
equivalent mongo query is

db.maxdemo.aggregate([
{$group : {_id : null, maximum_marks : {$max : "$marks"}}}

for sql query
select subject,max(marks) from maxdemo_table group by subject having subject ="maths"
])
equivalent mongo query is

db.maxdemo.aggregate([
{$group : {_id : "$subject", maximum_marks : {$max : "$marks"}}},
{$match: { _id : { $eq: "maths"} } },
])
for sql query
select subject,max(marks) from maxdemo_table group by subject having max(marks) = 85
equivalent mongo query is

db.maxdemo.aggregate([
{$group : {_id : "$subject", maximum_marks : {$max : "$marks"}}},
{$match: { maximum_marks : { $eq: 85} } },
])
Getting max value and all records on which max value calculated

db.maxdemo.aggregate([
{
$group : {
_id : "$subject",
maximum_marks : {$max : "$marks"},
student: {$push: "$$ROOT"}
}
}
])

Result:
{
"_id" : "geography",
"maximum_marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0,
"subject" : "geography"
}
]
}
{
"_id" : "maths",
"maximum_marks" : 90.0,
"student" : [
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba49fa0a3b0f1fce83f3f44"),
"name" : "shashank",
"marks" : 79.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba4a20affaf9599885ab186"),
"name" : "vineet",
"marks" : 85.0,
"subject" : "maths"
}
]
}
{
"_id" : "marathi",
"maximum_marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f73a3b0f1fce83f3f40"),
"name" : "sagar",
"marks" : 77.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba49fcaa3b0f1fce83f3f46"),
"name" : "sujay",
"marks" : 64.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
"name" : "vivek",
"marks" : 85.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba4a205ffaf9599885ab185"),
"name" : "vijay",
"subject" : "marathi",
"marks" : 85.0
}
]
}
{
"_id" : "english",
"maximum_marks" : 76.0,
"student" : [
{
"_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
"name" : "sangram",
"marks" : 76.0,
"subject" : "english"
},
{
"_id" : ObjectId("5ba49fe4a3b0f1fce83f3f47"),
"name" : "subodh",
"marks" : 74.0,
"subject" : "english"
},
{
"_id" : ObjectId("5ba49ff6a3b0f1fce83f3f48"),
"name" : "sunit",
"marks" : 60.0,
"subject" : "english"
}
]
}

Saving result into new collection

var temp = db.maxdemo.aggregate([
{
$group :
{
_id : "$subject",
maximum_marks : {$max : "$marks"},
student: {$push: "$$ROOT"}
}
},
{
$out : "hc_hosting_stat"
}
])

Getting record by comparison of two fields

just for illustration purpose update a record as follows by adding new field beside maximum marks called marks
{
"_id" : "geography",
"maximum_marks" : 85.0,
"marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0,
"subject" : "geography"
}
]
}
and below
{
"_id" : "maths",
"maximum_marks" : 90.0,
"marks" : 89.0,
"student" : [
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba49fa0a3b0f1fce83f3f44"),
"name" : "shashank",
"marks" : 79.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba4a20affaf9599885ab186"),
"name" : "vineet",
"marks" : 85.0,
"subject" : "maths"
}
]
}

Now lets issue a mongo query
db.hc_hosting_stat.find({$where: function() { return this.maximum_marks == this.marks } } );

Result:
{
"_id" : "geography",
"maximum_marks" : 85.0,
"marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0,
"subject" : "geography"
}
]
}

we are getting all records where maximum_marks matches marks.


For collection "hc_hosting_stat"

{
"_id" : "geography",
"maximum_marks" : 85.0,
"marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49f87a3b0f1fce83f3f42"),
"name" : "swapnil",
"marks" : 80.0,
"subject" : "geography"
},
{
"_id" : ObjectId("5ba49fbba3b0f1fce83f3f45"),
"name" : "sunil",
"marks" : 84.0,
"subject" : "geography"
}
]
}
{
"_id" : "maths",
"maximum_marks" : 90.0,
"marks" : 89.0,
"student" : [
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba49fa0a3b0f1fce83f3f44"),
"name" : "shashank",
"marks" : 79.0,
"subject" : "maths"
},
{
"_id" : ObjectId("5ba4a20affaf9599885ab186"),
"name" : "vineet",
"marks" : 85.0,
"subject" : "maths"
}
]
}
{
"_id" : "marathi",
"maximum_marks" : 85.0,
"student" : [
{
"_id" : ObjectId("5ba49f73a3b0f1fce83f3f40"),
"name" : "sagar",
"marks" : 77.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba49fcaa3b0f1fce83f3f46"),
"name" : "sujay",
"marks" : 64.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
"name" : "vivek",
"marks" : 85.0,
"subject" : "marathi"
},
{
"_id" : ObjectId("5ba4a205ffaf9599885ab185"),
"name" : "vijay",
"subject" : "marathi",
"marks" : 85.0
}
]
}
{
"_id" : "english",
"maximum_marks" : 76.0,
"student" : [
{
"_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
"name" : "sangram",
"marks" : 76.0,
"subject" : "english"
},
{
"_id" : ObjectId("5ba49fe4a3b0f1fce83f3f47"),
"name" : "subodh",
"marks" : 74.0,
"subject" : "english"
},
{
"_id" : ObjectId("5ba49ff6a3b0f1fce83f3f48"),
"name" : "sunit",
"marks" : 60.0,
"subject" : "english"
}
]
}





Getting subject topper and topped marks

db.hc_hosting_stat.find({}).forEach(function(item)
{
var new_item=[];
for(var i=0;i < item.student.length;i++){
if(item.student[i].marks == item.maximum_marks){
new_item.push(item.student[i])
}
}
db.result.insert(new_item)
})

New result collection is as below
{
"_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
"name" : "sachin",
"marks" : 85.0,
"subject" : "geography"
}
{
"_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
"name" : "saurabh",
"marks" : 90.0,
"subject" : "maths"
}
{
"_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
"name" : "vivek",
"marks" : 85.0,
"subject" : "marathi"
}
{
"_id" : ObjectId("5ba4a205ffaf9599885ab185"),
"name" : "vijay",
"subject" : "marathi",
"marks" : 85.0
}
{
"_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
"name" : "sangram",
"marks" : 76.0,
"subject" : "english"
}

or

db.hc_hosting_stat.aggregate([
             {
                $project: {
                    maximum_marks: 1,
                    'topper': {
                        $filter: {
                            input: '$student',
                            as: 'item',
                            cond: {
                                $eq: ['$$item.marks', '$maximum_marks']
                            }
                        }
                    }
                }
            }
        ])

Result:

{
    "_id" : "geography",
    "maximum_marks" : 85.0,
    "topper" : [
        {
            "_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
            "name" : "sachin",
            "marks" : 85.0,
            "subject" : "geography"
        }
    ]
}
{
    "_id" : "maths",
    "maximum_marks" : 90.0,
    "topper" : [
        {
            "_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
            "name" : "saurabh",
            "marks" : 90.0,
            "subject" : "maths"
        }
    ]
}
{
    "_id" : "marathi",
    "maximum_marks" : 85.0,
    "topper" : [
        {
            "_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
            "name" : "vivek",
            "marks" : 85.0,
            "subject" : "marathi"
        },
        {
            "_id" : ObjectId("5ba4a205ffaf9599885ab185"),
            "name" : "vijay",
            "subject" : "marathi",
            "marks" : 85.0
        }
    ]
}
{
    "_id" : "english",
    "maximum_marks" : 76.0,
    "topper" : [
        {
            "_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
            "name" : "sangram",
            "marks" : 76.0,
            "subject" : "english"
        }
    ]
}

combining two steps into one:

This query list all subject toppers keeping in concern that many student getting top score in a subject.e.g.vivek & vineet both score top in Marathi.

 db.maxdemo.aggregate([
{
    $group : {
        _id : "$subject",
        topscore : {$max : "$marks"},
        student: {$push: "$$ROOT"}
    }      
},
{
  $project:
  {
        topscore: 1,
        _id:0,
        'subject': '$_id',
        'topper': {
                        $filter: {
                            input: '$student',
                            as: 'item',
                            cond: {
                                $eq: ['$$item.marks', '$topscore']
                            }
                        }
                    }
  }
}
])

Result:

{
    "topscore" : 85.0,
    "subject" : "geography",
    "topper" : [
        {
            "_id" : ObjectId("5ba49f7da3b0f1fce83f3f41"),
            "name" : "sachin",
            "marks" : 85.0,
            "subject" : "geography"
        }
    ]
}
{
    "topscore" : 90.0,
    "subject" : "maths",
    "topper" : [
        {
            "_id" : ObjectId("5ba49f90a3b0f1fce83f3f43"),
            "name" : "saurabh",
            "marks" : 90.0,
            "subject" : "maths"
        }
    ]
}
{
    "topscore" : 85.0,
    "subject" : "marathi",
    "topper" : [
        {
            "_id" : ObjectId("5ba4a1fdffaf9599885ab184"),
            "name" : "vivek",
            "marks" : 85.0,
            "subject" : "marathi"
        },
        {
            "_id" : ObjectId("5ba4a205ffaf9599885ab185"),
            "name" : "vijay",
            "subject" : "marathi",
            "marks" : 85.0
        }
    ]
}
{
    "topscore" : 76.0,
    "subject" : "english",
    "topper" : [
        {
            "_id" : ObjectId("5ba49f63a3b0f1fce83f3f3f"),
            "name" : "sangram",
            "marks" : 76.0,
            "subject" : "english"
        }
    ]
}

2018/09/07

Javascript Promise & Async/await

What is promise ?
    Promise is an object that represent result of  asynchronous operation in future.Promise can be in one of following state

        1) pending - The initial state of a promise.
        2) resolved - The state of a promise representing a successful operation.
        3) rejected - The state of a promise representing a failed operation.

    Once a promise is resolved or rejected it remain so it can't change it state further.In sense it is immutable.

Initial Javascript do not have native promises library though there were third party implementation like Deferred,Q,Asyc.js or bluebird.
ES6 brought a Promises natively to javascript.

Only the function responsible for creating the promise will have knowledge of the promise status, or access to resolve or reject.
The ES6 promise constructor takes a function. That function takes two parameters, resolve(), and reject().
You can resolve() or reject() with values, which will be passed to the callback functions attached with .then().
e.g.
     assume we want to get IP address of client through REST API.'request-promise' is promisified version of 'request' library.
promise provide alternative to callback.

    var Request = require("request");
    var RequestPromise = require('request-promise');

    var getPublicIp = function () {
        return new Promise(function (resolve, reject) {
        RequestPromise('http://httpbin.org/ip')
            .then(function (htmlString) {
                resolve(JSON.parse(htmlString))
            })
            .catch(function (err) {
                reject(err)
            });
        }
        );
    }

we can call this function as

    getPublicIp()
        .then(function (fulfilled) {
        console.log('Public Ip Address:' + fulfilled.origin)
        })
        .catch(function (error) {
        console.log("Error:" + error);
        })

or using async/await as

    async function getPublicIpAsyc() {
        try {
        var result = await getPublicIp();
        console.log("Async/Await:" + JSON.stringify(result))
        } catch (err) {
        console.log("Async/Await:" + err)
        }

    }

    getPublicIpAsyc();

Here await can be used only inside function which is defined as async.Async/await is new feature that await ask calling function to wait till get response of statement marked with await.
async/await let us to handle exception in single try catch unlike promise where we have to chain catch block after then block.

2018/09/06

CRUD opeation using Mongoose & Express.js

Mongoose is an ORM for mongodb in node.js.
It provides validation of property like field is required,max length & min length of text field & many more.

we will create an express application that does CRUD operation on mongo using mongoose.


First lets create an express app

    express --view=ejs myapp

add models folder.Into models folder add user.js with following code

    var mongoose = require('mongoose');
    var Schema = mongoose.Schema;

    mongoose.connect('mongodb://localhost/palbum');

    // create a schema
    var userSchema = new Schema({
        firstname: String,
        lastname: String,
        username: { type: String, required: true, unique: true },
        password: { type: String, required: true },
        admin: { type: Boolean, default: false },
        location: { type: String, minlength: 5, maxlength: 15 },
        meta: {
        age: { type: Number, min: 5 },
        website: String
        },
        created_at: { type: Date, default: Date.now },
        updated_at: { type: Date, default: Date.now }
    });

    //instance method
    userSchema.methods.dudify = function () {
        this.name = this.name + '-dude';
        return this.name;
    };

    //static method
    userSchema.statics.findByLocation = function(location, cb) {
        return this.find({ location: new RegExp(location, 'i') }, cb);
    };

    // we need to create a model using it
    var User = mongoose.model('User', userSchema);

    // make this available to our users in our Node applications
    module.exports = {
       "User": User,
       "UserSchema":userSchema
    }

In user schema validation has been put in place over username to ensure uniqueness of username across records. "location" has been asssigned maxlength & minlength validation defaukt value has been set on created_at & updated_at.

In mongoose we can add instance method & static method."findByLocation" is static method while "dudify" is instance method.for using "dudify" we need instance of User unlike "findByLocation".

update route/users.js as follows

    var express = require('express');
    var router = express.Router();
    var User = require('../models/user').User;
    var userSchema = require('../models/user').userSchema;
    var mongoose = require('mongoose');

    /* GET users listing. */
    router.get('/', function (req, res, next) {
      res.send('respond with a resource');
    });


    router.post('/add', function (req, res, next) {
      // create a new user called chris
      var postUser = new User({
        firstname: req.body.firstname,
        lastname: req.body.lastname,
        location: req.body.location,
        meta: {
          age: req.body.age,
          website: req.body.website
        },
        username: req.body.username,
        password: req.body.password,
      });

      //dudify
      postUser.dudify(function (err, name) {
        if (err) throw err;
        console.log('Your new name is ' + name);
      });

      postUser.save(function (err) {
        if (err) throw err;
        console.log('User saved successfully!');
        res.send('User saved successfully!');
      });


    });

    router.post('/location', function (req, res, next) {
      var User = mongoose.model('User', userSchema);
      User.findByLocation(req.body.location, function (err, users) {
        console.log(users);
        res.json(users);
      });
    });

    router.get('/getAll', function (req, res, next) {
      // get all the users
      User.find({}, function (err, users) {
        if (err) throw err;

        // object of all the users
        console.log(users);
        res.json(users);
      });
    });

    router.get('/get/:username', function (req, res, next) {
      User.find({ username: req.params.username }, function (err, user) {
        if (err) throw err;

        // object of the user
        console.log(user);
        res.json(user);
      });
    });

    router.delete('/findthenremove/:username', function (req, res, next) {
      console.log("username" + req.params.username);
      User.find({ username: req.params.username }, function (err, user) {
        if (err) throw err;

        console.log(JSON.stringify(user));

        if (user == undefined || user == null) {
          res.json({
        "success": 0,
        "data": {},
        "message": "user not found!"
          });
        } else {

          // delete him
          User.deleteOne({ username:  req.params.username }, function (err) {
        if (err) return handleError(err);

        console.log('User successfully deleted!');
        res.json({
          "success": 1,
          "data": user,
          "message": "user removed!"
        });
          });

        }
      });
    });


    router.delete('/findandremove/:username', function (req, res, next) {
      console.log("username" + req.params.username);

      User.findOneAndRemove({ username: req.params.username }, function (err) {
        if (err) throw err;

        // we have deleted the user
        console.log('User deleted!');
        res.json('User deleted!')
      });
    });

    router.put('/update/:username', function (req, res, next) {
      User.updateOne({ username: req.params.username }, { password: req.body.password}, function(err, resp) {
        res.json("user password updated!");
      });
    });

    module.exports = router;

we have following endpoints

1) adding new user:
    Endpoint:http://localhost:3000/users/user
    Method:http post
    Header:Content-Type:application/json
    sample Payload:
    {
        "firstname": "Chris",
        "lastname":"harris",
        "username": "oneoneone",
        "password": "password",
        "location":"puneeee",
        "age":36,
        "website":"msdotnetbuddy.blogspot.in"
    }
   response:User saved successfully!

2)find by location:list all record with location mumbai.

    Endpoint: http://localhost:3000/users/location
    Method:post
    Header:Content-Type:application/json
    Payload:
    {
        "location":"mumbai"
    }
    Response:
    [
        {
        "meta": {
            "age": 36,
            "website": "msdotnetbuddy.blogspot.in"
        },
        "admin": false,
        "_id": "5b90e1e549999d1d073d79ca",
        "firstname": "Chris",
        "lastname": "harris",
        "location": "mumbai",
        "username": "one",
        "password": "password",
        "created_at": "2018-09-06T08:14:29.295Z",
        "updated_at": "2018-09-06T08:14:29.295Z",
        "__v": 0
        }
    ]
3) List all Records:
       Endpoint:http://localhost:3000/users/getAll
       method:get
       response:
        [
            {
            "admin": false,
            "created_at": "2018-09-06T12:02:39.372Z",
            "updated_at": "2018-09-06T12:02:39.372Z",
            "_id": "5b90dfab631d041a2aa22b47",
            "name": "Chris-dude",
            "username": "sevilayha",
            "password": "password",
            "__v": 0
            },
            {
            "admin": false,
            "_id": "5b90e0ce168db71c5bd4284a",
            "username": "chris",
            "password": "password",
            "created_at": "2018-09-06T08:09:50.942Z",
            "updated_at": "2018-09-06T08:09:50.942Z",
            "__v": 0
            },
            {
            "meta": {
                "age": 36,
                "website": "msdotnetbuddy.blogspot.in"
            },
            "admin": false,
            "_id": "5b90e1e549999d1d073d79ca",
            "firstname": "Chris",
            "lastname": "harris",
            "location": "mumbai",
            "username": "one",
            "password": "password",
            "created_at": "2018-09-06T08:14:29.295Z",
            "updated_at": "2018-09-06T08:14:29.295Z",
            "__v": 0
            },
            {
            "meta": {
                "age": 36,
                "website": "msdotnetbuddy.blogspot.in"
            },
            "admin": false,
            "_id": "5b90e6a851b57b2056e18210",
            "firstname": "Chris",
            "lastname": "harris",
            "location": "puneeee",
            "username": "oneoneone",
            "password": "password",
            "created_at": "2018-09-06T08:34:48.451Z",
            "updated_at": "2018-09-06T08:34:48.451Z",
            "__v": 0
            }
        ]
4)find single record by username
          Endpoint:http://localhost:3000/users/get/one
          Method:Get
      response:
        [
            {
            "meta": {
                "age": 36,
                "website": "msdotnetbuddy.blogspot.in"
            },
            "admin": false,
            "_id": "5b90e1e549999d1d073d79ca",
            "firstname": "Chris",
            "lastname": "harris",
            "location": "mumbai",
            "username": "one",
            "password": "password",
            "created_at": "2018-09-06T08:14:29.295Z",
            "updated_at": "2018-09-06T08:14:29.295Z",
            "__v": 0
            }
        ]
5)Delete:find & remove and coupled together rather than in two stages
    Endpoint:http://localhost:3000/users/findandremove/sevilayha
        Method:http/delete
    response:"User deleted!"

6) Delete:find & remove are decoupled
      Endpoint:http://localhost:3000/users/findthenremove/chris
      method:http/delete
      response:
    {
        "success": 1,
        "data": [
        {
            "admin": false,
            "_id": "5b90e0ce168db71c5bd4284a",
            "username": "chris",
            "password": "password",
            "created_at": "2018-09-06T08:09:50.942Z",
            "updated_at": "2018-09-06T08:09:50.942Z",
            "__v": 0
        }
        ],
        "message": "user removed!"
    }
7)Update:it updates password to new value.
    Endpoint:http://localhost:3000/users/update/oneoneone
    Header:Content-Type:application/json
    Payload:
        {
        "password":"newpassword"
        }
    Response:"user updated!"

Code of this article can be viewed at https://github.com/gitsangramdesai/mongoose-express-rest