Статьи

Neo4j vs Relational: Рефакторинг — Извлечение узла / таблицы

В своем предыдущем посте в блоге я показал, как добавить новое свойство / поле к узлу с меткой / записью в таблице для набора данных футбольных передач, с которым я играл.

После введения этого свойства «национальности» я понял, что у меня теперь есть некоторое дублирование в модели:

2016-05-22_10-15-15

Players.nationality и clubs.country ссылаются на одни и те же страны, но они оба хранят их в виде строк, поэтому мы не можем гарантировать целостность наших стран и гарантировать, что мы ссылаемся на одну и ту же страну.

У нас та же проблема в графической модели:

2016-05-22_10-40-40

На этот раз Player.nationality и Club.country относятся к одним и тем же странам.

Мы можем решить нашу проблему, введя таблицу стран в реляционной модели и набор узлов с меткой «Страна» в графовой модели. Начнем с реляционного.

Это модель, к которой мы стремимся:

2016-05-22_10-50-43

Первое, что нам нужно сделать, это создать таблицу стран и заполнить ее:

1
2
3
4
CREATE TABLE countries (
    "code" CHARACTER VARYING(3) NOT NULL PRIMARY KEY,
    "name" CHARACTER VARYING(50) NOT NULL
);
1
2
3
INSERT INTO countries VALUES('MNE', 'Montenegro');
INSERT INTO countries VALUES('SWZ', 'Swaziland', 'caf');
...

Далее давайте обновим таблицу клубов для ссылки на таблицу стран:

1
2
3
ALTER TABLE clubs
ADD COLUMN country_id CHARACTER VARYING(3)
REFERENCES countries(code);

И давайте запустим запрос для заполнения этого столбца:

1
2
3
4
5
6
UPDATE clubs AS cl
SET country_id = c.code
FROM clubs
INNER JOIN countries AS c
ON c.name = clubs.country
WHERE cl.id = clubs.id;

Этот запрос перебирает все клубы, запрашивает таблицу стран, чтобы найти идентификатор страны для этой строки, а затем сохраняет его в поле «country_id». Наконец, мы можем удалить поле «страна»:

1
2
ALTER TABLE clubs
DROP COLUMN country;

Теперь мы делаем то же самое упражнение для стола игроков:

1
2
3
ALTER TABLE players
ADD COLUMN country_id CHARACTER VARYING(3)
REFERENCES countries(code);
1
2
3
4
5
6
UPDATE players AS p
SET country_id = c.code
FROM players
INNER JOIN countries AS c
ON c.name = players.nationality
WHERE p.id = players.id;
1
2
ALTER TABLE players
DROP COLUMN nationality;

Теперь пришло время для графика. Вот модель, к которой мы хотим добраться:

2016-05-22_10-51-49

Сначала мы создадим страны:

1
2
CREATE CONSTRAINT ON (c:Country)
ASSERT c.id IS UNIQUE
1
2
3
4
LOAD CSV WITH HEADERS FROM "file:///countries.csv"
AS row
MERGE (country:Country {id: row.countryCode})
ON CREATE SET country.name = row.country

А теперь давайте заставим клубы и игроков указывать на узлы этих стран и избавляться от их соответствующих свойств национальности / страны:

1
2
3
4
MATCH (club:Club)
MATCH (country:Country {name: club.country})
MERGE (club)-[:PART_OF]->(country)
REMOVE club.country
1
2
3
4
MATCH (player:Player)
MATCH (country:Country {name: player.nationality})
MERGE (player)-[:PLAYS_FOR]->(country)
REMOVE player.nationality

И это все, теперь мы можем писать запросы к нашей новой модели.