Добрый день.
Пишу сюда, поскольку решения вопроса, найденные самостоятельно, не устраивают, а в данном случае необходимо найти оптимальный путь.
Описание проблемы:
Реализую проект на django с картографическим отображением данных в виде маркеров.
Имеется древовидная структура состоящая из узлов и конечных объектов и список маркеров. Каждому маркеру принадлежит несколько конечных объектов, каждый объект не привязан жестко к одному маркеру (связь между моделями m2m).
При запросе по нескольким критериям отбираются необходимые маркеры, а также некоторая информация, определяющая, то, как ему отображаться (В запросе отсылаем уровень узла дерева).
Исходя из принадлежащих объектов, маркеры фильтруются (если маркеру принадлежит один или несколько конечных объектов которые, являются потомками запрашиваемого узла дерева, то данный маркер выбирается и отправляется с ответом сервера в составе списка.
Помимо этого, для каждого маркера выясняется его способ отображения - это данные дочернего узла от запрашиваемого (если этому маркеру принадлежат несколько элементов, восходящих до разных потомков первого уровня (дочерних узлов), то принимаются данные от всех).
О реализации:
Изначально я написал совершенно глупую функцию, которая тремя вложенными циклами запросов(!) формировала два списка, один из которых содержал маркеры, другой содержал информацию о их отображении (у меня в примере будет "цвет"), затем они склеивались по индексам и пересылались. Естественно, что скорость выполнения такого монстра была удручающая. (тест из 20 маркеров и дерева 5 уровней и десятка объектов, раскиданных по маркером выдал время исполнения порядка 1-1.2 секунды на базах mysql и postgres. Решил переписывать это (тот вариант приводить здесь не буду) во что бы то ни стало с целью уменьшить количество циклов запросов. Решил воспользоваться mptt и select и prefetch_related() (далее приведу куски кода функции, из которых убрал все лишнее):
# Модель Marker, связанная ManyToMany по полю "objects" c моделью Object // Модель Object, связанная с моделью Type по полю "type" - ForeighnKey // Модель Type, связанная сама с собой по полю "parent"
# Переменную level отправляем в запросе
tree = Type.objects.get(id=level)
related_types = list(MPTTModel.get_descendants(tree, include_self=True).values_list('id')) #Выбираем все маркеры, в которых потомки элемента с type == level
types_list = []
for a in related_types:
types_list += a
markers_query = Marker.objects.filter(items__type__in=types_list).prefetch_related('objects') # Тормоз0 - items__type__in=types_list.
#Еще нужно сказать, что prefetch_related никакого прироста к скорости не дает, однако, ради этого решил перевести проект на 1.4
for a in markers_query: # Проходим по списку маркеров
types_color = a.items.select_related('type') # Тормоз1 тут (однако, выбор родственных потомков дает прирост к скорости выполнения запроса, примерно 40%.
res = []
for b in types_color:
res += MPTTModel.get_ancestors(b.type, include_self=True).filter(parent=level).values() # Тормоз2, проход по списку // Это главное место, тормозящее всю конструкцию
# // + интересно, можно ли получить потомков только одного определенного уровня
markers_array[a.name] = {'id': a.pk, 'name': a.name, 'value': a.value, 'lon': a.lon, 'lat': a.lat, 'b_date': a.b_date, 'e_date': a.e_date, 'type': list(set(res))}
В итоге получились те же три уровня циклов запросов (только 3-й по алгоритмам mptt). Скорость, к сожалению стала еще медленнее в сравнении с первым вариантом.
Очень буду рад, если подскажите, есть ли возможность собрать нужную информацию без использования такого кол-ва вложенных циклов и какая реализация задачи является оптимальной?
Не могу определить сейчас, понятно ли я написал. Если слишком путано, могу попробовать нарисовать диаграммки.
Updated 9 April 2012, 15:08 by v.bazhin.