2011-01-28 4 views
6

У меня есть таблица, которая содержит местоположение всех географических местоположений в мире и их взаимоотношения.Какую иерархическую модель мне следует использовать? Адяжентность, Вложенный или Перечислимый?

Вот пример, показывающий иерархию. Вы увидите, что данные хранятся как все три

  • Перенумерованного Путь
  • списка смежности
  • Nested Set

Данные, очевидно, никогда не меняется либо. Ниже приведен пример прямых предков места Брайтона в Англии, который имеет WOEID из 13911.

Таблица: geoplanet_places (Имеет 5.6million строк) Ancestors Большие изображения: http://tinyurl.com/68q4ndx

я тогда еще одна таблица называется entities. В этой таблице хранятся мои объекты, которые я хотел бы сопоставить с географическим положением. Я храню некоторую базовую информацию, но самое главное, я храню woeid, который является внешним ключом от geoplanet_places. enter image description here

В конечном итоге таблица entities будет содержать несколько тысяч объектов. И я хотел бы иметь возможность вернуть полное дерево всех узлов, которые содержат объекты.

Я планирую создать что-то, что облегчит фильтрацию и поиск объектов на основе их географического местоположения и сможет узнать, сколько объектов можно найти на этом конкретном узле.

Так что, если у меня есть только один объект в моем entities столе, я мог бы что-то вроде этого

`Земля (1)

Соединенное Королевство (1)

Англия (1)

East Sussex (1)

Брайтон-Сити (1)

Brighton (1) `

Позволяет затем сказать, что у меня есть другой объект, который находится в Девон, то это было бы показать что-то вроде:

Земли (2)

Соединенное интегрированным (2)

Англия (2)

Девон (1)

Восточный Суссекс (1) ...и т. д.

The (Counts), который скажет, сколько объектов «внутри» каждого географического местоположения не обязательно должно быть живым. Я могу жить с созданием моего объекта каждый час и кэшированием его.

Цель, чтобы быть в состоянии создать интерфейс, который могли бы начать показывать только те страны, которые субъекты ..

Так как

Argentina (1021), Chile (291), ..., United States (32,103), United Kingdom (12,338)

Затем пользователь нажимает на местоположение, такое как United Kindom, и затем будет предоставлено все непосредственные дочерние узлы, которые являются потомками Соединенного Королевства и имеют в них сущность.

Если в Объединенном Kindgdom есть 32 округа, но только 23 из них в конце концов, когда вы сверляете, имеют в них сущности, то я не хочу отображать другие 9. Это только местоположения.

Этот сайт метко демонстрирует функциональность, что я хочу добиться: http://www.homeaway.com/vacation-rentals/europe/r5 enter image description here

Как вы рекомендуете, что я управлять такой структурой данных?

Вещи, которые я использую.

  • PHP
  • MySQL
  • Solr

Я планирую иметь Сверло спады быть как можно быстрее. Я хочу создать интерфейс AJAX, который будет казаться бесполезным для поиска.

Мне также было бы интересно узнать, в каких столбцах вы бы порекомендовали индексирование.

+0

Это отличный вопрос! –

ответ

8

Как правило, существуют три вида запросов в иерархии, которые вызывают проблемы:

  1. Верните все предки
  2. всех потомков
  3. Возвращение всех детей (непосредственные потомки).

Вот небольшая таблица, которая показывает эффективность различных методов в MySQL:

     Ancestors Descendants Children  Maintainability InnoDB 
Adjacency list   Good  Decent  Excellent  Easy   Yes 
Nested sets (classic) Poor  Excellent Poor/Excellent Very hard  Yes 
Nested sets (spatial) Excellent Very good Poor/Excellent Very hard  No 
Materialized path  Excellent Very good Poor/Excellent Hard   Yes 

В children, poor/excellent означает, что ответ зависит от того, вы смешиваете метод со списком смежности, я. е. сохраняя parentID в каждой записи.

