75
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

mongo shellの操作まとめ

Posted at

Mongo shellの操作まとめ

下記の環境で動作確認を行いました。

  • Windows7 (64bit)
  • MongoDB 3.0.2

この記事の説明で使用するコレクション

  • データベース: test
  • コレクション名: product
Field Data Type Desc
no no 品番
name string 品名
price int 価格
variety array (*)バラエティ
snack boolean 食用油で揚げている菓子類
drink boolean 清涼飲料水類
activate boolean true:取扱可、false:不可
update_dt date 更新日

バラエティ配列の要素は下記のオブジェクト

Field Data Type Desc
flavor string
ppl int 人気度(3:大、2:中、1:小)

サンプルデータの準備

この後の説明で使用するデータを下記のように作成します。

sample_data
> use test
> db.product.insert([
{no:1, name:"うまい棒", price:10, variety:[{flavor:"メンタイ", ppl:3},{flavor:"サラミ", ppl:3},{flavor:"ピザ", ppl:2},{flavor:"オニオンサラダ", ppl:2},{flavor:"コーンポタージュ", ppl:1}], snack:true, activate:true, update_dt:ISODate("2015-01-01T00:00:00+09:00") },
{no:2, name:"ポテトフライ", price:30, variety:[{flavor:"ステーキ", ppl:2},{flavor:"フライドチキン", ppl:1},{flavor:"カルビ", ppl:2}], snack:true, activate:false, update_dt:ISODate("2015-01-15T00:00:00+09:00") },
{no:3, name:"きなこ棒", price:10, activate:true, update_dt:ISODate("2015-01-31T00:00:00+09:00")},
{no:4, name:"生いきビール", price:40, drink:true, activate:false, update_dt:ISODate("2015-02-01T00:00:00+09:00")},
{no:5, name:"フルーツヨーグル", price:20, activate:true, update_dt:ISODate("2015-02-15T00:00:00+09:00")},
{no:6, name:"コーヒー牛乳キャンディ", price:10, activate:true, update_dt:ISODate("2015-02-28T00:00:00+09:00")},
{no:7, name:"ミニコーラ", price:30, activate:true, update_dt:ISODate("2015-03-01T00:00:00+09:00")},
{no:8, name:"フエラムネ", price:60, variety:[{flavor:"コーラ", ppl:3},{flavor:"いちご", ppl:1}], activate:true, update_dt:ISODate("2015-03-15T00:00:00+09:00")},
{no:9, name:"こざくら餅", price:20, variety:[{flavor:"グレープ", ppl:2},{flavor:"コーラ", ppl:3},{flavor:"フルーツソーダ", ppl:1},{flavor:"ミックス", ppl:2}], activate:true, update_dt:ISODate("2015-03-31T00:00:00+09:00")},
{no:10,name:"たまごアイス", price:10, variety:[{flavor:"ミルク", ppl:1},{flavor:"バニラ", ppl:3},{flavor:"チョコレート", ppl:2}], activate:true, update_dt:ISODate("2015-04-01T00:00:00+09:00")},
{no:11,name:"餅太郎", price:30, activate:true, update_dt:ISODate("2015-04-15T00:00:00+09:00")},
{no:12,name:"あんこ玉", price:10, activate:true, update_dt:ISODate("2015-04-30T00:00:00+09:00")},
{no:13,name:"ラムネ", price:30, drink:true, activate:true, update_dt:ISODate("2015-05-01T00:00:00+09:00")},
{no:14,name:"みかん水", price:40, drink:true, activate:true, update_dt:ISODate("2015-05-15T00:00:00+09:00")},
{no:15,name:"さくら大根", activate:false, update_dt:ISODate("2015-05-15T00:00:00+09:00"), desc:"価格不明"}
])
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 15,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})

collection

createCollection()

definition
db.createCollection(name, options)

コレクションの作成はcreateCollection()を使用して明示的に作成する方法とinsert()で暗黙的に作成する方法があります。

コレクション名を指定してコレクションを作成します。

> db.createCollection("product")
{ "ok" : 1 }

insert時にコレクションが存在しなければ暗黙的に作成します。

> db.product.insert({...})

コレクション作成時に指定するオプションでCapped collectionを作成することができます。
この例ではコレクションサイズの上限を1GB、ドキュメント数を100万件に指定しています。

> db.createCollection("product", {capped:true, size:1073741824, max:1000000})
{ "ok" : 1 }

options (一部)

Field Type Description
capped Boolean Optional. true/false. trueにした場合sizeの指定も 必要.
autoIndexId Boolean Optional. true/false. falseにした場合_idインデックスは作成されません。
size Number Optional. コレクションの最大byte数. 最小4KBで256の整数倍で指定.
max Number Optional. コレクションの最大ドキュメント数. max値を超えてドキュメントを登録すると古いドキュメントから削除される.

:exclamation: capped collectionはremove()でドキュメントを削除することはできません。

> db.product.remove({_id:ObjectId("55537369d5ce22def8f104e1")})
WriteResult({
        "nRemoved" : 0,
        "writeError" : {
                "code" : 20,
                "errmsg" : "cannot remove from a capped collection: test.product"
        }
})

MongoDB Reference - db.createCollection
MongoDB Manual - Capped Collections

stats()

syntax
db.collection.stats(options)

コレクションの状態はstats()で確認できます。(下記はサンプルデータ登録後の状態です。)

