Статьи

Neo4j: динамически добавлять свойство / устанавливать динамическое свойство

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

Например, узел для представления остановки может быть создан следующим образом:

1
CREATE (stop:Stop {arrival: "0802", departure: "0803H"})

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

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

01
02
03
04
05
06
07
08
09
10
11
MATCH (stop:Stop)
UNWIND ["arrival", "departure"] AS key
  
WITH key,
     toInteger(substring(stop[key], 0, 2)) AS hours,         
     toInteger(substring(stop[key], 2, 2)) AS minutes,
     CASE WHEN substring(stop[key], 4,1) = "H" THEN 30 ELSE 0 END AS seconds
  
WITH key, (hours * 60 * 60) + (minutes * 60) + seconds AS secondsSinceStartOfDay
  
RETURN key + "SecondsSinceStartOfDay" AS newKey, secondsSinceStartOfDay
1
2
3
4
5
6
7
╒═══════════════════════════════╤══════════════════════╕
│newKey                         │secondsSinceStartOfDay│
╞═══════════════════════════════╪══════════════════════╡
│arrivalSecondsSinceStartOfDay  │28920                
├───────────────────────────────┼──────────────────────┤
│departureSecondsSinceStartOfDay│29010                
└───────────────────────────────┴──────────────────────┘

Теперь мы готовы установить эти свойства на узле «стоп».

01
02
03
04
05
06
07
08
09
10
11
12
MATCH (stop:Stop2)
UNWIND ["arrival", "departure"] AS key
  
WITH stop,
     key,
     toInteger(substring(stop[key], 0, 2)) AS hours,         
     toInteger(substring(stop[key], 2, 2)) AS minutes,
     CASE WHEN substring(stop[key], 4,1) = "H" THEN 30 ELSE 0 END AS seconds
  
WITH stop, key, (hours * 60 * 60) + (minutes * 60) + seconds AS secondsSinceStartOfDay
WITH stop, key + "SecondsSinceStartOfDay" AS newKey, secondsSinceStartOfDay
SET stop[newKey] = secondsSinceStartOfDay
1
2
3
Invalid input '[': expected an identifier character, whitespace, '{', node labels, a property map, a relationship pattern, '.', '(', '=' or "+=" (line 12, column 9 (offset: 447))
"SET stop[newKey] = secondsSinceStartOfDay"
         ^

Хм, это не сработало, как ожидалось! Похоже, мы пока не можем установить динамические свойства, используя Cypher.

К счастью, мой коллега Майкл Хангер и сообщество Neo4j курировали библиотеку процедур APOC, и у нее есть только процедура, которая нам поможет.

Вам нужно скачать jar для вашей версии Neo4j и затем поместить его в каталог плагинов . Я использую Neo4j 3.1 Beta1, так что для меня это выглядит так:

1
2
3
4
5
6
$ tree neo4j-enterprise-3.1.0-BETA1/plugins/
  
neo4j-enterprise-3.1.0-BETA1/plugins/
└── apoc-3.1.0.1-all.jar
  
0 directories, 1 file

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

1
2
3
4
5
call dbms.procedures()
YIELD name
WITH name
WHERE name STARTS WITH "apoc"
RETURN COUNT(*)
1
2
3
4
5
╒════════╕
│COUNT(*)│
╞════════╡
183    
└────────┘

Теперь мы готовы динамически устанавливать свойства на графике. Процедура, которую мы будем использовать, это apoc.create.setProperty, и наш запрос легко обновить, чтобы использовать его:

01
02
03
04
05
06
07
08
09
10
11
12
MATCH (stop:Stop)
UNWIND ["arrival", "departure"] AS key
  
WITH stop,
     key,
     toInteger(substring(stop[key], 0, 2)) AS hours,         
     toInteger(substring(stop[key], 2, 2)) AS minutes,
     CASE WHEN substring(stop[key], 4,1) = "H" THEN 30 ELSE 0 END AS seconds
  
WITH stop, key, (hours * 60 * 60) + (minutes * 60) + seconds AS secondsSinceStartOfDay
WITH stop, key + "SecondsSinceStartOfDay" AS newKey, secondsSinceStartOfDay
CALL apoc.create.setProperty(stop, newKey, secondsSinceStartOfDay)
1
2
3
Query cannot conclude with CALL (must be RETURN or an update clause) (line 12, column 1 (offset: 439))
"CALL apoc.create.setProperty(stop, newKey, secondsSinceStartOfDay)"
 ^

Ой, я говорил слишком рано! Нам нужно получить возвращаемый столбец процедуры и вернуть его или просто вернуть счетчик, чтобы обойти это:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
MATCH (stop:Stop)
UNWIND ["arrival", "departure"] AS key
  
WITH stop,
     key,
     toInteger(substring(stop[key], 0, 2)) AS hours,         
     toInteger(substring(stop[key], 2, 2)) AS minutes,
     CASE WHEN substring(stop[key], 4,1) = "H" THEN 30 ELSE 0 END AS seconds
  
WITH stop, key, (hours * 60 * 60) + (minutes * 60) + seconds AS secondsSinceStartOfDay
WITH stop, key + "SecondsSinceStartOfDay" AS newKey, secondsSinceStartOfDay
CALL apoc.create.setProperty(stop, newKey, secondsSinceStartOfDay)
YIELD node
RETURN COUNT(*)
1
2
3
4
5
╒════════╕
│COUNT(*)│
╞════════╡
2      
└────────┘

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