MongoDB more Update

Insert many
  • Send multiple data in one request will be faster than insert one by one
  • Command line tool mongoimport can be used to import data instead of insertMany many times
  • Message size can’t be larger than 48 MB
> db.movies.insertMany([{"title":"Movie 1"},{"title":"Movie 2"},{"title":"Movie 3"}])
{
    "acknowledged" : true,
    "insertedIds" : [
        ObjectId("5f6876732fbc27989eb7f4c2"),
        ObjectId("5f6876732fbc27989eb7f4c3"),
        ObjectId("5f6876732fbc27989eb7f4c4")
    ]
}

Ordered and unordered insert
Ordered insert (Default): MongoDB stop insert later records whenever any data insert fail
Unordered insert: MongoDB will try to insert all data regardless some data fail, unordered insert will have better performance

drop 
ex. db.movies.drop()
> db.movies.insertOne(movie2)
{
        "acknowledged" : true,
        "insertedId" : ObjectId("5f68e75e85d32460c3bfa6e3")
}
> db.movies.find().pretty()
{
        "_id" : ObjectId("5f68e75885d32460c3bfa6e2"),
        "title" : "A movie",
        "director" : "A director",
        "year" : 2020
}
{
        "_id" : ObjectId("5f68e75e85d32460c3bfa6e3"),
        "title" : "A movie 2",
        "director" : "A director 2",
        "year" : 2020
}
> db.movies.drop()
true
> db.movies.find().pretty()

update
It seems update is an atomic operation, but I'm curious what will happen when two update operations happen at the same time, and the second operation filter criteria is not matched because the first operation modified the data?
> db.movies.find().pretty()
{
        "_id" : ObjectId("5f6950a694d2d7702605f470"),
        "title" : "A movie",
        "director" : "A director",
        "year" : 2020
}
{
        "_id" : ObjectId("5f6950a794d2d7702605f471"),
        "title" : "A movie 2",
        "director" : "A director 2",
        "year" : 2020
}