> db.product.stats()
{
        "ns" : "test.product",
        "count" : 15,
        "size" : 3856,
        "avgObjSize" : 257,
        "numExtents" : 1,
        "storageSize" : 8192,
        "lastExtentSize" : 8192,
        "paddingFactor" : 1,
        "paddingFactorNote" : "paddingFactor is unused and unmaintained in 3.0.
It remains hard coded to 1.0 for compatibility only.",
        "userFlags" : 1,
        "capped" : false,
        "nindexes" : 1,
        "totalIndexSize" : 8176,
        "indexSizes" : {
                "_id_" : 8176
        },
        "ok" : 1
}
Field Description
ns namespace
count number of documents
size collection size in bytes
avgObjSize average object size in bytes
numExtents The total number of contiguously allocated data file regions.
storageSize (pre)allocated space for the collection in bytes
lastExtentSize The size of the last extent allocated.
paddingFactor Deprecated since version 3.0.0
paddingFactorNote
userFlags A number that indicates the user-set flags on the collection.
capped This field will be “true” if the collection is capped.
max Shows the maximum number of documents that may be present in a capped collection.
maxSize Shows the maximum size of a capped collection.
nindexes number of indexes
totalIndexSize total index size in bytes
indexSizes size of specific indexes in bytes

MongoDB Reference - coll Stats

オプションでscaleを1024に設定するとデータサイズがKB単位で表示されます。

> db.product.stats({scale:1024})

その他にも下記の状態確認用メソッドがあります。

> db.product.isCapped()
false
> db.product.dataSize()
3856
> db.product.storageSize()
8192
> db.product.totalSize()
16368
> db.product.totalIndexSize()
8176

find()

definition
db.collection.find(<query>)
db.collection.find(<query>, <projection>)

コレクションからドキュメントを検索します。

無条件で検索する

> db.product.find()

または{}を明示的に渡すことでも無条件の検索になります。

> db.product.find( {} )

条件を指定して検索する

検索条件をfind()の第1引数(query)に記述します。
この例では_idを指定して検索します。

> db.product.find(
  {_id: ObjectId("5553d954e8b70b81d56a0383")}
)

nameフィールドが"うまい棒"と等価なドキュメントを検索します。

> db.product.find( {name:"うまい棒"} )

priceフィールドが10と等価なドキュメントを検索します。

> db.product.find( {price:10} )

ただし、次のような比較演算子を使った検索は行えません。
このような検索は後述するQuery Selectorsを使用します。

> db.product.find( {price: >= 10} )
2015-05-17T07:21:21.726+0900 E QUERY    SyntaxError: Unexpected token >=

配列要素に対して検索する

フィールドに配列を記述することで配列要素に対する検索が行えます。

> db.product.find( {"variety.flavor":"コーラ"} )

添え字を指定して検索することができます。
この例ではvariety[0].flavorが"コーラ"のドキュメントを検索します。

> db.product.find( {"variety.0.flavor":"コーラ"} )

Query Selectors

$eq,$gte,$existsなどをQuery Selectorといいます。

Comparison Query Operators

$eq, &ne, $gt, $gte, $lt, $lte, $in, $nin

$eq

$eqを使用したnameフィールドが"うまい棒"の検索です。

> db.product.find( {name: {$eq:"うまい棒"}} )
$gte

priceフィールドが50以上のドキュメントを検索します。

> db.product.find( {price: {$gte:50}} )
$lte

priceフィールドが30以下のドキュメントを検索します。

> db.product.find( {price: {$lte:30}} )

priceフィールドが20以上かつ40以下のドキュメントを検索します。

> db.product.find( {price: {$gte:20, $lte:40}} )
$in
syntax
{ field: { $in: [ <value1>, <value2>, ... <valueN> ] } }

priceフィールドが20か40のドキュメントを検索します。

> db.product.find( {price: {$in:[20,40]}} )
$nin
syntax
{ field: { $nin: [ <value1>, <value2> ... <valueN> ] } }

priceフィールドが30および50以外のドキュメントを検索します。
なお、この例のようにpriceフィールドが存在しないドキュメントも検索されます。

> db.product.find( {price: {$nin:[30,50]}} )

priceフィールドが存在しないドキュメントを除外したい場合は$existsも使用します。

> db.product.find(
  {$and:[ {price: { $nin:[30,50] }},
          {price: { $exists:true} } ]}
)

Logical Query Operators

$or, $and, $not, $nor

$or
syntax
{ $or: [ {<expression1>}, {<expression2>}, ... , {<expressionN>} ] }

update_dtフィールドが4/30か5/01のドキュメントを検索します。

> db.product.find(
  {$or:[ {update_dt:{$eq:ISODate("2015-04-30T00:00:00+09:00")}},
         {update_dt:{$eq:ISODate("2015-05-01T00:00:00+09:00")}} ]}
)
$and
syntax
{ $and: [ {<expression1>}, {<expression2>} , ... , {<expressionN>} ] }

priceフィールドが10で且つupdate_dtフィールドが1/31のドキュメントを検索します。

> db.product.find(
  {$and:[ {price:{$eq:10}},
          {update_dt:{$eq:ISODate("2015-01-31T00:00:00+09:00")}} ]}
)
$not
syntax
{ field: { $not: {<operator-expression>} } }

priceフィールドが10ではないドキュメントを検索します。
$ninと同様にpriceフィールドが存在しないドキュメントも検索されます。

> db.product.find( {price: { $not: {$eq:10} }} )

priceフィールドが存在しないドキュメントを除外したい場合は$existsも使用します。

> db.product.find(
  {$and:[ {price: { $not:{$eq:10} }},
          {price: { $exists:true} } ]}
)
$nor

与えられた式をすべて満たさないドキュメントを検索します。

syntax
{ $nor: [ {<expression1>}, {<expression2>}, ...  {<expressionN>} ] }

この例ではこれらの条件を満たすドキュメントを検索します。

  • priceフィールドが30では無く且つdrinkフィールドがtrueでは無い
  • priceフィールドが30では無く且つdrinkフィールドが存在しない
  • priceフィールドが存在しなく且つdrinkフィールドがtrueでは無い
  • priceフィールドもdrinkフィールドも存在しない
> db.product.find(
  {$nor: [{price:30},
          {drink:true}]}
)

Element Query Operators

$exists, $type

$exists
syntax
{ field: { $exists: <boolean> } }

drinkフィールドが存在するドキュメントを検索します。

