Функции для работы со строками
Cypher имеет функции для работы со строками, при обычном использовании BloodHound они не используются, но при разработке собственных утилит и запросов могут потребоваться. Не будем глубоко погружаться в эту тему и быстро пройдемся по основным функциям.
Функция REPLACEФункция
replace
замещает один текст в строке другим. Простой пример – заменим в строке одно слово другим:WITH 'BloodHound' AS Original
RETURN Original, replace(Original, 'Blood', 'Sharp') AS Replaced
Рис. 3.31. Результат замены
Другой пример: в статье мы хотим показать интересный вектор, и, чтобы не закрашивать имя домена на узлах, мы можем поменять его название на что-то нейтральное.
MATCH (c) SET c.name = replace(c.name, "CORP.LOCAL", "DOMAIN.LOCAL")
Функция SPLITМы уже сталкивались с этой функцией в разделе про работу с датами. Функция
split
разбивает строку по разделителю и создает список. Например, мы хотим получить свойство name пользователя без указания домена. В качестве разделителя будет выступать @,
и запрос будет выглядеть следующим образом:MATCH (u: User {name: "USER@DOMAIN.LOCAL"})
RETURN u.name AS name, SPLIT(u.name,'@') AS list
Рис. 3.32. Результат использования SPLIT
В разделе про работу со списками мы уже говорили, что элементы в списке имеют идентификатор и можно получить каждый элемент отдельно, указав его индекс. Поэтому, если мы хотим получить только имя узла, запрос будет следующим:
MATCH (u: User {name: "USER@DOMAIN.LOCAL"})
RETURN u.name AS name, split (u.name,'@')[0] AS name_list
Рис. 3.33. Получение имени из списка
Функции TOLOWER и TOUPPERФункция
ToLower
переводит строку в строчные буквы, а функция ToUpper
– в заглавные. Функция ToLower
полезна при формировании данных в табличном режиме для размещения в отчете, так как заглавные буквы смотрятся громоздкими, в то время как строчные буквы более аккуратны. Например, выгрузим имена компьютеров и отобразим их строчными буквами:MATCH(c: Computer) RETURN toLower(c.name) AS name
Рис. 3.34. Использование функции ToLower
В BloodHound значимые свойства узла пишутся заглавными буквами, и функция
ToUpper
будет полезна при добавлении свойств к новым узлам. Например, свойство objectid
используется для идентификации узла, для пользователей, компьютеров и групп используется sid
в качестве идентификатора, для всех остальных объектов используется guid
, и свойство objectid
записывается заглавными буквами. В neo4j есть функция randomUUID
для генерации guid
, которую можно использовать при создании новых узлов, и, чтобы следовать стандарту BloodHound, ее надо записать заглавными буквами. Вот так будет выглядеть запрос с использованием функции ToUpper
:RETURN toUpper(randomUUID()) AS objectid
Рис. 3.35. Использование функции ToUpper
Функции LTRIM, RTRIM и TRIMФункция
ltrim
удаляет все пробелы слева от строки, rtrim
– справа, а trim
– одновременно справа и слева:WITH " BloodHound " as string
RETURN ltrim(string) AS 'Left Trim', trim(string) AS 'Trim', rtrim(string) AS 'Rigth Trim'
Рис. 3.36. Результат использования функции trim
Функции LEFT и RIGHTФункция
left
отсчитывает слева указанное количество символов и возвращает их как результат, все остальное отбрасывается. Функция right
делает все то же самое, только справа:WITH "BloodHound" as string
RETURN left(string, 5), right(string,5)
Рис. 3.37. Результат использования функций left и right
Функция SUBSTRINGФункция
substring
возвращает от строки подстроку с указанной позицией и длиной. Например:WITH "BloodHound" as string
RETURN string, substring(string, 3,4)
Рис. 3.38. Результат использования функции substring
Объединение строкМожет возникнуть ситуация, когда потребуется объединить две строки в одну. Для этого можно использовать знак
+.
Например, объединим два слова в одно, тогда запрос Cypher получится следующий:WITH "Blood" AS s1, "Hound" AS s2
RETURN s1, s2, s1+s2 AS string
Рис. 3.39. Объединение двух слов
Функция TOSTRINGФункция
ToString
приводит к типу данных string
. Рассмотрим простой пример: запросим текущую дату, воспользуемся функцией ToString
и посмотрим на результаты. Чтобы получить тип данных, воспользуемся процедурой apoc.meta.type
из плагина APOC:WITH Date() AS date
RETURN date, apoc.meta.type(date), apoc.meta.type(toString(date))
Рис. 3.40. Результат использования функции ToString
Создание и удаление узлов и связей
Базовое использование Bloodhound не подразумевает создания новых элементов, тем не менее это важный функционал для будущих работ. Как ни странно, создание связей требует понимания работы оператора
MATCH
, условий выборки и установки свойств для узлов, и поэтому в книге этот раздел идет предпоследним в теме языка запросов Cypher.Перед началом удалим все данные в базе, и на этом этапе можно воспользоваться функционалом BloodHound.
Оператор CREATEДля создания узлов используется оператор CREATE.
Рис. 3.41. Оператор Create
Запрос на создание узлов состоит из следующих элементов:
●
var
– переменная;●
Label
– метка, необязательный элемент;●
prop1
и prop2
– названия свойств, а val
– их значения после двоеточия.Свойства указываются в фигурных скобках и разделяются запятыми.
Внимание
Если идет цепочка запросов, то переменные должны отличаться или каждый запрос завершается точкой с запятой.
Теперь создадим узел с меткой
User
и свойством name
, которое имеет значение TEST
. Запрос Cypher будет следующим:CREATE (u: User {name: "TEST"})
Если повторить этот запрос, то будет создан еще один пользователь с тем же именем, отличаться будет только
id
.Выполним запрос, чтобы получить все созданные узлы.
MATCH (u: User) RETURN u
Рис. 3.42. Два новых пользователя
В инфраструктуре Active Directory нет одинаковых объектов. Даже если имена у них будут совпадать, все равно найдутся отличительные признаки, например
SID
или Distinguishedname
.Чтобы избежать создания повторных узлов, в ранних версиях использовалась связка
CREATE UNIQUE
, теперь ту же роль играет оператор MERGE
.Оператор MERGEПринцип работы оператора
MERGE
точно такой же, как и у CREATE
, с одним условием: он проверяет, существует ли такой элемент в базе.
Рис. 3.43. Оператор MERGE
Если мы выполним следующий запрос Cypher, то ничего не произойдет.
MERGE(u: User {name: "TEST"})
Давайте добавим новые элементы, но введем еще одно свойство
sid
:MERGE(m: User {name: "TEST", sid:'U-001'})
MERGE(n: User {name: "TEST", sid:'U-002'})
В данном случае оба узла удалось создать, так как они имеют отличительный признак в виде свойства
sid
.
Добавление свойств при создании
Как говорилось выше, свойства передаются в фигурных скобках, но в операторе
MERGE
есть функция