Pergunta

Eu armazenados 30.000 SimpleObjects no meu banco de dados:

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

Eu quero executar uma consulta em DB4O que encontra todos os SimpleObjects com qualquer um dos IDs especificados:

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();
}  

Como faço para escrever essa consulta para que DB4O não ativar todos os 30.000 objetos?

Foi útil?

Solução

Se você tentar executar essa consulta usando LINQ que vai correr unoptimized (o que significa que db4o estão indo para recuperar todos os objetos do tipo SimpleObject e delegar o resto para LINQ para objetos)

A melhor abordagem seria para executar n consultas (uma vez que o campo id é indexado, cada consulta deve correr rápido) e agregar os resultados como sugerido por "Mark Hall".

Você ainda pode usar LINQ para este (algo como)

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]);
     }
}

Melhor

Outras dicas

Eu não sou um especialista sobre isso, e ele pode ser bom para postar nos fóruns DB4O sobre isso, mas eu acho que tenho uma solução. Não envolve o uso de LINQ e usando SODA.

Isto é o que eu fiz. Eu criei um projeto rápido que preenche o banco de dados com 30000 SimpleObject com base na definição do seu post. Eu, então, escreveu uma consulta para pegar todos os SimpleObjects do banco de dados:

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

Quando eu envolvi um cronômetro em torno dele, que correm leva cerca de 740 milissegundos. Então, usei o código para procurar um 100 números aleatórios entre 0 e 2999. A resposta foi 772 ms, assim com base nesse número que eu estou supondo que ele está puxando todos os objetos do banco de dados. Não estou certo de como verificar se, mas depois acho que provou isso com o desempenho.

Então eu fui mais baixo. No meu entendimento, o provedor LINQ da equipe DB4O está apenas fazendo uma tradução para SODA. Portanto, eu percebi que eu iria escrever uma consulta de sódio para teste, eo que eu encontrei foi que usando SODA contra uma propriedade é ruim para o desempenho porque demorou 19902 ms para executar. Aqui está o código:

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;
}

Assim, pensar sobre por que isso seria tão ruim, eu decidi não usar uma propriedade implementadas através de auto e defini-lo minha auto porque as propriedades são realmente métodos e não valores:

public class SimpleObject
{
    private int _id;

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

Em seguida, reescreveu a consulta para usar o campo privado _id em vez da propriedade. O desempenho foi muito melhor em cerca de 91 ms. Aqui é que o código:

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;
}

Apenas para se certificar de que não é foi um acaso, eu corri o teste executado várias vezes e resultados semelhantes recebidas. Eu, então, acrescentou mais 60.000 registros para um total de 90.000, e esta foi a diferença de desempenho:

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

Espero que ajude. Eu sei que isso não explica por que, mas isso pode ajudar com o como. Além disso, o código para a consulta campo SODA não seria difícil para embrulhar a ser mais genérico.

Eu não tenho feito muito com db4o LINQ. Mas você pode usar o DiagnosticToConsole (ou ToTrace) e adicioná-lo à IConfiguration.Diagnostic (). AddListener. Isto irá mostrar se a consulta é otimizada.

Você não dão um monte de detalhes, mas é propriedade Id em SimpleObject indexados?

Uma vez que os diagnósticos estão ligados, você pode tentar a consulta como assim ...

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

ver se isso dá-lhe um plano de consulta diferente.

Você poderia 'construir' uma consulta linq dinâmico. Por exemplo, a API poderia ser assim:

O primeiro parâmetro: a expressão que diz sobre a qual propriedade você pesquisar Os outros parâmetros:. Do id ou o que você está procurando

 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");

A implementação constrói uma consulta dinâmica como esta:

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

Uma vez que tudo é combinado com OU o pode ser avaliada diretamente sobre os índices. Ela não precisa de ativação. EXEMPLO Implementações:

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;
    }

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top