> db.product.find( {drink: {$exists:true}} )
$type
syntax
{ field: { $type: <BSON type> } }

update_dtフィールドがdate型(9)のドキュメントを検索します。

> db.product.find( {update_dt:{$type:9}} )

Available Types

Type Number Notes
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 Deprecated.
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255 Query with -1.
Max key 127

MongoDB Reference - BSON TYPE

Evaluation Query Operators

$mod, $regex, $text, $where

$mod

剰余が指定した値と同じドキュメントを検索します。

syntax
{ field: { $mod: [ divisor, remainder ] } }

noフィールドが5の整数倍のドキュメントを検索します。

> db.product.find( {no: {$mod:[5, 0]} } )
$regex

検索条件に正規表現を使用します。

syntax
{ <field>: { $regex: /pattern/, $options: '<options>' } }
{ <field>: { $regex: 'pattern', $options: '<options>' } }
{ <field>: { $regex: /pattern/<options> } }
$text

全文検索を行います。そのためにはtext indexを作成する必要があります。(現時点では日本語は未対応の模様)

syntax
{ $text: { $search: <string>, $language: <string> } }

[MongoDB reference - Text search Languages] (http://docs.mongodb.org/manual/reference/text-search-languages/)

$where

条件にjavascirptを使用します。

syntax
{ $where: "javascript" }

priceフィールドが10以上20以下のドキュメントを検索します。

> db.product.find(
  {$where:"this.price >= 10 && this.price <= 20"}
)

Array Query Operators

$all,$elemMatch,$size

$all

指定された要素をすべて含む配列を持つドキュメントを検索します。

syntax
{ <field>: { $all: [ <value1> , <value2> ... ] } }
> db.product.find(
  {"variety.flavor": { $all: ["コーラ", "グレープ"] } }
)

$allを使用しなくても同様の検索を行えます。

> db.product.find(
  {$and: [ {"variety.flavor":"コーラ"},
           {"variety.flavor":"グレープ"} ] }
)
$elemMatch

条件を満たす配列を持つドキュメントを検索します。

syntax
{ <field>: { $elemMatch: { <query1>, <query2>, ... } } }
> db.product.find(
  {variety:{ $elemMatch:{ flavor:{$eq:"コーラ"}, ppl:{$gte:3} } }}
)

$elemMatchを使用しなくても同様の検索を行えます。

> db.product.find(
  {$and:[ {"variety.flavor":{$eq:"コーラ"}},
          {"variety.ppl":{$gte:3}}]}
)
$size

指定する要素数の配列を持つドキュメントを検索します。

syntax
{ <field>: { $size: <number> } }
> db.product.find(
  {variety:{ $size:3 }}
)

取得するフィールドを選択する

取得するフィールドをfind()の第2引数(projection)に記述します。フィールド名とフラグ(1:取得、0:除外)を指定します。(_idは指定しなくても含まれるようです。)

> db.product.find({}, {name:1, price:1})

フィールドに0を指定すると除外することができます。この例では_idを除外しています。

> db.product.find({}, {_id:0, name:1, price:1})

MongoDB Reference - find
MongoDB Reference - Query and Projection Operators

findOne()

definition
db.collection.findOne(<query>)
db.collection.findOne(<query>, <projection>)

find()と同じ使い方ができますが、メソッドの返り値はドキュメントになります。

無条件で検索する

> db.product.findOne()

または

> db.product.findOne({})

返り値はドキュメントなのでフィールドにアクセスできます。

> db.product.findOne({}).name
うまい棒

条件を指定して検索する

条件に一致するドキュメントが複数あっても1件しか取得しません。

> db.product.findOne({price:10})

取得するフィールドを選択する

> db.product.findOne({},{name:1, price:1})

MongoDB Reference - findOne

findAndModify()

definition
db.collection.findAndModify({
    query: <document>,
    sort: <document>,
    remove: <boolean>,
    update: <document>,
    new: <boolean>,
    fields: <document>,
    upsert: <boolean>
})

取得と更新を行う

nameフィールドが"うまい棒"のドキュメントを返すと共にpriceフィールドを30に更新します。

> db.product.findAndModify({
  query: {name:"うまい棒"},
  update: {$set:{price:30}}
})
{
        "_id" : ObjectId("5557d59cb636d6666833fb01"),
        "no" : 1,
        "name" : "うまい棒",
        "price" : 10,
        "variety" : [
                {
                        "flavor" : "メンタイ",
                        "ppl" : 3
                },
                {
                        "flavor" : "サラミ",
                        "ppl" : 3
                },
                {
                        "flavor" : "ピザ",
                        "ppl" : 2
                },
                {
                        "flavor" : "オニオンサラダ",
                        "ppl" : 2
                },
                {
                        "flavor" : "コーンポタージュ",
                        "ppl" : 1
                }
        ],
        "snack" : true,
        "activate" : true,
        "update_dt" : ISODate("2014-12-31T15:00:00Z")
}

findAndModify()が更新するドキュメントの件数は1件だけです。もしqueryの条件が複数のドキュメントに一致した場合、更新は最初のドキュメントに行います。

この例ではpriceフィールドが60のドキュメントを対象にしていますが、noフィールドの降順にソートしているため、更新されるのは"すこんぶ"になります。

/* priceフィールドが60のドキュメントは2件 */
> db.product.find({price:60},{_id:0,no:1,name:1,price:1})
{ "no" : 8, "name" : "フエラムネ", "price" : 60 }
{ "no" : 18, "name" : "すこんぶ", "price" : 60 }
> db.product.findAndModify({
  query: {price:60},
  update: {$set:{price:65}},
  sort: {no:-1}
})
{
        "_id" : ObjectId("5557c76db636d6666833fb00"),
        "no" : 18,
        "name" : "すこんぶ",
        "activate" : true,
        "update_dt" : ISODate("2015-05-31T15:00:00Z"),
        "price" : 60
}

queryの条件が一致するドキュメントが存在しなかった場合は、更新も追加もされません。

> db.product.findAndModify({
  query: {no:16},
  update: {no:16, name:"ボンタンアメ", price:100, activate:false, update_dt:ISODate("2015-05-15T00:00:00+09:00")}
})
null

/* 追加されていないのでドキュメントは見つかりません */
> db.product.find({no:16},{_id:0,no:1,name:1,price:1})
>

upsert:trueを指定すると、queryの条件に一致するドキュメントが見つからなかった場合に追加を行います。

> db.product.findAndModify({
  query: {no:16},
  update: {no:16, name:"ボンタンアメ", price:100, activate:false, update_dt:ISODate("2015-05-15T00:00:00+09:00")},
  upsert:true
})
null

/* upsert:trueを指定することでドキュメントを追加することができました */
> db.product.find({no:16},{_id:0,no:1,name:1,price:1})
{ "no" : 16, "name" : "ボンタンアメ", "price" : 100 }

new:trueを指定すると、更新後の(追加した場合は追加した)ドキュメントを返します。(追加の場合はnullを返します。)

> db.product.findAndModify({
  query: {no:17},
  update: {no:17, name:"スモモちゃん", price:40, activate:false, update_dt:ISODate("2015-05-30T00:00:00+09:00")},
  upsert: true,
  new: true
})
{
        "_id" : ObjectId("5557c5e436efe7aa0e474d31"),
        "no" : 17,
        "name" : "スモモちゃん",
        "price" : 40,
        "activate" : false,
        "update_dt" : ISODate("2015-05-29T15:00:00Z")
}

取得と削除を行う

nameフィールドが"うまい棒"のドキュメントを返すと共にそのドキュメントを削除します。

> db.product.findAndModify({
  query: {name:"うまい棒"},
  remove: true
})
{
        "_id" : ObjectId("5557d59cb636d6666833fb01"),
        "no" : 1,
        "name" : "うまい棒",
        "price" : 30,
        "variety" : [
                {
                        "flavor" : "メンタイ",
                        "ppl" : 3
                },
                {
                        "flavor" : "サラミ",
                        "ppl" : 3
                },
                {
                        "flavor" : "ピザ",
                        "ppl" : 2
                },
                {
                        "flavor" : "オニオンサラダ",
                        "ppl" : 2
                },
                {
                        "flavor" : "コーンポタージュ",
                        "ppl" : 1
                }
        ],
        "snack" : true,
        "activate" : true,
        "update_dt" : ISODate("2014-12-31T15:00:00Z")
}
/* 削除されたのでドキュメントは見つかりません */
> db.product.find({name:"うまい棒"})
>

[MongoDB Reference - findAndModify] (http://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/)

cursorオブジェクト

find()はcursorオブジェクトを返します。cursorオブジェクトにはcount()forEach()limit()sort()などのメソッドがあります。
なお、findOne()はcursorオブジェクトではなくdocumentオブジェクトを返します。

下記のようにcursorオブジェクトを変数に保存して処理を行うことができます。

> var c = db.product.find().limit(3)
> while (c.hasNext()) { print(c.next().name); }
ポテトフライ
きなこ棒
生いきビール

cursorオブジェクトの主なメソッド

count()

カーソルが返すドキュメントの件数を取得します。

> db.product.find().count()
17

コレクションにもcount()がありますので、下記のようにしても件数を取得することができます。

> db.product.count()
17

コレクションのcount()は引数にqueryを渡すことが可能です。

> db.product.count({price:10})
5

findOne()はcursorオブジェクトを返さないためエラーになります。

> db.product.findOne().count()
2015-05-17T07:35:12.954+0900 E QUERY    TypeError: Object [object Object] has no
 method 'count'
    at (shell):1:22

forEach()

検索したドキュメント個々に対して任意の処理を実行します。

> db.product.find().forEach(function(doc){ print(doc.name); })
うまい棒
ポテトフライ
きなこ棒
生いきビール
フルーツヨーグル
コーヒー牛乳キャンディ
ミニコーラ
フエラムネ
こざくら餅
たまごアイス
餅太郎
あんこ玉
ラムネ
みかん水
さくら大根
ボンタンアメ
スモモちゃん

hasNext() / next()

> var cursor = db.product.find()
> while(cursor.hasNext()) {
    printjsononeline(cursor.next());
}

limit()

カーソルが返すドキュメントの最大件数を設定します。

> db.product.find({},{_id:0,no:1, name:1,price:1}).limit(5)
{ "no" : 1, "name" : "うまい棒", "price" : 10 }
{ "no" : 2, "name" : "ポテトフライ", "price" : 30 }
{ "no" : 3, "name" : "きなこ棒", "price" : 10 }
{ "no" : 4, "name" : "生いきビール", "price" : 40 }
{ "no" : 5, "name" : "フルーツヨーグル", "price" : 20 }

sort()

カーソルが返すドキュメントの並び順を指定します。ソートしたいフィールド名と並び順を「1:昇順」「-1:降順」で指定します。

> db.product.find({},{_id:0,no:1, name:1,price:1}).sort({price:1, no:1})
{ "no" : 15, "name" : "さくら大根" }
{ "no" : 1, "name" : "うまい棒", "price" : 10 }
{ "no" : 3, "name" : "きなこ棒", "price" : 10 }
{ "no" : 6, "name" : "コーヒー牛乳キャンディ", "price" : 10 }
{ "no" : 10, "name" : "たまごアイス", "price" : 10 }
{ "no" : 12, "name" : "あんこ玉", "price" : 10 }
{ "no" : 5, "name" : "フルーツヨーグル", "price" : 20 }
{ "no" : 9, "name" : "こざくら餅", "price" : 20 }
{ "no" : 2, "name" : "ポテトフライ", "price" : 30 }
{ "no" : 7, "name" : "ミニコーラ", "price" : 30 }
{ "no" : 11, "name" : "餅太郎", "price" : 30 }
{ "no" : 13, "name" : "ラムネ", "price" : 30 }
{ "no" : 4, "name" : "生いきビール", "price" : 40 }
{ "no" : 14, "name" : "みかん水", "price" : 40 }
{ "no" : 17, "name" : "スモモちゃん", "price" : 40 }
{ "no" : 8, "name" : "フエラムネ", "price" : 60 }
{ "no" : 16, "name" : "ボンタンアメ", "price" : 100 }

skip()

カーソルが返すドキュメントを指定した件数だけskipします。

> db.product.find({},{_id:0,no:1,name:1,price:1}).sort({no:1}).skip(5).limit(3)
{ "no" : 6, "name" : "コーヒー牛乳キャンディ", "price" : 10 }
{ "no" : 7, "name" : "ミニコーラ", "price" : 30 }
{ "no" : 8, "name" : "フエラムネ", "price" : 60 }

explain()

クエリの実行計画を確認します。

> db.product.find().explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "test.product",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "$and" : [ ]
                },
                "winningPlan" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                                "$and" : [ ]
                        },
                        "direction" : "forward"
                },
                "rejectedPlans" : [ ]
        },
        "serverInfo" : {
                "host" : "localhost",
                "port" : 27017,
                "version" : "3.0.2",
                "gitVersion" : "6201872043ecbbc0a4cc169b5482dcf385fc464f"
        },
        "ok" : 1
}

