3

データベースに 30,000 個の SimpleObject を保存しました。

class SimpleObject
{
    public int Id { get; set; }
} 

指定された ID のいずれかを持つすべての SimpleObjects を検索するクエリを DB4O で実行したいと考えています。

public IEnumerable<SimpleObject> GetMatches(int[] matchingIds)
{
     // OH NOOOOOOES! This activates all 30,000 SimpleObjects. TOO SLOW!
     var query = from SimpleObject simple in db
                 join id in matchingIds on simple.Id equals id
                 select simple;
     return query.ToArray();
}  

DB4O が 30,000 個のオブジェクトすべてを活動化しないようにするには、この照会をどのように作成すればよいでしょうか?

4

4 に答える 4

3

私はこれについての専門家ではないので、DB4O フォーラムに投稿するのは良いことかもしれませんが、私には解決策があると思います。LINQ を使用せず、SODA を使用する必要があります。

これが私がしたことです。投稿の定義に基づいて、データベースに 30000 個の SimpleObject を入力する簡単なプロジェクトを作成しました。次に、データベースからすべての SimpleObjects を取得するクエリを作成しました。

var simpleObjects = db.Query<SimpleObject>(typeof(SimpleObject));

ストップウォッチをラップすると、その実行には約 740 ミリ秒かかります。次に、コードを使用して、0 から 2999 までの 100 個の乱数を検索しました。応答は 772 ミリ秒だったので、その数値に基づいて、データベースからすべてのオブジェクトを引き出していると想定しています。それを確認する方法はわかりませんが、後でパフォーマンスで証明したと思います。

それから私は下に行きました。私の理解では、DB4O チームの LINQ プロバイダーは SODA への変換を行っているだけです。したがって、テストのために SODA クエリを作成することにしました。実行に 19902 ミリ秒かかるため、プロパティに対して SODA を使用するとパフォーマンスが低下することがわかりました。コードは次のとおりです。

private SimpleObject[] GetSimpleObjectUsingSodaAgainstAProperty(int[] matchingIds, IObjectContainer db)
{
    SimpleObject[] returnValue = new SimpleObject[matchingIds.Length];

    for (int counter = 0; counter < matchingIds.Length; counter++)
    {
        var query = db.Query();
        query.Constrain(typeof(SimpleObject));
        query.Descend("Id").Constrain(matchingIds[counter]);
        IObjectSet queryResult = query.Execute();
        if (queryResult.Count == 1)
            returnValue[counter] = (SimpleObject)queryResult[0];
    }

    return returnValue;
}

なぜこれが悪いのかを考えて、プロパティは実際には値ではなくメソッドであるため、自動実装されたプロパティを使用せずに自分で定義することにしました。

public class SimpleObject
{
    private int _id;

    public int Id { 
        get
        { return _id; }
        set
        { _id = value; }
    }
}

次に、プロパティの代わりに _id プライベート フィールドを使用するようにクエリを書き直しました。パフォーマンスは約 91 ミリ秒ではるかに優れていました。そのコードは次のとおりです。

private SimpleObject[] GetSimpleObjectUsingSodaAgainstAField(int[] matchingIds, IObjectContainer db)
{
    SimpleObject[] returnValue = new SimpleObject[matchingIds.Length];

    for (int counter = 0; counter < matchingIds.Length; counter++)
    {
        var query = db.Query();
        query.Constrain(typeof(SimpleObject));
        query.Descend("_id").Constrain(matchingIds[counter]);
        IObjectSet queryResult = query.Execute();
        if (queryResult.Count == 1)
            returnValue[counter] = (SimpleObject)queryResult[0];
    }

    return returnValue;
}

まぐれではないことを確認するために、テストを数回実行しましたが、同様の結果が得られました。次に、さらに 60,000 レコードを追加して合計 90,000 にしたところ、パフォーマンスの違いは次のようになりました。

GetAll: 2450 ms
GetWithOriginalCode: 2694 ms
GetWithSODAandProperty: 75373 ms
GetWithSODAandField: 77 ms

それが役立つことを願っています。理由を実際に説明していないことはわかっていますが、これはその方法に役立つかもしれません。また、SODA フィールド クエリのコードをより汎用的にラップすることも難しくありません。

于 2009-11-12T17:08:21.993 に答える
2

LINQを使用してこのクエリを実行しようとすると、最適化されていない状態で実行されます(つまり、db4oはSimpleObjectタイプのすべてのオブジェクトを取得し、残りをLINQのオブジェクトに委任します)

最善のアプローチは、n個のクエリを実行し(idフィールドにインデックスが付けられているため、各クエリは高速に実行される必要があります)、「MarkHall」によって提案された結果を集約することです。

これにはLINQを使用することもできます(次のようなもの)

IList<SimpleObject> objs = new List<SimpleObject>();
foreach(var tbf in ids)
{
     var result = from SimpleObject o in db()
               where o.Id = tbf
                  select o;

     if (result.Count == 1)
     {
        objs.Add(result[0]);
     }
}

一番

于 2009-11-12T20:04:42.603 に答える
2

動的 linq クエリを「構築」できます。たとえば、API は次のようになります。

最初のパラメーター: 検索するプロパティを示す式 他のパラメーター: ID または検索対象のもの。

 var result1 = db.ObjectByID((SimpleObject t) => t.Id, 42, 77);
 var result2 = db.ObjectByID((SimpleObject t) => t.Id, myIDList);
 var result3 = db.ObjectByID((OtherObject t) => t.Name, "gamlerhart","db4o");

実装は、次のような動的クエリを構築します。

var result = from SimpleObject t 
  where t.Id = 42 || t.Id==77 ... t.Id == N
  select t

すべてが OR で結合されているため、インデックスで直接評価できます。アクティベーションは必要ありません。実装例:

public static class ContainerExtensions{

public static IDb4oLinqQuery<TObjectType> ObjectByID<TObjectType, TIdType>(this IObjectContainer db,
Expression<Func<TObjectType, TIdType>> idPath,
params TIdType[] ids)
{
  if(0==ids.Length)
  {
       return db.Cast<TObjectType>().Where(o=>false);
  }
  var orCondition = BuildOrChain(ids, idPath);
  var whereClause = Expression.Lambda(orCondition, idPath.Parameters.ToArray());
  return db.Cast<TObjectType>().Where((Expression<Func<TObjectType, bool>>) whereClause);
}

private static BinaryExpression BuildOrChain<TIdType, TObjectType>(TIdType[] ids,     Expression<Func<TObjectType, TIdType>> idPath)
{
  var body = idPath.Body;
  var currentExpression = Expression.Equal(body, Expression.Constant(ids.First()));
  foreach (var id in ids.Skip(1))
  {
    currentExpression = Expression.OrElse(currentExpression, Expression.Equal(body,     Expression.Constant(id)));
  }
return currentExpression;
    }

}
于 2009-11-12T22:24:12.147 に答える
2

私は db4o LINQ をあまり使っていません。ただし、DiagnosticToConsole (または ToTrace) を使用して、それを IConfiguration.Diagnostic().AddListener に追加できます。これにより、クエリが最適化されているかどうかがわかります。

多くの詳細を提供していませんが、SimpleObject の Id プロパティはインデックス化されていますか?

診断がオンになったら、次のようにクエリを試すことができます...

from SimpleObject simple in db
where matchingIds.Contains(simple.Id)
select simple

それが別のクエリプランを提供するかどうかを確認してください。

于 2009-11-06T23:39:43.887 に答える