Несколько недель назад я написал пост, в котором показан пример запроса на шифрование, в котором использовался оператор WITH, но я до сих пор не до конца понимаю, как он работает, поэтому решил написать еще несколько запросов, в которых он используется.
Я хотел выяснить, имеет ли Луис Суарес лучший результат в зависимости от того, в какой день проводится матч.
Мы начнем с поиска всех матчей, в которых он играл и в какие дни были эти матчи:
START player = node:players('name:"Luis Suárez"') MATCH game-[:in]-stats-[:played]-player, game-[:on_day]-day RETURN day.name, game.name
+---------------------------------------------------+ | day.name | game.name | +---------------------------------------------------+ | "Saturday" | "Liverpool vs Southampton" | | "Saturday" | "Southampton vs Liverpool" | | "Saturday" | "Liverpool vs Reading" | | "Saturday" | "West Bromwich Albion vs Liverpool" | ... +---------------------------------------------------+ 29 rows
Затем мы можем сгруппировать эти матчи по дням, чтобы узнать, сколько игр он сыграл в определенный день:
START player = node:players('name:"Luis Suárez"') MATCH game-[:in]-stats-[:played]-player, game-[:on_day]-day RETURN day.name, COUNT(game.name)
+--------------------------------+ | day.name | COUNT(game.name) | +--------------------------------+ | "Sunday" | 13 | | "Wednesday" | 4 | | "Monday" | 1 | | "Saturday" | 11 | +--------------------------------+ 4 rows
Теперь мы хотим выяснить, в какие дни были игры, в которых забил Суарес, поэтому мы начинаем с того, что возвращаемся каждый день, когда Суарес играл в матче, а затем возвращаем коллекцию, содержащую информацию об играх, в которые он играл в тот день, и о том, забил ли он:
+----------------------------------------------------------------------------------------+ | day.name | games | +----------------------------------------------------------------------------------------+ | "Sunday" | [["scored_in","Liverpool vs Manchester City"]…] | | "Wednesday" | [[<null>,"Tottenham Hotspur vs Liverpool"],[<null>,"Stoke City vs Liverpool"]...] | | "Monday" | [[<null>,"Liverpool vs West Bromwich Albion"]] | | "Saturday" | [[<null>,"Liverpool vs Southampton"]…] | +----------------------------------------------------------------------------------------+ 4 rows
Этот запрос стал немного сложнее, чем наши предыдущие, потому что мы хотели вернуть все дни, когда Суарес сыграл матчи, даже если он не забивал в тот день.
Единственная интересная вещь в первых двух строках состоит в том, что мы необязательно сопоставляем отношение ‘scored_in’, чтобы мы могли справиться с ситуацией, когда Суарес не забил, все еще возвращая строку.
В третьей строке мы возвращаем день, а затем получаем коллекцию кортежей отношения ‘scored_in’ и соответствующей игры.
Мы используем DISTINCT в этой строке, чтобы позаботиться о ситуации, когда Суарес забил несколько раз в одном матче. Мы рассчитываем количество игр, в которых Суарес забил, поэтому подсчет нескольких голов в одном матче разрушил бы этот счет.
Затем нам нужно немного подправить этот запрос, чтобы получить количество совпадений, в которых забил Суарес, а не просто вернуть их. В итоге мы получаем следующее:
START player = node:players('name:"Luis Suárez"') MATCH game-[:in]-stats-[:played]-player-[r?:scored_in]-game-[:on_day]-day WITH day, COLLECT(DISTINCT([type(r), game.name])) AS games RETURN day.name, REDUCE(totalGames = 0, game in FILTER(x in games : head(x) = "scored_in"): totalGames + 1) AS gamesScoredIn
+-----------------------------+ | day.name | gamesScoredIn | +-----------------------------+ | "Wednesday" | 2 | | "Saturday" | 6 | | "Sunday" | 7 | | "Monday" | 0 | +-----------------------------+ 4 rows
Мы начинаем с фильтрации игр, чтобы сохранить только те, в которых забил Суарес. Затем мы запускаем REDUCE для полученной коллекции, которая просто добавляет 1 к аккумулятору для каждой записи в коллекции.
Теперь, когда мы получили, следующим шагом будет объединение наших игр и игр, набранных по запросам, чтобы мы могли видеть, какой процент игр Суарес забивает в каждый день.
В итоге мы получаем следующее:
START player = node:players('name:"Luis Suárez"') MATCH game-[:in]-stats-[:played]-player, game-[:on_day]-day WITH player, day, COUNT(game) AS playedGames MATCH game-[:in]-stats-[:played]-player-[r?:scored_in]-game-[:on_day]-day WITH day, COLLECT(DISTINCT([type(r), game.name])) AS games, playedGames WITH day, REDUCE(totalGames = 0, game in FILTER(x in games : head(x) = "scored_in"): totalGames + 1) AS scoredGames, playedGames RETURN day.name, playedGames, scoredGames, (scoredGames*1.0/playedGames*1.0) * 100 AS percentage
+-------------------------------------------------------------+ | day.name | playedGames | scoredGames | percentage | +-------------------------------------------------------------+ | "Saturday" | 11 | 6 | 54.54545454545454 | | "Monday" | 1 | 0 | 0.0 | | "Wednesday" | 4 | 2 | 50.0 | | "Sunday" | 13 | 7 | 53.84615384615385 | +-------------------------------------------------------------+ 4 rows
Одна вещь, которая меня здесь смущала, это то, что нам нужно передавать все, что мы хотим, в конечном итоге, возвращать в каждом операторе WITH, иначе он не будет нам доступен в конце.
Если мы собираемся сделать несколько операторов MATCH, нам нужно передать начальный узел в предыдущем операторе WITH, что в данном случае означает, что нам нужно передать переменную player.
Кроме этого этот запрос является объединением предыдущих двух, за исключением того, что мы добавили некоторую арифметику в последнюю строку, чтобы вычислить% совпадений, в которых набрал Суарес. Мне пришлось умножить каждое число на 1,0, чтобы форсировать арифметику на основе числа с плавающей запятой, а не целочисленный
В ответ на наш первоначальный вопрос не имеет значения, в какой день проводится матч, Суарес забивает примерно в любой другой игре.