// Update one movie
> db.movies.updateOne({"title":"A movie"},{$set:{"year":2021,"review":0}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.movies.find({"title":"A movie"}).pretty()
{
        "_id" : ObjectId("5f6950a694d2d7702605f470"),
        "title" : "A movie",
        "director" : "A director",
        "year" : 2021,
        "review" : 0
}

replace
replace command will replace whole document
> db.movies.replaceOne({"title":"A movie"}, {"title":"B movie", "year":1990})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.movies.find().pretty()
{
        "_id" : ObjectId("5f6950a694d2d7702605f470"),
        "title" : "B movie",
        "year" : 1990
}
{
        "_id" : ObjectId("5f6950a794d2d7702605f471"),
        "title" : "A movie 2",
        "director" : "A director 2",
        "year" : 2020
}

increment a number
// create page document
> pageA= {"page":"page A", "view":0}
{ "page" : "page A", "view" : 0 }
> db.page.insertOne(pageA)
{
        "acknowledged" : true,
        "insertedId" : ObjectId("5f69551d94d2d7702605f472")
}

// check data
> db.page.find({"page":"page A"}).pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "view" : 0
}

// increment
> db.page.updateOne({"page":"page A"},{"$inc":{"view":1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// check result
> db.page.find({"page":"page A"}).pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "view" : 1
}

$unset
// check there is a view property
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "view" : 1
}

// unset view property
> db.page.updateOne({"page":"page A"},{"$unset":{"view":1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// check view property disappeared
> db.page.find().pretty()
{ "_id" : ObjectId("5f69551d94d2d7702605f472"), "page" : "page A" }

$set change attribute value in embedded document
// set new property: user
> db.page.updateOne({"page":"page A"}, {"$set":{
...    "user":{
...      "display_name":"",
...      "devices":[
...
...      ],
...      "subscriptions":{
...          "vpn":{
...            "active":true,
...          }
...      },
...      "max_devices":5
...    },
... }})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// check data
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "user" : {
                "display_name" : "",
                "devices" : [ ],
                "subscriptions" : {
                        "vpn" : {
                                "active" : true
                        }
                },
                "max_devices" : 5
        }
}

// change display_name
> db.page.updateOne({"page":"page A"},{"$set":{"user.display_name":"ABCDE"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// check update result
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "user" : {
                "display_name" : "ABCDE",
                "devices" : [ ],
                "subscriptions" : {
                        "vpn" : {
                                "active" : true
                        }
                },
                "max_devices" : 5
        }
}

$push data to array attribute
// push data to array attribute
> db.page.updateOne({"page":"page A"},{"$push":{"comment":"hello"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.page.updateOne({"page":"page A"},{"$push":{"comment":"hello2"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// check result
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "comment" : [
                "hello",
                "hello2"
        ]
}

$push multiple elements to an array attribute by $each
// Can't add multiple elements to an array by simply $push
> db.page.updateOne({"page":"page A"},{"$push":{"comment":["hello3","hello4"]}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// Because only $push will add only one element, thus one element with array value will be added
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "comment" : [
                "hello",
                "hello2",
                [
                        "hello3",
                        "hello4"
                ]
        ]
}

// Need $each to add multiple elements
> db.page.updateOne({"page":"page A"},{"$push":{"comment":{"$each":["hello3","hello4"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "comment" : [
                "hello",
                "hello2",
                [
                        "hello3",
                        "hello4"
                ],
                "hello3",
                "hello4"
        ]
}

$slice array to 3 elements when performing $each
> db.page.updateOne({"page":"page A"},{"$push":{"comment":{"$each":[],"$slice":-3}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// rest 3 records
> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "comment" : [
                [
                        "hello3",
                        "hello4"
                ],
                "hello3",
                "hello4"
        ]
}


$sort when $slice
> db.page.updateOne({"page":"page A"},{"$push":{"top3":{"$each":[{"name":"A","height":5},{"name":"B","height":4},{"name":"C","height":3},{"name":"D","height":2},{"name":"E","height":1}],"$slice":-3,"$sort":{"height":1}}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.page.find().pretty()
{
        "_id" : ObjectId("5f69551d94d2d7702605f472"),
        "page" : "page A",
        "comment" : [
                "hello3",
                "hello4"
        ],
        "top3" : [
                {
                        "name" : "C",
                        "height" : 3
                },
                {
                        "name" : "B",
                        "height" : 4
                },
                {
                        "name" : "A",
                        "height" : 5
                }
        ]
}

$push comment
> db.users.updateOne({"name":"user1"},{"$push":{"comment":{"email":"a@gmail.com"}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.updateOne({"name":"user1"},{"$push":{"comment":{"email":"b@gmail.com"}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.find().pretty()
{
        "_id" : ObjectId("5f6a38748074fb4051b75cb8"),
        "name" : "user1",
        "comment" : [
                {
                        "email" : "a@gmail.com"
                },
                {
                        "email" : "b@gmail.com"
                }
        ]
}

$push and then $addToSet phone with $each
> user = {"name":"user 1"}
{ "name" : "user 1" }
> db.users.insertOne(user)
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f6c46393c90a681c72b7df5")
}

> db.users.updateOne({"name":"user 1"},{$push:{"phone":"123456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "123456"
    ]
}

// add existing phone, no modification
> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": "123456"}})
{ "acknowledged" : true, “matchedCount" : 1, "modifiedCount" : 0 }

// add new phone
> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": "223456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "123456",
        "223456"
    ]
}

// add existing phone, no modification
> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": "223456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }

// add multiple elements by $each, one non-existing phone, so one modification
> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": {"$each":["223456","12345"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": {"$each":["223456","12345","123456"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "123456",
        "223456",
        "12345"
    ]
}


Remove element by $pop, $pop 1 remove from end, $pop -1 remove from beginning
> db.users.updateOne({"name":"user 1"}, {"$pop":{"phone": 1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "123456",
        "223456"
    ]
}

> db.users.updateOne({"name":"user 1"}, {"$pop":{"phone": -1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456"
    ]
}

Remove element by $pull
// pull element but value is not matched, no modification
> db.users.updateOne({"name":"user 1"}, {"$pull":{"phone":"123456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }

//
> db.users.updateOne({"name":"user 1"}, {"$pull":{"phone":"223456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [ ]
}

Remove multiple elements by $pull
// push elements
> db.users.updateOne({"name":"user 1"}, {"$addToSet":{"phone": {"$each":["223456","12345","123456"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "123456"
    ]
}
> db.users.updateOne({"name":"user 1"}, {$push:{"phone": {"$each":["223456","12345","123456"]}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "123456",
        "223456",
        "12345",
        "123456"
    ]
}

// pull 123456, all 123456 will be removed
> db.users.updateOne({"name":"user 1"}, {"$pull":{"phone":"123456"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ]
}

$inc increase value in specified index array
// original data
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ]
}

// push 2 comments
> db.users.updateOne({"name":"user 1"}, {$push:{"comment":{"vote":0,"message":"haha"}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.updateOne({"name":"user 1"}, {$push:{"comment":{"vote":0,"message":"haha 2"}}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 0,
            "message" : "haha"
        },
        {
            "vote" : 0,
            "message" : "haha 2"
        }
    ]
}

// increase the first element vote
> db.users.updateOne({"name":"user 1"}, {"$inc":{"comment.0.vote":1}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "haha"
        },
        {
            "vote" : 0,
            "message" : "haha 2"
        }
    ]
}

// increase 100 to the second element
> db.users.updateOne({"name":"user 1"}, {"$inc":{"comment.1.vote":100}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "haha"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ]
}

// update message when array element match arrayFilters
> db.users.updateOne({"name":"user 1"}, {$set:{"comment.$[elmt].message":"Good"}},{arrayFilters:[{"elmt.vote":{$lte: 11}}]})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "Good"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ]
}

upsert: update when exist, otherwise, insert
// upsert age to user 1
> db.users.updateOne({"name":"user 1"},{"$set":{"age":10}},{"upsert":true})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "Good"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ],
    "age" : 10
}

// upsert age to user 2
> db.users.updateOne({"name":"user 2"},{"$set":{"age":10}},{"upsert":true})
{
    "acknowledged" : true,
    "matchedCount" : 0,
    "modifiedCount" : 0,
    "upsertedId" : ObjectId("5f6d6fa0d8c8c1bae1785718")
}
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "Good"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ],
    "age" : 10
}
{
    "_id" : ObjectId("5f6d6fa0d8c8c1bae1785718"),
    "name" : "user 2",
    "age" : 10
}

$setOnInsert to config attribute when insert
// set createdAt when insert
> db.users.updateOne({"name":"user 2"},{"$setOnInsert":{"createdAt":new Date()}},{"upsert":true})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "Good"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ],
    "age" : 10
}
{
    "_id" : ObjectId("5f6d6fa0d8c8c1bae1785718"),
    "name" : "user 2",
    "age" : 10
}

// 
> db.users.updateOne({"name":"user 3"},{"$setOnInsert":{"createdAt":new Date()}},{"upsert":true})
{
    "acknowledged" : true,
    "matchedCount" : 0,
    "modifiedCount" : 0,
    "upsertedId" : ObjectId("5f6d738ed8c8c1bae1785730")
}
> db.users.find().pretty()
{
    "_id" : ObjectId("5f6c46393c90a681c72b7df5"),
    "name" : "user 1",
    "phone" : [
        "223456",
        "12345",
        "223456",
        "12345"
    ],
    "comment" : [
        {
            "vote" : 1,
            "message" : "Good"
        },
        {
            "vote" : 100,
            "message" : "haha 2"
        }
    ],
    "age" : 10
}
{
    "_id" : ObjectId("5f6d6fa0d8c8c1bae1785718"),
    "name" : "user 2",
    "age" : 10
}
{
    "_id" : ObjectId("5f6d738ed8c8c1bae1785730"),
    "name" : "user 3",
    "createdAt" : ISODate("2020-09-25T04:35:26.398Z")
}

findOneAndUpdate can handle race condition, but it return old document by default. 
Can returnNewDocument to return the one after modification
// update and return old document
> db.users.findOneAndUpdate({"name":"user1"},{"$push":{"phone":{"$each":["123456"]}}})
{
        "_id" : ObjectId("5f6a38748074fb4051b75cb8"),
        "name" : "user1",
        "comment" : [
                {
                        "email" : "a@gmail.com"
                },
                {
                        "email" : "b@gmail.com"
                }
        ]
}

// update and return new document
> db.users.findOneAndUpdate({"name":"user1"},{"$push":{"phone":{"$each":["1234567"]}}},{"returnNewDocument":true})
{
        "_id" : ObjectId("5f6a38748074fb4051b75cb8"),
        "name" : "user1",
        "comment" : [
                {
                        "email" : "a@gmail.com"
                },
                {
                        "email" : "b@gmail.com"
                }
        ],
        "phone" : [
                "123456",
                "1234567"
        ]
}




Start MongoDB & Shell CRUD

 Pull image

sudo docker pull mongo

Start container
sudo docker run --name mongo -d -p 27017:27017 mongo
c38be4173edc4490e954c1046abe47aadf39c938e2faa188c89aef0cad221e0d

Folder
/data/db

Go to shell, 進這個 shell 之後可以輸入 javascript, 也可以定義 function.
docker exec -it mongo bash
iliao@MacBook-Pro ~ % docker exec -it mongo bash
root@12c0a2efb72c:/# mongo
>

如果沒輸入完整的 javascript command, 可以按三下 enter 之後跳出來
> Math.sin(Math.PI/2)
1
> a =
...
...
>

Create data
// Define movie
> movie = {"title":"A movie", "director": "A director", "year":2020}
{ "title" : "A movie", "director" : "A director", "year" : 2020 }

// Insert a movie
> db.movies.insertOne(movie)
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f685a71a9ea26aaea91f834")
}

// Query a movie
> db.movies.find().pretty()
{
    "_id" : ObjectId("5f685a71a9ea26aaea91f834"),
    "title" : "A movie",
    "director" : "A director",
    "year" : 2020
}

// 定義第二部電影
> movie2 = {"title":"A movie 2", "director": "A director 2", "year":2020}
{ "title" : "A movie 2", "director" : "A director 2", "year" : 2020 }

// 儲存第二部電影
> db.movies.insertOne(movie2)
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5f685b21a9ea26aaea91f835")
}

// 把電影都列出來
> db.movies.find().pretty()
{
    "_id" : ObjectId("5f685a71a9ea26aaea91f834"),
    "title" : "A movie",
    "director" : "A director",
    "year" : 2020
}
{
    "_id" : ObjectId("5f685b21a9ea26aaea91f835"),
    "title" : "A movie 2",
    "director" : "A director 2",
    "year" : 2020
}

讀一筆資料 READ
> db.movies.findOne()
{
    "_id" : ObjectId("5f685a71a9ea26aaea91f834"),
    "title" : "A movie",
    "director" : "A director",
    "year" : 2020
}

更新一筆資料 UPDATE
// 指定條件: title
// 修改: $set
> db.movies.updateOne({"title":"A movie"}, {$set:{reviews:[], "title":"A movie 3"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

// 檢查修改結果
> db.movies.find().pretty()
{
    "_id" : ObjectId("5f685a71a9ea26aaea91f834"),
    "title" : "A movie 3",
    "director" : "A director",
    "year" : 2020,
    "reviews" : [ ]
}
{
    "_id" : ObjectId("5f685b21a9ea26aaea91f835"),
    "title" : "A movie 2",
    "director" : "A director 2",
    "year" : 2020
}

Delete a record
> db.movies.deleteOne({"title":"A movie 3"})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.movies.find().pretty()
{
    "_id" : ObjectId("5f685b21a9ea26aaea91f835"),
    "title" : "A movie 2",
    "director" : "A director 2",
    "year" : 2020
}

Delete multiple records
> db.movies.deleteMany({})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.movies.find().pretty()
>

leetcode easy - Contains Duplicate

 LeetCode


Code
class Solution {
    public boolean containsDuplicate(int[] nums) {
        Arrays.sort(nums);


        for ( int i = 0; i < nums.length-1; i++ ) {
            if (nums[i] == nums[i+1]) return true;
        }


        return false;
    }

leetcode easy - Pascal's Triangle

LeetCode

Code

import java.util.ArrayList;
import java.util.List;


class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> result = new ArrayList<>();
        for ( int i = 0; i < numRows; i++ ) {
            List<Integer> row = new ArrayList<>();
            for ( int j = 0; j <= i; j++ ) {
                if (i == 0 || i == 1 || j == 0 || i == j) {
                    row.add(1);
                    continue;
                }


                System.out.println("(" + i + "," + j + ")");
                row.add(result.get(i-1).get(j-1) + result.get(i-1).get(j));
            }
            result.add(row);
        }
        return result;
    }


leetcode medium - Number of Islands

 Leetcode


Code
class Solution {
    public int numIslands(char[][] grid) {

        int count = 0;

        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == '1') {
                    count++;
                    clearIsland(grid, i, j);
                }
            }
        }
        return count;
    }

    private void clearIsland(char[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] == '0') return;


        grid[i][j] = '0';
        clearIsland(grid, i, j-1); // up
        clearIsland(grid, i, j+1); // down
        clearIsland(grid, i-1, j); // left
        clearIsland(grid, i+1, j); // right
    }

leetcode medium - LRU Cache

LeetCode

Code
It will be great to implement LinkedList by myself, I use JDK LinkedList directly

class LRUCache {

    private LinkedList<Integer> list = new LinkedList<>();
    private Map<Integer, Integer> map = new HashMap<>();
    private final int cap;


    public LRUCache(int capacity) {
        this.cap = capacity;
    }


    public int get(int key) {
        Integer result = map.get(key);
        if (result == null) {
            return -1;
        } else {
            list.remove(Integer.valueOf(key));
            list.addFirst(key);
        }
        return result;
    }


    public void put(int key, int value) {
        Integer result = map.get(key);
        if (result != null) {
            list.remove(Integer.valueOf(key));
        } else {
            if (map.size() == cap) {
                map.remove(list.removeLast());
            }
        }
        list.addFirst(key);
        map.put(key, value);
     }

java.lang.ref


  1. SoftReference: Cleared when GC is response to memory demand. Often used to implement memory-sensitive cache.
  2. WeakReference: Don’t prevent to be finalized. Often used to implement canonicalizing mapping. (Mapping only reachable object instances)
  3. PhantomReference: Are enqueued after determining to be reclaimed. Not automatically cleared by GC. Object referenced via phantom reference won’t be cleared by GC automatically until phantom reference cleared. 

LeetCode - Kids With the Greatest Number of Candies

 

LeetCode

Code
class Solution {
    public List<Boolean> kidsWithCandies(int[] candies, int extraCandies) {
        int max = max(candies);
        List<Boolean> result = new ArrayList<>();
        for (int i = 0; i < candies.length; i++) {
            result.add(candies[i] + extraCandies >= max);
        }
        return result;
    }
    
    private int max(int[] candies) {
        int max = 0;
        for ( int i = 0; i < candies.length; i++ ) {
            if (max < candies[i]) {
                max = candies[i];
            }
        }
        return max;
    }
}

leetcode - Shuffle the Array

 

LeetCode

Code
class Solution {
    public int[] shuffle(int[] nums, int n) {
        int[] result = new int[nums.length];
        int currentIdx = 0;
        for (int i = 0; i < n; i++) {
            result[currentIdx] = nums[i];
            result[currentIdx+1] = nums[i+n];
            currentIdx += 2;
        }
        return result;
    }
}

leetcode - Running Sum of 1d Array

 

LeetCode

Code
class Solution {
    public int[] runningSum(int[] nums) {
        int[] result = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            if (i == 0) {
                result[i] = nums[i];
            } else {
                result[i] = result[i-1] + nums[i];
            }
        }
        return result;
    }
}

Spring Data Reactive Cassandra CRUD

 Git branch


Cassandra commands
sudo docker run --name test-cassandra -p 9042:9042 -d cassandra:latest
sudo docker run --name test-cassandra-2 --link test-cassandra:cassandra -d cassandra:latest

docker exec -it test-cassandra /bin/bash

cqlsh> CREATE KEYSPACE test WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'datacenter1' : 3 } AND DURABLE_WRITES = false;

cqlsh> use test;

cqlsh:test> CREATE TABLE person ( id text PRIMARY KEY, name text );

Person.java
@Data
public class Person {

    @PrimaryKey private String id;
    private String name;

}

ReactivePersonRepository.java
public interface ReactivePersonRepository extends ReactiveCassandraRepository<Person, String> {}

ReactivePersonService.java
@Service
public class ReactivePersonService {

    @Autowired
    private ReactivePersonRepository reactivePersonRepository;

    public Mono<Person> save(Person person) {
        return reactivePersonRepository.save(person);
    }

    public Mono<Person> findById(String id) {
        return reactivePersonRepository.findById(id);
    }

    public Flux<Person> findAll() {
        return reactivePersonRepository.findAll();
    }

    public Mono<Void> deleteById(String id) {
        return reactivePersonRepository.deleteById(id);
    }

    public Mono<Void> deleteAll() {
        return reactivePersonRepository.deleteAll();
    }

}

ReactivePersonController.java
@RestController
@RequestMapping("/api/v2/")
public class ReactivePersonController {

    @Autowired
    private ReactivePersonService reactivePersonService;

    @PostMapping(value = "/person", consumes = "application/json")
    public Mono<Person> createPerson(@RequestBody Person person) {
        System.out.println("create person" + person);
        person.setId(UUID.randomUUID().toString());
        return reactivePersonService.save(person);
    }

    @GetMapping(value = "/person")
    public Mono<Person> getPerson(@RequestParam String id) {
        return reactivePersonService.findById(id);
    }

    @GetMapping(value = "/persons")
    public Flux<Person> getAllPersons() {
        return reactivePersonService.findAll();
    }

    @DeleteMapping("/person/{id}")
    public Mono<Void> deletePerson(@PathVariable String id) {
        return reactivePersonService.deleteById(id);
    }

    @DeleteMapping("/persons")
    public Mono<Void> deleteAll() {
        return reactivePersonService.deleteAll();
    }

express.js - body-parser - specific uri

 

Commit

code
var express = require('express');
var bodyParser = require('body-parser')
var app = express()
    .use( '/test', bodyParser() ).use(function (req, res) {
        console.log("body", req.body)
        console.log("foo", req.body.foo)
        res.send(req.body)
    })
    .listen(3000);

execute
$ node app.js

curl
$ curl -s  http://127.0.0.1:3000/test/  -H "content-type: application/json" -d   "{\"foo\":123}"
{"foo":123}

express.js - body-parser

Commit

npm install body-parser

code
var express = require('express');
var bodyParser = require('body-parser')
var app = express()
    .use( bodyParser() ).use(function (req, res) {
        console.log("body", req.body)
        console.log("foo", req.body.foo)
        res.send(req.body)
    })
    .listen(3000);

$ curl -s  http://127.0.0.1:3000/  -H "content-type: application/json" -d  "{\"foo\":123}"
{"foo":123}







express.js - serve-index - list files

  • Commit

  • install serve-index
serve-index (npm install serve-index)

  • list files 
var express = require('express');
var serveIndex = require('serve-index')
var app = express()
    .use( express.static(__dirname + '/public') ) // won't show serveIndex if specifying index files
    .use( serveIndex(__dirname + '/public', {}) )
    .listen(3000);

  • curl 
$ curl http://localhost:3000
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--      0<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1.0,  maximum-scale=1.0, user-scalable=no" />
    <title>listing directory /</title>
    <style>* {
  margin: 0;
  padding: 0;
  outline: 0;
}
body {
  padding: 80px 100px;
  font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
  background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff),  to(#ECE9E9));
  background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9);
  background-repeat: no-repeat;
  color: #555;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 {
  font-size: 22px;
  color: #343434;
}
h1 em, h2 em {
  padding: 0 5px;
  font-weight: normal;
}
h1 {
  font-size: 60px;
}
h2 {
  margin-top: 10px;
}
h3 {
  margin: 5px 0 10px 0;
  padding-bottom: 5px;
  border-bottom: 1px solid #eee;
  font-size: 18px;
}
ul li {
  list-style: none;
}
ul li:hover {
  cursor: pointer;
  color: #2e2e2e;
}
ul li .path {
  padding-left: 5px;
  font-weight: bold;
}
ul li .line {
  padding-right: 5px;
  font-style: italic;
}
ul li:first-child .path {
  padding-left: 0;
}
p {
  line-height: 1.5;
}
a {
  color: #555;
  text-decoration: none;
}
a:hover {
  color: #303030;
}
#stacktrace {
  margin-top: 15px;
}
.directory h1 {
  margin-bottom: 15px;
  font-size: 18px;
}
ul#files {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
ul#files li {
  float: left;
  width: 30%;
  line-height: 25px;
  margin: 1px;
}
ul#files li a {
  display: block;
  height: 25px;
  border: 1px solid transparent;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
  overflow: hidden;
  white-space: nowrap;
}
ul#files li a:focus,
ul#files li a:hover {
  background: rgba(255,255,255,0.65);
  border: 1px solid #ececec;
}
ul#files li a.highlight {
  -webkit-transition: background .4s ease-in-out;
  background: #ffff4f;
  border-color: #E9DC51;
}
#search {
  display: block;
  position: fixed;
  top: 20px;
  right: 20px;
  width: 90px;
  -webkit-transition: width ease 0.2s, opacity ease 0.4s;
  -moz-transition: width ease 0.2s, opacity ease 0.4s;
  -webkit-border-radius: 32px;
  -moz-border-radius: 32px;
  -webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px  rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
  -moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px  rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
  -webkit-font-smoothing: antialiased;
  text-align: left;
  font: 13px "Helvetica Neue", Arial, sans-serif;
  padding: 4px 10px;
  border: none;
  background: transparent;
  margin-bottom: 0;
  outline: none;
  opacity: 0.7;
  color: #888;
}
#search:focus {
  width: 120px;
  opacity: 1.0;
}
/*views*/
#files span {
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  text-indent: 10px;
}
#files .name {
  background-repeat: no-repeat;
}
#files .icon .name {
  text-indent: 28px;
}
/*tiles*/
.view-tiles .name {
  width: 100%;
  background-position: 8px 5px;
}
.view-tiles .size,
.view-tiles .date {
  display: none;
}
/*details*/
ul#files.view-details li {
  float: none;
  display: block;
  width: 90%;
}
ul#files.view-details li.header {
  height: 25px;
  background: #000;
  color: #fff;
  font-weight: bold;
}
.view-details .header {
  border-radius: 5px;
}
.view-details .name {
  width: 60%;
  background-position: 8px 5px;
}
.view-details .size {
  width: 10%;
}
.view-details .date {
  width: 30%;
}
.view-details .size,
.view-details .date {
  text-align: right;
  direction: rtl;
}
/*mobile*/
@media (max-width: 768px) {
  body {
    font-size: 13px;
    line-height: 16px;
    padding: 0;
  }
  #search {
    position: static;
    width: 100%;
    font-size: 2em;
    line-height: 1.8em;
    text-indent: 10px;
    border: 0;
    border-radius: 0;
    padding: 10px 0;
    margin: 0;
  }
  #search:focus {
    width: 100%;
    border: 0;
    opacity: 1;
  }
  .directory h1 {
    font-size: 2em;
    line-height: 1.5em;
    color: #fff;
    background: #000;
    padding: 15px 10px;
    margin: 0;
  }
  ul#files {
100  7241  100  7241    0     0  1010k      0 --:--:-- --:--:-- --:--:-- 1178k    border-top: 1px solid #cacaca;
  }
  ul#files li {
    float: none;
    width: auto !important;
    display: block;
    border-bottom: 1px solid #cacaca;
    font-size: 2em;
    line-height: 1.2em;
    text-indent: 0;
    margin: 0;
  }
  ul#files li:nth-child(odd) {
    background: #e0e0e0;
  }
  ul#files li a {
    height: auto;
    border: 0;
    border-radius: 0;
    padding: 15px 10px;
  }
  ul#files li a:focus,
  ul#files li a:hover {
    border: 0;
  }
  #files .header,
  #files .size,
  #files .date {
    display: none !important;
  }
  #files .name {
    float: none;
    display: inline-block;
    width: 100%;
    text-indent: 0;
    background-position: 0 50%;
  }
  #files .icon .name {
    text-indent: 41px;
  }
}
</style>
    <script>
      function $(id){
        var el = 'string' == typeof id
          ? document.getElementById(id)
          : id;
        el.on = function(event, fn){
          if ('content loaded' == event) {
            event = window.attachEvent ? "load" : "DOMContentLoaded";
          }
          el.addEventListener
            ? el.addEventListener(event, fn, false)
            : el.attachEvent("on" + event, fn);
        };
        el.all = function(selector){
          return $(el.querySelectorAll(selector));
        };
        el.each = function(fn){
          for (var i = 0, len = el.length; i < len; ++i) {
            fn($(el[i]), i);
          }
        };
        el.getClasses = function(){
          return this.getAttribute('class').split(/\s+/);
        };
        el.addClass = function(name){
          var classes = this.getAttribute('class');
          el.setAttribute('class', classes
            ? classes + ' ' + name
            : name);
        };
        el.removeClass = function(name){
          var classes = this.getClasses().filter(function(curr){
            return curr != name;
          });
          this.setAttribute('class', classes.join(' '));
        };
        return el;
      }
      function search() {
        var str = $('search').value.toLowerCase();
        var links = $('files').all('a');
        links.each(function(link){
          var text = link.textContent.toLowerCase();
          if ('..' == text) return;
          if (str.length && ~text.indexOf(str)) {
            link.addClass('highlight');
          } else {
            link.removeClass('highlight');
          }
        });
      }
      $(window).on('content loaded', function(){
        $('search').on('keyup', search);
      });
    </script>
  </head>
  <body class="directory">
    <input id="search" type="text" placeholder="Search" autocomplete="off" />
    <div id="wrapper">
      <h1><a href="/">~</a> / </h1>
      <ul id="files" class="view-tiles"><li><a href="/lalala.html" class=""  title="lalala.html"><span class="name">lalala.html</span><span  class="size">168</span><span class="date">2020-7-29 1:04:56 ├F10:  AM┤</span></a></li>
<li><a href="/test.html" class="" title="test.html"><span  class="name">test.html</span><span class="size">147</span><span  class="date">2020-7-24 1:46:51 ├F10: AM┤</span></a></li></ul>
    </div>
  </body>
</html>


express.js - express.static - index file

  • Commit


  • use express.static
var express = require('express');  
var app = express()  
            .use(  express.static(__dirname + '/public')  )  
            .listen(3000);


express.js - serveStatic - index file

  • specufy in the second argument
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index - lalala</title>
</head>
<body>
This is index page - lalala
</body>
</html>

var express = require('express');
var serveStatic =  require('serve-static');
var app = express().use(  serveStatic(__dirname + '/public', {'index': ['lalala.html']})  ).listen(3000);

$curl http://localhost:3000
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index - lalala</title>
</head>
<body>
This is index page - lalala
</body>
</html>

express.js - helloworld

Commit

npm install
npm install express
npm install serve-static

public file: public/test.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Helloworld</title>
</head>
<body>
Helloworld
</body>
</html>

app.js
var express = require('express');
var serveStatic =  require('serve-static');
var app = express().use(  serveStatic(__dirname + '/public')  ).listen(3000);

Run
node app.js

Browse
$ curl http://localhost:3000/test.html
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   147  100   147    0     0  29400      0 --:--:-- --:--:-- --:--:--  36750<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Helloworld</title>
</head>
<body>
Helloworld
</body>
</html>





inquirer - mark todo item done

By ‘checkbox’ type can choose items

Commit

Code
import * as inquirer from "inquirer"

// Object.values(Commands) will become [List, Quit, 1, 2] without specifying string value
enum Commands {
    Add = "Add",
    Done = "Done",
    Quit = "Quit"
}

let todo: Array<Task> = []

promptList()

class Task {
    done: boolean = false;
    constructor(public name:string) {
    }
    markDone() {
        this.done = true
    }
    toString = ():string => {
        return this.name + "=>" + (this.done ? "done" : "not done")
    }
}

function listTodo() {
    todo.forEach(item => {
        console.log(`${item}`)
    })
}

function promptDone() {
    inquirer.prompt({
        type: "checkbox",
        name: "check",
        message: "Mark done:",
        choices: todo.map(item => ({
            name: item.name,
            value: item.name,
            checked: item.done
        }))
    }).then(answers => {
        console.log(answers)
        let checkedItems = answers['check'] as string[]
        if (answers['check'] != "") {
            todo.forEach(item => item.done = false)
            todo.forEach(item => {
                checkedItems.forEach(checkedItem => {
                    if (item.name == checkedItem) {
                        item.done = true
                    }
                })
            })
        }
        promptList();
    })
}

function promptAdd() {
    inquirer.prompt({
        type: "input",
        name: "add",
        message: "Input TODO:",
    }).then(answers => {
        if (answers['add'] != "") {
            todo.push(new Task(answers['add']))
        }
        promptList();
    })
}

function promptList() {
    console.clear();
    listTodo();
    inquirer.prompt({
        type: "list",
        name: "command",
        message: "Choose option",
        choices: Object.values(Commands)
    }).then(answers => {
        switch (answers["command"]) {
            case Commands.Add:
                promptAdd();
                break;
            case Commands.Done:
                promptDone();
                break;
            case Commands.Quit:
                console.log("Select Quit")
                break;
            default:
                console.log("Missing " + answers["command"])
        }
    })
}

Inquirer - add item to list

Use "input" type in inquirer can support input information

Commit

Code
import * as inquirer from "inquirer"

// Object.values(Commands) will become [List, Quit, 1, 2] without specifying string value
enum Commands {
    AddTodo = "AddTodo",
    Quit = "Quit"
}

promptList()

var todo: Array<string> = []

function listTodo() {
    console.log(todo)
}

function promptAdd() {
    inquirer.prompt({
        type: "input",
        name: "add",
        message: "Input TODO:",
    }).then(answers => {
        if (answers['add'] != "") {
            todo.push(answers['add'])
        }
        promptList();
    })
}

function promptList() {
    console.clear();
    listTodo();
    inquirer.prompt({
        type: "list",
        name: "command",
        message: "Choose option",
        choices: Object.values(Commands)
    }).then(answers => {
        switch (answers["command"]) {
            case Commands.AddTodo:
                promptAdd();
                break;
            case Commands.Quit:
                console.log("Select Quit")
                break;
            default:
                console.log("Missing " + answers["command"])
        }
    })
}




Convert mov to gif in Mac

Motivation
File extension of screen record in Mac is .mov (By QuickTime)
Can use ffmpeg to convert to gif to reduce file size

Install ffmpeg
brew install ffmpeg

Convert mov to gif
ffmpeg -i AM.mov output.gif

Reference

inquirer - simple list and quit command

Git Repository

Install libraries
npm install inquirer
npm install typescript

List and Quit commands
import * as inquirer from "inquirer"


// Object.values(Commands) will become [List, Quit, 1, 2] without specifying string value
enum Commands {
    List = "List",
    Quit = "Quit"
}


inquirer.prompt({
    type: "list",
    name: "command",
    message: "Choose option",
    choices: Object.values(Commands)
}).then(answers => {
    switch (answers["command"]) {
        case Commands.List:
            console.log("Select List")
            break;
        case Commands.Quit:
            console.log("Select Quit")
            break;
        default:
            console.log("Missing " + answers["command"])
    }
})

Config Wireguard

Wireguard
Wireguard is a VPN software, which is included in Linux 5.6 kernel 

Install ubuntu 18.04
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install openssh-server

$ sudo add-apt-repository ppa:wireguard/wireguard
$ sudo apt-get update
$ sudo apt-get install wireguard

Open /etc/gai.conf
Uncomment following line
#
# For sites which prefer IPv4 connections change the last line to
#
precedence ::ffff:0:0/96 100

Enable ip forward in server and reboot, so that packet can be forwarded from default gateway to other interface with same subnet
echo "net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1" > /etc/sysctl.d/wg.conf

Gen key for server and client
$ umask 077
$ sudo wg genkey > private
$ sudo wg pubkey < private > public

Deploy server config
File: /etc/wireguard/wg0.conf
MASQUERADE: packet’s ip header will be changed to private ip and restore to public ip when writing back
10.0.0.1 can be freely configured, only need to make sure peers are in the same subnet
[Interface]
Address = 10.0.0.1/24
#SaveConfig = true
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT && iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE && iptables -A INPUT -i wg0 -p udp --dport 51820 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT && iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE && iptables -D INPUT -i wg0 -p udp --dport 51820 -j ACCEPT
ListenPort = 51820
PrivateKey = aP7Y6f0ubHbweFSs5EouXsT+klvsp2iFRZsmuBz+IHQ=

[Peer]
PublicKey = utH967EMNmx3Of9Breqp27T8+ZCOs1nawsmk+HpCLCY=
AllowedIPs = 10.0.0.2/32

Deploy client config
File: /etc/wireguard/client.conf
[Interface]
Address = 10.0.0.2/24
PrivateKey = WHLj16xU6/dq59Qks8Zn14vCjk3PMc7o4Pjm6lktfmE=
DNS = 1.1.1.1

[Peer]
PublicKey = 3GODl2zWseKTpRRiArn00TEZHw9qs0oOxD1AF4gcv3c=
AllowedIPs = 0.0.0.0/0
Endpoint = 10.247.33.177:51820

Gen QRCode
$ sudo apt install qrencode
$ qrencode -t ansiutf8 < /etc/wireguard/client.conf

Start server
$ sudo wg-quick up wg0

Start client
$ sudo wg-quick up client

Wireguard do handshake through UDP protocol, so client connect successfully doesn’t mean VPN connection work.
Can debug by ip ping, route, traceroute commands to make sure peers can be connected.

Lessons Learned While Benchmarking vLLM with GPU

Recently, I benchmarked vLLM on a GPU to better understand how much throughput can realistically be expected in an LLM serving setup. One ...