Я поигрался с тем, как вы можете смоделировать пребывание менеджеров Премьер-лиги в разных клубах в neo4j, и в итоге решил выбрать следующую модель:
Моделирование даты основано на подходе, с которым я впервые столкнулся в закрытой презентации, и более подробно описан в документации .
Я создал фиктивный набор данных с некоторыми назначенными встречами и увольнениями, а затем попытался написать запрос, чтобы показать мне, кто был менеджером команды на определенную дату.
CREATE (year2013 { name: "2013" }) CREATE (january2013 { name: "January" }) CREATE (january012013 { name: "1st" }) CREATE (january022013 { name: "2nd" }) CREATE (january032013 { name: "3rd" }) CREATE (january042013 { name: "4th" }) CREATE (january052013 { name: "5th" }) CREATE (chelsea { name: "Chelsea", type: "team" }) CREATE (joseMourinho { name: "Jose Mourinho"}) CREATE (mourinhoChelsea { name: "Mourinho tenure at Chelsea" }) CREATE (manUtd { name: "Manchester United", type: "team" }) CREATE (davidMoyes { name: "David Moyes"}) CREATE (davidMoyesUnited { name: "Moyes tenure at Manchester United" }) CREATE (year2013)-[:`January`]-(january2013) CREATE (january2013)-[:`01`]-(january012013) CREATE (january2013)-[:`02`]-(january022013) CREATE (january2013)-[:`03`]-(january032013) CREATE (january2013)-[:`04`]-(january042013) CREATE (january2013)-[:`05`]-(january052013) CREATE (january012013)-[:NEXT]-(january022013) CREATE (january022013)-[:NEXT]-(january032013) CREATE (january032013)-[:NEXT]-(january042013) CREATE (january042013)-[:NEXT]-(january052013) CREATE (mourinhoChelsea)-[:HIRED_ON {date: "January 1st 2013"}]->(january012013) CREATE (mourinhoChelsea)-[:MANAGER]->(joseMourinho) CREATE (mourinhoChelsea)-[:TEAM]->(chelsea) CREATE (mourinhoChelsea)-[:FIRED_ON]->(january032013) CREATE (davidMoyesUnited)-[:HIRED_ON {date: "January 2nd 2013"}]->(january022013) CREATE (davidMoyesUnited)-[:MANAGER]->(davidMoyes) CREATE (davidMoyesUnited)-[:TEAM]->(manUtd)
START team = node:node_auto_index('name:"Chelsea" name:"Manchester United"'), date = node:node_auto_index(name="5th") MATCH date<-[:NEXT*0..]-()<-[hire:HIRED_ON]-tenure-[:MANAGER]->manager, tenure-[:TEAM]->team, tenure-[fired?:FIRED_ON]-dateFired RETURN team.name, manager.name, hire.date, dateFired
Запрос начинается с 5 января, затем получает все предыдущие даты и ищет отношение «HIRED_ON», а затем следует, чтобы получить менеджера и команду, к которой он относится.
Затем мы пересекаем необязательные отношения ‘FIRED_ON’, потому что не хотим сказать, что менеджер в настоящее время находится в клубе, если его уволили.
Возвращает следующее:
==> +----------------------------------------------------------------------------------+ ==> | team.name | manager.name | hire.date | dateFired | ==> +----------------------------------------------------------------------------------+ ==> | "Manchester United" | "David Moyes" | "January 2nd 2013" | <null> | ==> | "Chelsea" | "Jose Mourinho" | "January 1st 2013" | Node[5]{name:"3rd"} | ==> +----------------------------------------------------------------------------------+ ==> 2 rows
В этом наборе данных Жозе Моуринью уволен 3 января, поэтому у «Челси» не должно быть менеджера 5 января.
Один из способов исключить его состоит в том, чтобы собрать все даты, на которые нас связывают наши «СЛЕДУЮЩИЕ» отношения, а затем проверить, присутствует ли «dateFired» в этой коллекции. Если это так, менеджер был уволен, и мы не должны возвращать их:
START team = node:node_auto_index('name:"Chelsea" name:"Manchester United"'), startDate = node:node_auto_index(name="5th") MATCH startDate<-[:NEXT*0..]-day WITH team, startDate, COLLECT(day) AS dates MATCH startDate<-[:NEXT*0..]-day<-[hire:HIRED_ON]-tenure-[:MANAGER]->manager, tenure-[:TEAM]->team, tenure-[?:FIRED_ON]-dateFired WHERE dateFired IS NULL OR NOT dateFired IN dates RETURN team.name, manager.name, hire.date, dateFired
Это возвращает следующее:
==> +----------------------------------------------------------------------+ ==> | team.name | manager.name | hire.date | dateFired | ==> +----------------------------------------------------------------------+ ==> | "Manchester United" | "David Moyes" | "January 2nd 2013" | <null> | ==> +----------------------------------------------------------------------+ ==> 1 row
К сожалению, мы сейчас не получаем строки для «Челси», потому что предложение WHERE отфильтровывает Моуринью.
Я не мог думать, как обойти это, поэтому Уэс предложил использовать neo4j 2.0 и оператор CASE, который делает это очень легко.
В конце концов я получил следующий запрос, который выполняет эту работу:
START team = node:node_auto_index('name:"Chelsea" name:"Manchester United"'), startDate = node:node_auto_index(name="2nd") MATCH startDate<-[:NEXT*0..]-day WITH team, startDate, COLLECT(day) AS dates MATCH startDate<-[:NEXT*0..]-day<-[hire:HIRED_ON]-tenure-[:MANAGER]->manager, tenure-[:TEAM]->team, tenure-[?:FIRED_ON]->dateFired RETURN team.name, CASE WHEN dateFired is null THEN manager.name WHEN dateFired IN dates THEN null ELSE manager.name END as managerName, CASE WHEN dateFired is null THEN hire.date WHEN dateFired IN dates THEN null ELSE hire.date END as hireDate
Здесь мы ввели оператор CASE, который работает примерно так же, как оператор SQL CASE, поэтому он должен быть как-то знакомым. Этот запрос возвращает следующее:
==> +----------------------------------------------------------+ ==> | team.name | managerName | hireDate | ==> +----------------------------------------------------------+ ==> | "Manchester United" | "David Moyes" | "January 2nd 2013" | ==> | "Chelsea" | <null> | <null> | ==> +----------------------------------------------------------+ ==> 2 rows
что именно то, что мы хотим. Теперь мне нужно импортировать реальный набор данных, чтобы увидеть, как он выглядит!