requête db4o: trouver tous les objets avec ID = {quoi que ce soit dans le tableau}

StackOverflow https://stackoverflow.com/questions/1689089

  •  18-09-2019
  •  | 
  •  

Question

Je l'ai emmagasinés 30.000 SimpleObjects dans ma base de données:

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

Je veux exécuter une requête sur db4o qui trouve tous SimpleObjects avec l'un des ID spécifiés:

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

Comment puis-je écrire cette requête afin que db4o n'active tous les 30.000 objets?

Était-ce utile?

La solution

Si vous essayez d'exécuter cette requête en utilisant LINQ il courra unoptimized (cela signifie que db4o allez récupérer tous les objets de type SimpleObject et déléguer le reste à LINQ aux objets)

La meilleure approche serait d'exécuter des requêtes n (depuis le champ id est indexé, chaque requête doit être exécutée rapidement) et agréger les résultats comme suggéré par « Mark Hall ».

Vous pouvez même utiliser LINQ pour cela (quelque chose comme)

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

Best

Autres conseils

Je ne suis pas un expert en la matière, et il pourrait être bon de poster sur les forums db4o à ce sujet, mais je pense avoir une solution. Il ne pas utiliser LINQ implique et en utilisant SODA.

est ce que je faisais. J'ai créé un projet rapide qui renseigne la base de données avec 30000 SimpleObject basée sur la définition de votre poste. J'ai alors écrit une requête pour saisir toutes les SimpleObjects de la base de données:

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

Quand je passai un StopWatch autour, cette course prend environ 740 millisecondes. J'ai ensuite utilisé votre code pour rechercher un 100 nombres aléatoires entre 0 et 2999. La réponse était 772 ms, donc en fonction de ce nombre je suppose qu'il tire tous les objets de la base de données. Je ne sais pas comment vérifier, mais je pense que plus tard, je l'ai prouvé avec la performance.

Je suis ensuite allé plus bas. Si je comprends bien le fournisseur LINQ de l'équipe db4o fait juste une traduction en SODA. Par conséquent, je me suis dit que je voudrais écrire une requête SODA pour tester, et ce que j'ai été que l'utilisation SODA contre une propriété est mauvais pour les performances, car il a fallu 19902 ms pour exécuter. Voici le code:

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

Alors pourquoi penser à cela serait si mauvais, j'ai décidé de ne pas utiliser une propriété implémentée automatiquement et définir mon auto parce que les propriétés sont en fait des méthodes et non des valeurs:

public class SimpleObject
{
    private int _id;

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

Je puis réécrit la requête pour utiliser le champ privé _id au lieu de la propriété. La performance était beaucoup mieux à environ 91 ms. Voici ce code:

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

Juste pour vous assurer qu'il est était pas un hasard, j'ai couru la course de test à plusieurs reprises et avons reçu des résultats similaires. J'ai ensuite ajouté un autre 60.000 dossiers pour un total de 90 000, et ce sont les différences de performance:

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

L'espoir qui aide. Je sais que cela n'explique pas vraiment pourquoi, mais cela pourrait aider à la faire. De plus, le code pour la requête de champ SODA ne serait pas difficile de conclure à être plus générique.

Je ne l'ai pas fait beaucoup avec db4o LINQ. Mais vous pouvez utiliser le DiagnosticToConsole (ou ToTrace) et l'ajouter à la IConfiguration.Diagnostic (). AddListener. Cela vous montrera si la requête est optimisée.

Vous ne donnez pas beaucoup de détails, mais est la propriété Id sur SimpleObject indexé?

Une fois le diagnostic sont activés, vous pouvez essayer la requête comme si ...

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

Voyez si cela vous donne un plan de requête différent.

Vous pouvez « construire » une requête LINQ dynamique. Par exemple, l'API pourrait ressembler à ceci:

Le premier paramètre: une expression qui indique sur quelle propriété vous recherchez Les autres paramètres:. Les id ou tout ce que vous cherchez

 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 mise en œuvre crée une requête dynamique comme ceci:

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

Puisque tout est combiné avec OU le peut être évalué directement sur les indices. Il n'a pas besoin d'activation. Exemple-Implementations:

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

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top