MongoDB Manual - Explan Results

pretty()

ドキュメントを見やすいフォーマットに整形して出力します。

> db.product.find({no:1},{name:1,price:1,variety:1}).pretty()
{
        "_id" : ObjectId("5557c280b636d6666833faf1"),
        "name" : "うまい棒",
        "price" : 10,
        "variety" : [
                {
                        "flavor" : "メンタイ",
                        "ppl" : 3
                },
                {
                        "flavor" : "サラミ",
                        "ppl" : 3
                },
                {
                        "flavor" : "ピザ",
                        "ppl" : 2
                },
                {
                        "flavor" : "オニオンサラダ",
                        "ppl" : 2
                },
                {
                        "flavor" : "コーンポタージュ",
                        "ppl" : 1
                }
        ]
}

[MongoDB Manual - Cursors] (http://docs.mongodb.org/manual/core/cursors/)
MongoDB Reference - Cursor methods

insert()

definition
db.collection.insert(
    <document or array of documents>,
    { writeConcern: <document>, ordered: <boolean>  }
)

新規作成

ドキュメントを作成します。

> db.product.insert({no:18, name:"すこんぶ", price:60, activate:true, update_dt:ISODate("2015-06-01T00:00:00+09:00")})
WriteResult({ "nInserted" : 1 })

insert()は実行結果をWriteResultオブジェクトとして返します。

> var ws = db.product.insert({no:18, name:"すこんぶ", price:60, activate:true, update_dt:ISODate("2015-06-01T00:00:00+09:00")})
> printjson(ws)
{ "nInserted" : 1 }

bulk insert

複数件をまとめてインサートするにはデータを配列で渡します。

> db.product.insert([
  {...},
  {...},
  {...}
])

または、bulkオペレーションメソッドを使用します。

> var bulk = db.product.initializeOrderedBulkOp()
> bulk.insert({...})
> bulk.insert({...})
> bulk.insert({...})
> bulk.execute()

bulkインサートは実行結果をBulkWriteResultとして返します。

> var ws = bulk.execute()
> printjson(ws)
{
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 14,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
}

MongoDB Reference - insert
MongoDB Reference - Bulk Operation Methods

update()

definition
db.collection.update(
   <query>,
   <update>,
   { upsert: <boolean>, multi: <boolean>, writeConcern: <document> }
)

フィールドの更新

update()の第1引数(query)に更新条件を、第2引数(update)に更新内容を記述します。
$setを使用するとフィールドの値を置換することができます。

> db.product.findOne({name:"うまい棒"}).price
10
> db.product.update(
  {name:"うまい棒"},
  {$set:{price:25}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.findOne({name:"うまい棒"}).price
25

$setを使用せず次のような操作を行うとドキュメント自体が更新されます。

> db.product.update(
  {_id: ObjectId("5557c280b636d6666833faf1"},
  {price:35}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.findOne({_id:ObjectId("5557c280b636d6666833faf1")})
{ "_id" : ObjectId("5557c280b636d6666833faf1"), "price" : 35 }

Update Operators

$set,$unset,$incなどをUpdate Operatorといいます。

$set

上記のとおりです。

$inc

$incを使用するとフィールドの値をインクリメントすることができます。(マイナス値を使用すればデクリメントもできます。)

この例では、"きなこ棒"のpriceフィールドに5を加算します。

> db.product.findOne({name:"きなこ棒"}).price
10
> db.product.update(
  {name:"きなこ棒"},
  {$inc:{price:5}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.findOne({name:"きなこ棒"}).price
15
$min / $max

$min/$maxを使用すると条件付きでフィールドの値を変更することができます。
$minを使用すると現在の値より大きい値で更新はできなくなります。($maxはその逆の働きをします。)

この例では、"生いきビール"のpriceフィールドを40から50へ更新しようとしていますが、ドキュメントは更新されません。

> db.product.findOne(
  {name:"生いきビール"},
  {_id:0, name:1, price:1}
)
{ "name" : "生いきビール", "price" : 40 }
> db.product.update(
  {name:"生いきビール"},
  {$min:{price:50}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

現在の値より小さい値へ更新することは可能です。

> db.product.update(
  {name:"生いきビール"},
  {$min:{price:30}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.findOne(
  {name:"生いきビール"},
  {_id:0, name:1, price:1}
)
{ "name" : "生いきビール", "price" : 30 }
$currentDate

$currentDateを使用すると指定したフィールドを現在日時で更新することができます。

> db.product.update(
  {name:"生いきビール"},
  {$currentDate:{update_dt: {$type: "date"}}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

更新対象が見つからなかった場合

queryで指定する更新対象のドキュメントが見つからなかった場合は、ドキュメントは追加も更新もされません。

> db.product.update(
  {no:19},
  {no:19, name:"ハートチップル", price:100, activate:true, variety:[{flavor:"ニンニク", ppl:2}], snack:true, update_dt:ISODate("2015-06-01T00:00:00+09:00")}
)
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })

upsert:trueを指定すること更新対象がなかった場合にドキュメントを追加することができます。

> db.product.update(
  {no:19},
  {no:19, name:"ハートチップル", price:100, activate:true, variety:[{flavor:"ニンニク", ppl:2}], snack:true, update_dt:ISODate("2015-06-01T00:00:00+09:00")},
  {upsert:true}
)
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 1,
        "nModified" : 0,
        "_id" : ObjectId("5557cb2736efe7aa0e474d32")
})

upsertでinsertの時にだけフィールドを更新する

$setOnInsertで指定するフィールドはドキュメントのinsert時にしか更新されません。
この例ではinsert時にだけcreate_dtフィールドを現在日時で更新します。

> db.product.findOne({no:20})
null
> db.product.update(
  {no:20},
  {$set:{no:20, name:"きびだんご", price:100, activate:true, update_dt:ISODate()}, $setOnInsert:{create_dt:ISODate()}},
  {upsert:true}
)
WriteResult({
        "nMatched" : 0,
        "nUpserted" : 1,
        "nModified" : 0,
        "_id" : ObjectId("5557cda836efe7aa0e474d34")
})
> db.product.findOne({no:20},{_id:0,no:1,name:1,price:1,update_dt:1,create_dt:1})
{
        "no" : 20,
        "name" : "きびだんご",
        "price" : 100,
        "update_dt" : ISODate("2015-05-16T23:07:20.636Z"),
        "create_dt" : ISODate("2015-05-16T23:07:20.636Z")
}

priceフィールドの値を変えてもう一度updateを行います。
priceフィールドとupdate_dtフィールドは更新されていますが、create_dtフィールドはinsert時のときのままです。

> db.product.update(
  {no:20},
  {$set:{no:20, name:"きびだんご", price:120, activate:true, update_dt:ISODate()}, $setOnInsert:{create_dt:ISODate()}},
  {upsert:true}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.findOne({no:20},{_id:0,no:1,name:1,price:1,update_dt:1,create_dt:1})
{
        "no" : 20,
        "name" : "きびだんご",
        "price" : 120,
        "update_dt" : ISODate("2015-05-16T23:08:13.973Z"),
        "create_dt" : ISODate("2015-05-16T23:07:20.636Z")
}

フィールドのリネーム

フィールドをリネームするには$renameを使用します。ドキュメント全てを対象にする場合はmulti:trueを指定します。

> db.product.update(
  {},
  {$rename:{"price":"unit_price"}}, {multi:true}
)
WriteResult({ "nMatched" : 19, "nUpserted" : 0, "nModified" : 18 })
/* priceフィールドがunit_priceフィールドに変わっています */
> db.product.find({}, {_id:0, name:1, price:1, unit_price:1}).limit(5)
{ "name" : "ポテトフライ", "unit_price" : 30 }
{ "name" : "きなこ棒", "unit_price" : 15 }
{ "name" : "生いきビール", "unit_price" : 30 }
{ "name" : "フルーツヨーグル", "unit_price" : 20 }
{ "name" : "コーヒー牛乳キャンディ", "unit_price" : 10 }

複数件の更新

複数のドキュメントが更新対象になる場合、実際に更新されるのは最初の1件だけです。

> db.product.find({price:10},{_id:0,name:1,price:1})
{ "name" : "うまい棒", "price" : 10 }
{ "name" : "コーヒー牛乳キャンディ", "price" : 10 }
{ "name" : "たまごアイス", "price" : 10 }
{ "name" : "あんこ玉", "price" : 10 }
> db.product.update(
  {price:10},
  {$set:{price:15}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

条件に一致する全てのドキュメントを更新するには、multi:trueを指定します。

> db.product.update(
  {price:10},
  {$set:{price:15}},
  {multi:true}
)
WriteResult({ "nMatched" : 4, "nUpserted" : 0, "nModified" : 4 })

フィールドの追加

更新するフィールドがドキュメントに無い場合は、そのフィールドがドキュメントに追加されます。
この例では、"うまい棒"のドキュメントにdescフィールドを追加しています。

> db.product.update(
  {name:"うまい棒"},
  {$set:{desc:"伝統のスナック菓子"}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.find({name:"うまい棒"}).pretty()
{
        "_id" : ObjectId("5557d975b636d6666833fb02"),
        "no" : 1,
        "name" : "うまい棒",
        "price" : 10,
        "variety" : [
                {
                        "flavor" : "メンタイ",
                        "ppl" : 3
                },
                {
                        "flavor" : "サラミ",
                        "ppl" : 3
                },
                {
                        "flavor" : "ピザ",
                        "ppl" : 2
                },
                {
                        "flavor" : "オニオンサラダ",
                        "ppl" : 2
                },
                {
                        "flavor" : "コーンポタージュ",
                        "ppl" : 1
                }
        ],
        "snack" : true,
        "activate" : true,
        "update_dt" : ISODate("2014-12-31T15:00:00Z"),
        "desc" : "伝統のスナック菓子"
}

フィールドの削除

$unsetを使用するとドキュメントからフィールドを削除することができます。

> db.product.update(
  {name:"うまい棒"},
  {$unset:{price:""}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.find({name:"うまい棒"}).pretty()
{
        "_id" : ObjectId("5557d975b636d6666833fb02"),
        "no" : 1,
        "name" : "うまい棒",
        "variety" : [
                {
                        "flavor" : "メンタイ",
                        "ppl" : 3
                },
                {
                        "flavor" : "サラミ",
                        "ppl" : 3
                },
                {
                        "flavor" : "ピザ",
                        "ppl" : 2
                },
                {
                        "flavor" : "オニオンサラダ",
                        "ppl" : 2
                },
                {
                        "flavor" : "コーンポタージュ",
                        "ppl" : 1
                }
        ],
        "snack" : true,
        "activate" : true,
        "update_dt" : ISODate("2014-12-31T15:00:00Z"),
        "desc" : "伝統のスナック菓子"
}

MongoDB Reference - update
MongoDB Reference - Update Operators

save()

definition
db.collection.save(
   <document>,
   { writeConcern: <document> }
)

追加または更新

update()は更新対象のドキュメントをqueryで指定できますが、save()はqueryを指定することはできません。
代わりに_idが指定されていて当該のドキュメントがあればupdateを行い、無ければinsertします。

> db.product.save(
{no:21, name:"ふ菓子", price:10, activate:false, update_dt:ISODate("2015-05-30T00:00:00+09:00")}
)
WriteResult({ "nInserted" : 1 })

_idが指定されていて且つそのid値が存在する場合は更新します。(id値が存在しない場合は追加)

> db.product.save(
  {_id:ObjectId("5557daa5b636d6666833fb03"), no:21, name:"ふ菓子", price:20, activate:true, update_dt:ISODate("2015-05-30T00:00:00+09:00")}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

update()との違い

  • queryで更新対象を指定できない
  • 複数件の同時更新ができない
  • upadte()、findAndModify()では使用できるupdate operatorが使用できない

MongoDB Reference - save

remove()

definition
db.collection.remove(
   <query>,
   <justOne>
)

すべて削除

> db.product.remove({})
WriteResult({ "nRemoved" : 14 })

条件を指定して削除

> db.product.remove({activate:false})
WriteResult({ "nRemoved" : 2 })

1件だけ削除

第2引数(justOne)にtrueを渡すと1件だけ削除します。

> db.product.remove({}, true)
WriteResult({ "nRemoved" : 1 })

remove()は実行結果をWriteResultオブジェクトとして返します。

> var wr = db.product.remove({}, true)
> printjson(wr)
{ "nRemoved" : 1 }

MongoDB Reference - remove

index

indexの確認

> db.product.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "test.product"
        }
]

indexの作成

definition
db.collection.createIndex(keys, options)

フィールドに1を指定すると昇順、-1を指定すると降順になります。

> db.product.createIndex({price:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
/* 作成したインデックスを確認してみます */
> db.product.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "test.product"
        },
        {
                "v" : 1,
                "key" : {
                        "price" : 1
                },
                "name" : "price_1",
                "ns" : "test.product"
        },
 ]
/* 名前を指定して削除します */
> db.product.dropIndex("price_1")
{ "nIndexesWas" : 2, "ok" : 1 }

オプションでindex名を指定することができます。

> db.product.createIndex(
  {price:1},
  {name:"price_idx01"}
)
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}

ユニークindexの作成

作成時のオプションでunique:trueを指定します。

> db.product.createIndex(
  {name:1},
  {unique:true, name:"name_uni01"}
)
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 2,
        "numIndexesAfter" : 3,
        "ok" : 1
}

ユニークindexを作成したフィールドには重複したデータを登録することはできませんが、追加するドキュメントにそのフィールドが無かった場合、フィールドの値はnullとして扱われるようです。

[MongoDB manual - unique index and missing field] (http://docs.mongodb.org/manual/tutorial/create-a-unique-index/#unique-index-and-missing-field)

試しに、nameフィールドにユニークindexを作成した状態で、nameフィールドが無いドキュメントを追加してみます。
1件目は追加することができました。

> db.product.insert(
  {no:22, price:30, activate:true, update_dt:ISODate("2015-05-30T00:00:00+09:00")}
)
WriteResult({ "nInserted" : 1 })

もう一度nameフィールドが無いドキュメントを追加しようとするとエラーになります。これはnull値も重複のチェックの対象になっているためのようです。

> db.product.insert(
  {no:23, price:30, activate:true, update_dt:ISODate("2015-05-30T00:00:00+09:00")}
)
WriteResult({
        "nInserted" : 0,
        "writeError" : {
                "code" : 11000,
                "errmsg" : "E11000 duplicate key error index: test.product.$name
_uni01 dup key: { : null }"
        }
})

この場合は、ユニークindex作成時にsparse:trueを併用することでnullを無視することができます。

/* ユニークindexを作り直すために削除 */
> db.product.dropIndex("name_uni01")
{ "nIndexesWas" : 3, "ok" : 1 }
/* ユニークindexを作成 */
> db.product.createIndex(
  {name:1},
  {unique:true, sparse:true, name:"name_uni01"}
)
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 2,
        "numIndexesAfter" : 3,
        "ok" : 1
}
/* nameフィールドの無いドキュメントを追加 */
> db.product.insert(
  {no:23, price:30, activate:true, update_dt:ISODate(
"2015-05-30T00:00:00+09:00")}
)
WriteResult({ "nInserted" : 1 })
/* nameフィールドの無いドキュメントを追加することができました */
> db.product.find({name:{$exists:false}},{_id:1,no:1,name:1})
{ "_id" : ObjectId("5557db82b636d6666833fb04"), "no" : 22 }
{ "_id" : ObjectId("5557dbf5b636d6666833fb06"), "no" : 23 }

TTLインデックス

時間経過によってドキュメントを自動的に削除したい場合、expireAfterSecondsを使用したインデックスを作成します。

この例ではdelete_dtフィールドにexpireAfterSecondsを300秒(5分)に設定したTTLインデックスを作成しています。

> db.product.createIndex(
  {delete_dt:1},
  {expireAfterSeconds:300, name:"ttl_idx"}
)
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 3,
        "numIndexesAfter" : 4,
        "ok" : 1
}

ためしに"コーヒー牛乳キャンディ"のドキュメントのdelete_dtを現在日時で更新します。
更新してから5分経過後にもう一度検索すると削除されていることがわかります。

> db.product.update(
  {name:"コーヒー牛乳キャンディ"},
  {$set:{delete_dt:new Date()}}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.product.find(
  {name:"コーヒー牛乳キャンディ"},
  {_id:0,name:1,delete_dt:1}
)
{ "name" : "コーヒー牛乳キャンディ", "delete_dt" : "Sun May 17 2015 09:10:36 GMT
+0900 (東京 (標準時))" }

5分経過後に検索するとドキュメントは見つかりません。

> db.product.find(
  {name:"コーヒー牛乳キャンディ"},
  {_id:0,name:1,delete_dt:1}
)
>

MongoDB Reference - createIndex
MongoDB Manual - Sparse Indexes
MongoDB Manual - Expire Data from Collections by Setting TTL

indexの削除

インデックスの名前を指定して削除します。

> db.product.dropIndex("price_idx01")
{ "nIndexesWas" : 4, "ok" : 1 }

"product"コレクションの(_id以外の)すべてのindexを削除します。

> db.product.dropIndexes()
{
        "nIndexesWas" : 3,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}

rename

definition
db.collection.renameCollection(target, dropTarget)

"product"コレクションの名前を"newProduct"にリネームします。

> db.product.renameCollection("newProduct")
{ "ok" : 1 }

指定した"newProduct"コレクションが既に存在する場合はエラーになります。

{ "ok" : 0, "errmsg" : "target namespace exists" }

第2引数(dropTarget)にtrueを指定した場合、既に"newProduct"コレクションがある場合はそのコレクションを削除し、その後に"product"を"newProduct"へリネームします。

> db.product.renameCollection("newProduct", true)
{ "ok" : 1 }

MongoDB Reference - RenameCollection

drop()

definition
db.collection.drop()

"product"コレクションを削除します。

> db.product.drop()
true

MongoDB Reference - drop

store javascript function

登録する

> db.system.js.save({
    _id:"add",
    value:function(a, b) { return a + b; }
})
WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "add" })

登録されているfunctionを調べる

> db.system.js.find()
{ "_id" : "add", "value" : function (a, b) { return a + b; } }

使用する

> db.loadServerScripts()
> var f = db.system.js.findOne({_id:"add"}).value
> f(2,3)
5

削除する

> db.system.js.remove({_id:"add"})
WriteResult({ "nRemoved" : 1 })

MongoDB Manual - store javascript function

日付

Date()

Unixエポックからのミリ秒数(1970年1月1日)を表す64ビットの整数です。

> var dt = new Date()
> dt.toString()
Sun May 17 2015 06:25:43 GMT+0900 (東京 (標準時))

ISODate()

Date()のラッパー関数で返される値はDateです。

> var iso = ISODate()
> iso.toString()
Sun May 17 2015 06:25:43 GMT+0900 (東京 (標準時))
> iso instanceof Date
true
> iso instanceof ISODate
false

任意の日時を生成するにはISODate()に基本形式か拡張形式の日時表記文字列を渡します。

基本形式
"20150510T102322+0900"
拡張形式
"2015-05-10T10:23:22+09:00"

世界協定時(UTC)として表現する場合は最後に"Z"を付けます。
ISODate("2015-05-10T00:00:00Z")

日本標準時(UTC+9)の場合は"+0900"または"+09:00"を付けます。
ISODate("2015-05-10T09:00:00+09:00")

> var iso = ISODate("2015-05-15T12:00:01+09:00")
> iso.toString()
Fri May 15 2015 12:00:01 GMT+0900 (東京 (標準時))

Timestamp()

MongoDB内部で使用されているデータ型で、通常のアプリケーションではDate型で十分らしいです。

> var ts = new Timestamp()
> ts.toString()
Timestamp(0, 0)

[MongoDB reference - Date] (http://docs.mongodb.org/manual/reference/bson-types/#date)
[MongoDB reference - Timpstamps] (http://docs.mongodb.org/manual/reference/bson-types/#timestamps)

shellのカスタマイズ

find()の一度に取得する件数を変えます。デフォルトは20です。

> DBQuery.shellBatchSize = 10
10

shellの出力のデフォルトをpretty printにします。

> DBQuery.prototype._prettyShell = true
true

下記のようなjavascriptを実行してpromptをカスタマイズできます。

> cmdCount = 1;
> host = db.serverStatus().host;
> prompt = function() {
    return db + "@" + host + "(" + (cmdCount++) + ")> ";
}
test@localhost(1)>

.mongorc.js

ホームディレクトリにある.mongorc.jsに、shell起動時に実行したいスクリプトを記述することができます。

MongoDB Manual - Getting Started with the mongo Shell

Native Methods

load()

load()は引数で指定されたjavascriptファイルをロードして実行します。
この例では、下記のjavascriptをsample.jsファイルとして用意しmongo shellにロードします。

sample.js
function randomStr() {
  return Math.random().toString(36);
}

var col = new Array("red","blue","yellow","green","white");

function randomColor() {
  return col[Math.floor(Math.random() * 5)];
}

var act = new Array(true, false);

function randomAct() {
  return act[Math.floor(Math.random() * 2)];
}

function createTestData() {
  for (var idx = 0; idx<100000; idx++) {
    db.sample.insert({no:idx, color:randomColor(), desc:randomStr(), activate:randomAct(), create_dt:new Date()});
  }
}

sample.jsをロードします。

> load("sample.js")
true

createTestData()を実行するとsampleコレクションにテストデータを10万件作成します。

> createTestData()
> db.sample.find().count()
100000

その他の主なNativeメソッド

Name Description
cat() Returns the contents of the specified file.
version() Returns the current version of the mongo shell instance.
hostname() Returns the hostname of the system running the shell.
load() Loads and runs a JavaScript file in the shell.
ls() Returns a list of the files in the current directory.
quit() Exits the current shell session.
_rand() Returns a random number between 0 and 1.

MongoDB Reference - Native Methods

75
64
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
75
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?