前回に続き、 MongoDBをC#から試す。今回はスキーマレス的なCRUD。とりあえずソースは↓

public void TestCRUD()
{
    string connectionString = "mongodb://localhost";
    MongoClient client = new MongoClient(connectionString);
    var database = client.GetDatabase("foo");
    var collection = database.GetCollection("freedom");

    ///////////////////////////////////////////////////////////////////////////////////////
    //追加系
    ///////////////////////////////////////////////////////////////////////////////////////
    Console.WriteLine("\n■追加系\n");
    var document = new BsonDocument
    {
        { "free01", 1       },
        { "free02", "ABCDE" },
        { "free03", "自由"  },
        { "自由04", "FREE"  }
    };
    //1件追加
    collection.InsertOne(document);

    //↑で追加したのとは違う構成
    List documents = new List();
    documents.Add(
            new BsonDocument
            {
                {"HOGE",10 },
                {"FUGA","MOE" }
            }
        );
    //同じオブジェの中でも違う定義にする
    documents.Add(
            new BsonDocument
            {
                { "なにか" , "DOC" },
                { "もっと何か", new BsonDocument { { "BSON","ビーソン"}, { "JSON",1} } }
            }
        );
    //複数件追加
    collection.InsertMany(documents);

    ///////////////////////////////////////////////////////////////////////////////////////
    //参照系
    ///////////////////////////////////////////////////////////////////////////////////////
    Console.WriteLine("\n■参照系\n");
    //追加した中身の確認
    var finddocument = collection.Find(new BsonDocument());
    foreach (var tmp in collection.Find(new BsonDocument()).ToList())
    {
        Console.WriteLine(tmp);
    }
    //フィルターつけてFind
    finddocument = collection.Find(new BsonDocument { { "HOGE",10} });
    foreach (var tmp in finddocument.ToList())
    {
        Console.WriteLine(tmp);
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    //更新系
    ///////////////////////////////////////////////////////////////////////////////////////
    Console.WriteLine("\n■更新系\n");
    var updateElem = Builders.Update.Set("FUGA", "FUGAFUGA");
    var result = collection.UpdateMany(new BsonDocument { { "HOGE", 10 } }, updateElem);//マッチした中の先頭1件のみ更新の場合はUpdateOne
    if (result.IsModifiedCountAvailable)
    {
        Console.WriteLine("【更新結果】マッチ:{0}件 更新:{1}件", result.MatchedCount, result.ModifiedCount);
    }
    //更新確認
    finddocument = collection.Find(new BsonDocument { { "HOGE", 10 } });
    foreach (var tmp in finddocument.ToList())
    {
        Console.WriteLine(tmp);
    }
    //既存のドキュメントにフィールドを追加する場合
    updateElem = Builders.Update.Set("AddElem", "足したよ");
    result = collection.UpdateMany(new BsonDocument { { "HOGE", 10 } }, updateElem);
    if (result.IsModifiedCountAvailable)
    {
        Console.WriteLine("【更新結果】マッチ:{0}件 更新:{1}件", result.MatchedCount, result.ModifiedCount);
    }
    //更新確認
    finddocument = collection.Find(new BsonDocument { { "HOGE", 10 } });
    foreach (var tmp in finddocument.ToList())
    {
        Console.WriteLine(tmp);
    }
    //既存のフィールドを別のオブジェクトで置き換える
    updateElem = Builders.Update.Set("AddElem", new BsonDocument { { "置換","ReplaceVal" }, { "この人","置換です"} });
    result = collection.UpdateMany(new BsonDocument { { "HOGE", 10 } }, updateElem);
    if (result.IsModifiedCountAvailable)
    {
        Console.WriteLine("【更新結果】マッチ:{0}件 更新:{1}件", result.MatchedCount, result.ModifiedCount);
    }
    //更新確認
    finddocument = collection.Find(new BsonDocument { { "HOGE", 10 } });
    foreach (var tmp in finddocument.ToList())
    {
        Console.WriteLine(tmp);
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    //削除系
    ///////////////////////////////////////////////////////////////////////////////////////
    Console.WriteLine("\n■削除系\n");
    var delresult = collection.DeleteMany(new BsonDocument { { "HOGE", 10 } });//マッチした中の先頭1件のみ削除の場合はDeleteOne
    Console.WriteLine("削除件数:{0}", delresult.DeletedCount);
    //削除確認
    finddocument = collection.Find(new BsonDocument());
    foreach (var tmp in collection.Find(new BsonDocument()).ToList())
    {
        Console.WriteLine(tmp);
    }
}

基本的にはBsonDocumentでオブジェクト作って投げればいいだけですね。

ちょっと触ってて思ったのが、「これは危険だ」というところ。 特に既存のオブジェクトの置き換えはやヴぁい。気付かずに同じ名前で更新しちゃったら中身丸ごと変えられちゃうのね。

便利といえば便利だけども、怖い。

うすうす気付いてはいたけれども、結局のところはスキーマの管理が大事という 結論に至りそうなオチが見えてまいりました。

個別のフィールドを使う場合は下の感じになるのかしら。。。

string connectionString = "mongodb://localhost";
MongoClient client = new MongoClient(connectionString);
var database = client.GetDatabase("foo");
var collection = database.GetCollection("freedom");

var finddocument = collection.Find(new BsonDocument());
foreach (var tmp in collection.Find(new BsonDocument()).ToList())
{
    //ドキュメント全体
    Console.WriteLine("Document:",tmp);
    //フィールドの名前
    foreach (string tmp2 in tmp.Names)
    {
        Console.WriteLine("Name:{0}",tmp2);
    }
    //値の中身
    foreach (var tmp2 in tmp.Values)
    {
        Console.WriteLine("Value:{0}",tmp2.ToString());
    }
    //Elementでアクセス
    foreach (var tmp2 in tmp.Elements)
    {
        Console.WriteLine("{0}:{1}", tmp2.Name, tmp2.Value);
    }
    Console.WriteLine("Next...\n");
}

ついでに、普通のクラスのプロパティにBsonDocumentを入れておけば、そこだけスキーマレスに扱える模様。↓の感じ。

//クラス定義
public class Mixing
{
    public ObjectId Id { get; set; }
    public string str1 { get; set; }
    public BsonDocument Meta { get; set; }
    public BsonDocument Meta2 { get; set; }
    [BsonExtraElements]
    public BsonDocument ExMeta { get; set; }
}

//オブジェクト作る例
for (int i = 0; i < 20; i++)
{
    documents.Add(
        new Mixing
        {
            str1 = i.ToString(),
            Meta = new BsonDocument("metaId",i.ToString()),
            Meta2 = new BsonDocument{{ "x", 203 },{ "y", 102 }},
            ExMeta = new BsonDocument("ExMetaId",i*10)
        }
        );
}

//DB上は↓の感じで入る
{ 
"_id"      : ObjectId("5706aadb6446e821943aecf3"),
"str1"     : "10",
"Meta"     : { "metaId" : "10" },
"Meta2"    : { "x" : 203, "y" : 102 },
"ExMetaId" : 100 
}

結局はBsonDocumentでやれという話。注目は[BsonExtraElements]を使ってるプロパティ。 クラスのプロパティ名じゃなく、BsonDocumentで指定した名前になる。ちなみに[BsonExtraElements]の定義は「using MongoDB.Bson.Serialization.Attributes;」のusing入れておかないと使えない。はまった。

で、この動きは結構危険。別のプロパティと名前かぶったらどーなんでしょ。っていうか本家でもdangerって言ってるし、やっぱ危ないんでしょー。

まぁ使い方次第ということでしょうか。使いどころがあるかは知らないけど。。。

とりあえず、基本的な動きは見れたとゆーところですが、このままじゃ使いにくそー。 やりやすい何かを用意しておかないと厳しいかな。。。 次は、Linqをためしましょ。