Для вашей задачи, вам нужен все три запрос:

  1. Всех предки, чтобы показать Землю/UK/Девон вещи
  2. Всех детей, чтобы показать «Направление в Европе» (пункты)
  3. Все потомки, чтобы показать «Направления в Европе» (цифры)

Я бы пошел на материализованные пути, так как эта иерархия редко меняется (только в случае войны, восстания и т. Д.).

Создать столбец VARCHAR называется path, индекс его и заполнить его со значением, как это:

1:234:6345:45454: 

где цифры являются первичными ключами соответствующих родителей, в правильном порядке (1 для Европы, 234 для UK и т. Д.)

Вам также понадобится таблица под названием levels, чтобы сохранить номера от 1 до 20 (или независимо от того, какой максимальный уровень гнездования вы хотите).

Для выбора всех предков:

SELECT pa.* 
FROM  places p 
JOIN  levels l 
ON  SUBSTRING_INDEX(p.path, ':', l.level) <> p.path 
JOIN  places pa 
ON  pa.path = CONCAT(SUBSTRING_INDEX(p.path, ':', l.level), ':') 
WHERE p.id = @id_of_place_in_devon 

Для выбора всех детей и подсчета мест в них:

SELECT pc.*, COUNT(pp.id) 
FROM places p 
JOIN places pc 
ON  pc.parentId = p.id 
JOIN places pp 
ON  pp.path BETWEEN pc.path AND CONCAT(pc.path, ':') 
     AND pp.id NOT IN 
     (
     SELECT parentId 
     FROM places 
     ) 
WHERE p.id = @id_of_europe 
GROUP BY 
     pc.id 
+0

Как вы можете решить такой вопрос. Как вы можете видеть, у меня есть значения parentID и lft rgt. Я не уверен, что я рассматриваю проблему с совершенно неправильной точки зрения. Может, мне нужно сделать шаг назад. Например, я хочу только вернуть непосредственных детей из одного узла и (Count). Но чтобы получить это значение Count, мне все равно придется создавать сложный запрос. Проблема заключается в том, что значение Count вычисляется в запросе и не будет сохраняться. Если я сохраню значение Count, то я могу потенциально использовать его и в моем запросе. Я просто путаю много. :) – Layke

+0

Каким должен быть pp.id/pp.path во втором запросе? И должны ли все пути заканчиваться: также? – Layke

+0

Laykes: извините, забыли добавить 'GROUP BY'. 'pp' - это таблица, которая выбирает всех потомков для каждого из детей« Европы », которые не являются самими категориями. Это просто псевдоним для той же таблицы «мест». – Quassnoi

0

Это вопрос, который я придумал. Это адаптация того, что вы предлагаете Квасной.

SELECT pa.*, level, SUBSTRING_INDEX(p.ancestry, '/', l.level), p.* 
FROM  geoplanet_places p 
JOIN  levels l 
ON  SUBSTRING_INDEX(p.ancestry, '/', l.level) <> p.ancestry 
JOIN  geoplanet_places pa 
ON  pa.woeid = SUBSTRING_INDEX(SUBSTRING_INDEX(p.ancestry, '/', l.level),'/',-1) 
WHERE p.woeid = "13911" 

Это возвращает всех родителей Брайтона.

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

SELECT  pa.*, GROUP_CONCAT(pa.name ORDER BY pa.lft asc),group_concat(pa.lft ), pa.ancestry 
              FROM  geo_places p 
              JOIN  levels l 
              ON  SUBSTRING_INDEX(CONCAT(p.ancestry, p.woeid,'/'), '/', l.level) <> p.ancestry 
              JOIN  geo_places pa 
              ON  pa.woeid = SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(p.ancestry, p.woeid,'/'), '/', l.level),'/',-1) 
              WHERE p.woeid IN ("12767488","12832668","12844837","131390","131391","12846428","24534461") 
              GROUP BY p.woeid 
Смежные вопросы