Pregunta

Me he guardado 30.000 SimpleObjects en mi base de datos:

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

Quiero ejecutar una consulta en DB4O que encuentra todas SimpleObjects con cualquiera de los ID 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();
}  

¿Cómo se escribe esta consulta para que DB4O no se activa todos los 30.000 objetos?

¿Fue útil?

Solución

Si intenta ejecutar esta consulta utilizando LINQ que va a correr sin optimizar (eso significa que db4o se va a recuperar todos los objetos de tipo SimpleObject y delegar el resto de LINQ a objetos)

El mejor enfoque sería que se ejecutan consultas n (ya que está indexado el campo id, cada consulta debe correr rápido) y agregar los resultados según lo sugerido por "Mark Hall".

Puede incluso utilizar LINQ para esto (algo así)

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

Mejor

Otros consejos

No soy un experto en esto, y que podría ser bueno para publicar en los foros DB4O al respecto, pero creo que tengo una solución. No implica el uso de LINQ y el uso de soda.

Esto es lo que hice. He creado un proyecto rápido que rellena la base de datos con 30.000 SimpleObject basado en la definición de su puesto. Entonces escribí una consulta para agarrar todos los SimpleObjects de la base de datos:

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

Cuando Envolví un cronómetro alrededor de ella, que se ejecutan toma alrededor de 740 milisegundos. Luego utiliza su código de búsqueda de un 100 números aleatorios entre 0 y 2999. La respuesta fue 772 ms, por lo que en base a ese número Estoy asumiendo que está tirando de todos los objetos de la base de datos. No estoy seguro de cómo comprobar que, más adelante, pero creo que he demostrado con el rendimiento.

Entonces fui inferior. Desde mi entender el proveedor LINQ del equipo DB4O sólo está haciendo una traducción a SODA. Por lo tanto, que pensé que iba a escribir una consulta SODA para probar, y lo que encontré fue que el uso de la SODA contra una propiedad es malo para el rendimiento, ya que tuvo 19902 ms a ejecutar. Aquí está el 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;
}

Así que pensar acerca de por qué esto sería tan malo, decidí no utilizar una propiedad de auto-aplicado y definir mi auto porque las propiedades son en realidad métodos y no valores:

public class SimpleObject
{
    private int _id;

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

Entonces volvió a escribir la consulta para utilizar el campo privado _id lugar de la propiedad. El rendimiento era mucho mejor en unos 91 ms. Aquí está el 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;
}

Sólo para asegurarse de que está no era una casualidad, me encontré con la ejecución de prueba varias veces y recibimos resultados similares. Luego añade otros 60.000 registros para un total de 90.000, y esta era las diferencias de rendimiento:

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

Espero que ayude. Yo sé que en realidad no se explica por qué, pero esto podría ayudar con el cómo. También el código para la consulta de campo SODA no sería difícil para envolver a ser más genérico.

No he hecho mucho con LINQ db4o. Sin embargo, se puede utilizar el DiagnosticToConsole (o ToTrace) y añadirlo a la IConfiguration.Diagnostic (). AddListener. Esto le mostrará si se optimiza la consulta.

Usted no da muchos detalles, pero es la propiedad ID en SimpleObject indexados?

Una vez que el diagnóstico se encienden, puede probar la consulta como tal ...

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

A ver si eso le da un plan de consulta diferente.

Se puede 'construir' una consulta LINQ dinámica. Por ejemplo, la API podría tener este aspecto:

El primer parámetro: una expresión que dice sobre la que se busca la propiedad Los otros parámetros.: Los identificadores o lo que sea que estés buscando

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

La aplicación construye una consulta dinámica de esta manera:

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

Ya que todo se combina con O la puede evaluarse directamente en los índices. No necesita la activación. Ejemplo-Implementaciones:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top