Запрос Db4o:найти все объекты с ID = {что-нибудь в массиве}

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

  •  18-09-2019
  •  | 
  •  

Вопрос

В моей базе данных я храню 30 000 SimpleObjects:

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

Я хочу запустить запрос в DB4O, который найдет все SimpleObjects с любым из указанных идентификаторов:

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 объектов?

Это было полезно?

Решение

Если вы попытаетесь выполнить этот запрос с помощью LINQ, он будет выполнен неоптимизированным (это означает, что db4o будет извлекать все объекты типа SimpleObject и делегировать остальные объекты LINQ).

Лучшим подходом было бы выполнить n запросов (поскольку поле id индексируется, каждый запрос должен выполняться быстро) и агрегировать результаты, как предложил «Марк Холл».

Вы даже можете использовать для этого 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]);
     }
}

Лучший

Другие советы

Я не эксперт в этом вопросе, и, возможно, было бы полезно написать об этом на форумах DB4O, но я думаю, что у меня есть решение.Это предполагает отказ от использования LINQ и использование SODA.

Это то, что я сделал.Я создал быстрый проект, который заполняет базу данных 30 000 SimpleObject на основе определения вашего сообщения.Затем я написал запрос для получения всех SimpleObjects из базы данных:

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

Когда я надел на него секундомер, этот пробег занял около 740 миллисекунд.Затем я использовал ваш код для поиска 100 случайных чисел от 0 до 2999.Ответ составил 772 мс, поэтому, исходя из этого числа, я предполагаю, что он извлекает все объекты из базы данных.Я не уверен, как это проверить, но позже, думаю, я доказал это производительностью.

Затем я пошел ниже.Насколько я понимаю, поставщик LINQ из команды DB4O просто выполняет перевод в SODA.Поэтому я решил написать запрос SODA для тестирования, и я обнаружил, что использование SODA для свойства плохо влияет на производительность, поскольку на его выполнение ушло 19902 мс.Вот код:

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 не составит труда сделать более универсальным.

Я мало что делал с db4o LINQ.Но вы можете использовать DiagnosticToConsole (или ToTrace) и добавить его в IConfiguration.Diagnostic().AddListener.Это покажет вам, оптимизирован ли запрос.

Вы не даете подробностей, но индексируется ли свойство Id SimpleObject?

После включения диагностики вы можете попробовать выполнить запрос следующим образом...

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

Посмотрите, даст ли это вам другой план запроса.

Вы можете «построить» динамический запрос linq.Например, API может выглядеть так:

Первый параметр:выражение, которое сообщает, какое свойство вы ищете другие параметры:идентификатор или что-то еще, что вы ищете.

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

}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top