LiteDB(v5.0.15)を使ってみる
ドキュメント系。
LiteDBにレコードをinsertするときに、以下のようにプロパティを渡す必要があるが、このときジェネリクスはそのプロパティclassの型と一致していないとidの自動採番でエラーになる。
// OKパターン
public class TestProperty {
public int id { get; set; } // 自動採番
public string text { get; set; }
}
public static class TestOk{
public static void DbTest() {
using (var db = new LiteDatabase("testDatabase.db")) {
var col= db.GetCollection<TestProperty>("test");
col.Insert(new TestProperty { text = "aaa" });
}
}
}
// NGパターン
public interface ITestProperty {
}
public class TestNgProperty : ITestProperty {
public int id { get; set; }
public string text { get; set; }
}
public static class TestNG {
public static void DbTest() {
using (var db = new LiteDatabase("testDatabase.db")) {
// 実装元のinterfaceを型にしているがNG
var col = db.GetCollection<ITestProperty>("test");
col.Insert(new TestNgProperty { text = "aaa" });
}
}
}
// エラー内容
// LiteDB.LiteException: 'Cannot insert duplicate key in unique index '_id'. The duplicate value is '0'.'
以下Issuesを参考。
LiteDBではジェネリクスの型を見て、各プロパティの型を判断しているとのこと。
BsonDocumentでやることにした。(抜粋)
// Typeを使ってプロパティの一覧を取得
PropertyInfo[] properties = Type.GetType(this.propertyClass.FullName).GetProperties();
var propClassObj = Activator.CreateInstance(this.propertyClass);
foreach (var prop in properties) {
prop.SetValue(propClassObj, data.GetValueOrDefault(prop.Name));
}
var bson = BsonMapper.Global.ToDocument(propClassObj));
col.Insert(bson);
- プロパティのクラスを作る
- プロパティ全てをloopでDictionary型のdata変数と突き合わせ、プロパティに設定
- BsonMapperを使用し、BsonDocument型のオブジェクトを作成
- 作成したBsonDocumentオブジェクトでinsert
AutoId はリレーショナル データベースのシーケンスと同様に使用できますが、動作が少し異なります。AutoId は保持されず、ファイルを再度開いたときにメモリ内に再作成する必要があるため、以前に削除されたドキュメントの ID が再利用される可能性があります。
IN句をIdで絞る場合やり方は?
// this.DataListはList<Dictionary<string, object>>
List<BsonValue> Ids = this.DataList.Select(dict => new BsonValue(dict["Id"])).ToList();
// {`$.Id IN [{$oid:"63f9fb1c7e28c3014a4fdedc"}]` [In]} が入っている
var query = Query.In("Id", new BsonArray(Ids));
// これの結果が削除件数0
col.DeleteMany(query);
プロパティクラスでIdは以下のように定義していたが、
public ObjectId Id { get; set; }
LiteDBの列を覗くと列名は_id
だった。
上記コードのQuery.In("_id", new BsonArray(Ids));
にしたら削除された
特定のIDを持ったレコードで、特定のフィールドにnullを設定したい場合の方法。
以下はIN句を使ってマッチしたレコードのFieldNameというフィールドをnullに置き換えている。
List<BsonValue> Ids = // _idの値を持ったList<BsonValue>変数
string fieldName = "FieldName";
var query = Query.In(fieldName , new BsonArray(Ids));
//~LiteDatabase litedb = new LiteDatabase("collectionName")などの処理~
col.UpdateMany("{"+fieldName +":$unset}", query)
{FieldName:$unset}
この形式の文字列と条件(クエリ)をUpdateManyに渡してやればいい
LiteDBViewerを使用して置き換えるだけで構わないのであれば、LiteDBViewerを起動し普通にSQLクエリを投げればOK
update collection
set FieldName = null
where FieldName= "hoge"
ObjectIdのフィールドをUpdateManyする方法
List<BsonValue> Ids = // _idの値を持ったList<BsonValue>変数
string fieldName = "FieldName";
var query = Query.In(fieldName , new BsonArray(Ids));
//~LiteDatabase litedb = new LiteDatabase("collectionName")などの処理~
string updateQuery = "{ObjectIdField:OBJECTID('IDを設定')}";
col.UpdateMany(updateQuery, query);
以下に記載されているFunctionを使って変換している。
そのままObjectIdを設定するとstring型で登録されるので注意。
配列の値をINする方法。
// プロパティクラス
public class Data {
public ObjectId _id { get; set; }
public int Number { get; set; }
public List<int>? SubNumberList { get; set; }
}
// 検索する方法
// SubIdListフィールドの値として1もしくは2を持つレコードがヒットする
col.Find("$.SubNumberList[*] ANY IN [1,2]");
objectidの場合のメモ
OBJECTID()
でObjectIdの値を囲む
ObjectId oid1 = hoge;
ObjectId oid2 = huga;
col.Find("$.SubIdList[*] ANY IN [OBJECTID('" + oid1 + "'), OBJECTID('" + oid2 + "')]");