Статьи

neo4j / cypher 2.0: оператор CASE

Я поигрался с тем, как вы можете смоделировать пребывание менеджеров Премьер-лиги в разных клубах в neo4j, и в итоге решил выбрать следующую модель:

Tiff менеджеров

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

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

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

что именно то, что мы хотим. Теперь мне нужно импортировать  реальный набор данных,  чтобы увидеть, как он выглядит!