Внимание
В Cypher нет двунаправленных стрелок, но могут быть запросы, где направление не указано.
В браузере neo4j есть справочник по командам и операторам. Получить справку по операторам можно с помощью команды
:help
::help <Оператор>
:help MATCH
Рис. 3.5. Справка по оператору MATCH
Оператор MATCH
Для поиска по базе в neo4j используется оператор
MATCH
, следом идет шаблон поиска, условия выборки с помощью оператора WHERE
, и завершается запрос выводом результатов RETURN
.
Рис. 3.6. Оператор MATCH
Наша задача – создать правильный шаблон с добавлением условий. Неверно составленный запрос выдаст неверную информацию.
Рассмотрим простой запрос Cypher, где будут выбираться пользователи, у которых есть права локального администратора на компьютеры:
MATCH (u: User)-[r: AdminTo]->(c: Computer) RETURN u,r,c
Здесь переменным
u
, r
и c
будут передаваться результаты выборки, это не обязательно, если не нужно выделять какие-то особые условия, но для возврата данных все равно нужно определить переменную. Такой запрос нельзя профилировать и оптимизировать, а если добавить еще несколько узлов и связей, то перечисление будет требовать дополнительных затрат.Выходом из этой ситуации будет назначить общую переменную для всего запроса. Запрос приобретет следующий вид.
Рис. 3.7. Добавление общей переменной в запрос
Если выполнить измененный запрос, то результат будет аналогичным:
MATCH p=(:User)-[: AdminTo]->(:Computer) RETURN p
Различные варианты использования оператора
RETURN
мы рассмотрим позже.Указание метки будет влиять на скорость выполнения запроса, но и результат может быть другим. Например, в запросе выше упускается тот факт, что группы тоже могут иметь связь
CanRDP
. Таким образом, в запросе можно опустить указание метки User
, и он будет выглядеть следующим образом:MATCH p=(u)-[r: AdminTo]->(c: Computer) RETURN p
Варианты запросовCypher – достаточно свободный язык запросов, одинаковых результатов можно добиться разными путями. Рассмотрим выполнение запроса выше другими способами. Мы можем отдельно определить начальный и конечный узлы и затем запросить, есть ли между ними связь.
MATCH (u: User)
MATCH (c: Computer)
MATCH p=(u)-[r: AdminTo]->(c) RETURN p
Все это можно записать в одну строчку, но для удобства чтения сложные запросы лучше записывать в несколько строк.
И этот запрос можно оптимизировать, оставив только первый
MATCH
и после каждой строчки поставив запятую.MATCH (u: User),
(c: Computer),
p=(u)-[r: AdminTo]->(c) RETURN p
Браузер neo4j пометит, что данный запрос не очень удачный и его обработка может потребовать больше времени и ресурсов, тем не менее задача будет выполнена.
Объединение связейЗапрос может быть сложным: первым шагом мы можем запросить один шаблон, вторым – уже другой. Самый обычный пример для BloodHound – это поиск различных прав через членство в группах.
Рис. 3.8. Двухэтапный запрос
Возьмем все тот же пример с правами локального администратора. Посмотрим, какие группы имеют права локального администратора и какие пользователи входят в эти группы.
Информация
В сложных запросах стоит идти от конечной цели (последнего запроса) к начальному узлу.
MATCH p=(u: User)-[: MemberOf]-(g: Group)-[: AdminTo]->(c: Computer) RETURN p
Внимание
Данный запрос не вернет информацию, если пользователь имеет права локального администратора напрямую.
Мы можем объединить связи в этом запросе, используя логический оператор ИЛИ (представлен как |).
MATCH p=(u: User)-[: MemberOf|AdminTo]->(c: Computer) RETURN p
Синтактически этот запрос верен, но он будет искать только прямые связи и не учитывать, что группа может являться членом другой группы. Поэтому нужно добавить количество промежуточных узлов, к которому будет применяться данный шаблон.
Рис. 3.9. Объединение связей
И тогда наш запрос примет вид
MATCH p=(u: User)-[: MemberOf|AdminTo*1..]->(c: Computer) RETURN p
В предыдущем примере мы использовали запись *1.. – указание количества промежуточных узлов, к которым может применяться шаблон, в данном случае – от одного перехода до бесконечности. Число переходов здесь – это количество различных промежуточных узлов от начального узла до конечного.
Ниже приведены две таблицы, в которых описаны различные варианты синтаксиса.
Без указания типа связи:
Приведу несколько примеров. Все прямые связи между узлами:
MATCH p=(u: User)->(c: Group) RETURN p
MATCH p=(u: User)-[]->(c: Group) RETURN p
Все непрямые связи между узлами с указанием ограничений от одного до двух переходов:
MATCH p=(u: User)-[*1..2]->(c: Group) RETURN p
С указанием типа связей:
Пример – получить всех пользователей и их членство в группах:
MATCH p=(u: User)-[: MemberOf*1..]->(g: Group) RETURN p
Другой пример – получить все компьютеры, где пользователи являются локальными администраторами:
MATCH p=(u: User)-[: MemberOf|AdminTo*1..]->(c: Computer) RETURN p
Короткие путиПостроение графа со всеми непрямыми связями потребует большого количества ресурсов, и, скорей всего, база не выдержит и упадет. Для решения этой проблемы можно использовать операторы
ShortestPath
и AllShortestPaths
. Разница между ними в том, что первый находит один короткий путь, а второй – все, при условии, что они существуют.Для использования коротких путей необходимо поместить шаблон в круглые скобки. Так, предыдущий пример будет выглядеть следующим образом:
MATCH p=ShortestPath((u: User)-[*1..]->(c: Group)) RETURN p
Совет
Ограничение количества переходов поможет найти самые короткие пути.
В интерфейсе BloodHound в форме Поиск путей (Pathfinding) используется оператор
AllShortestPaths
, который применяется для поиска коротких путей между двумя указанными узлами.Оператор OPTIONAL MATCH
Оператор
OPTIONAL MATCH
работает точно так же, как и MATCH
; разница в том, что при использовании OPTIONAL MATCH
будет добавлять NULL
для недостающих элементов.Рассмотрим два примера, в которых будем искать локальных администраторов на компьютерах с помощью операторов
MATCH
и OPTIONAL MATCH
.MATCH (u: User)
MATCH (u)-[r: AdminTo]->(c: Computer)
RETURN u.name, c.name
Рис. 3.10. Результат с MATCH
MATCH (u: User)
OPTIONAL MATCH (u)-[r: AdminTo]-(c: Computer)
RETURN u.name, c.name
Рис. 3.11. Результат с OPTIONAL MATCH
Как можно увидеть на рисунке 3.11, там, где нет прав локального администратора, neo4j выставил
null
. То же самое в графическом представлении: мы получим всех пользователей, и только у некоторых будет связь AdminTo
с компьютерами.
Рис. 3.12. Графическое представление OPTIONAL MATCH
Условия фильтрации запросов
Ранее мы уже использовали фильтры по меткам
User
и Computer
, но только в некоторых случаях этого будет достаточно. Для указания более точных критериев поиска применяется оператор WHERE
, который может относиться ко всем свойствам узла и связи.Обычно оператор
WHERE
используется после формирования шаблона, но он может быть применен и внутри узла, и все условия будут относиться только к этому узлу:MATCH (g: Group) WHERE g.name = "DOMAIN ADMINS@DOMAIN.LOCAL" RETURN g.name
MATCH (g: Group WHERE g.name = "DOMAIN ADMINS@DOMAIN.LOCAL") RETURN g.name
Эти два запроса будут иметь одинаковый вывод, но второй запрос будет читаться сложнее.
Внимание
Помним, что Cypher чувствителен к регистру для свойств узла.
Рассмотрим синтаксис использования оператора
WHERE
на простых примерах.