Неочевидный дизайн enum в C#
Недавно столкнулся с задачей сконвертировать строковое значение в Enum. В строке может быть как нормальное строковое значение из перечисления, так и число.
Задачка тривиальная:
- Вызываем
- Если распарсили - используем это значение дальше
- Если нет - выполняем логику обработки отсутствия значения в enum
И тут на вход прилетает строковое значение "333" (такого значения в enum нет). А
Лично для меня супер неочевидно то, что TryParse не проверяет соответствие числа именованной константе из
А вот пример для воспроизведения:
Что интересно: в java и kotlin такого поведения не замечено (беглым тестом в онлайн компиляторе). В typescript тоже - при парсинге из числа, которое не входит в enum мы получаем undefined. Интересно, какие причины такой реализации?
#кейсы
Недавно столкнулся с задачей сконвертировать строковое значение в Enum. В строке может быть как нормальное строковое значение из перечисления, так и число.
Задачка тривиальная:
- Вызываем
Enum.TryParse(value, out var result)
- Если распарсили - используем это значение дальше
- Если нет - выполняем логику обработки отсутствия значения в enum
И тут на вход прилетает строковое значение "333" (такого значения в enum нет). А
TryParse
возвращает true
. Итог - некорректное поведение системы. И нет, это не баг - это стандартное поведение метода TryParse
.Лично для меня супер неочевидно то, что TryParse не проверяет соответствие числа именованной константе из
enum
. Теперь знаю, что такое поведение исправляется с помощью метода IsDefined
. (Источник - https://github.com/microsoft/referencesource/blob/master/mscorlib/system/enum.cs#L367)А вот пример для воспроизведения:
var value = "333";
Console.WriteLine($"Пытаемся распарсить: '{value}'");
if (Enum.TryParse<SomeEnum>(value, out var result))
{
Console.WriteLine($"Результат парсинга - '{result}'. Тип - '{result.GetType().Name}'");
}
else
{
Console.WriteLine("Распарсить значение не удалось");
}
public enum SomeEnum
{
FirstValue = 0,
SecondValue = 1
}
Что интересно: в java и kotlin такого поведения не замечено (беглым тестом в онлайн компиляторе). В typescript тоже - при парсинге из числа, которое не входит в enum мы получаем undefined. Интересно, какие причины такой реализации?
#кейсы
🤔5👏2🫡1
Да, это снова рекомендации по приложениям. На этот раз - DevToys - мультитул разработчика. Очень удобная штука, которую использую как в работе, так и в пет-проектах.
Мои основные кейсы:
- Форматирование JSON (или XML) - часто из дебага вытаскиваю огромные запросы эластика и монги, и надо их раскрыть и проанализировать. Копирую огромный JSON, вставляю в DevToys - получаю читаемый текст. Много проблемных запросов обнаружил как раз благодаря этому инструменту
- Сравнение текстов - снова пример с запросами в БД. Допустим, есть баг - на продакшн среде и на тестовой среде разное поведение. Беру запрос с прода, с теста и сравниваю. Или просто какие-то тексты, результаты от API, и прочее - всё, что нельзя или неудобно сравнивать через гит
- Проверка регулярных выражений - иногда пользуюсь сайтом regex101, иногда этим приложением
В зависимости от задач и специфики работы могут пригодиться совершенно разные функции этого приложения. Думаю, что оно будет полезно не только разработчикам, но и аналитикам, тестировщикам и многим другим. В общем, пробуйте!
#приложения
Flex Code
🔥8👍2🫡2
1. Введение в Elasticsearch (вы здесь)
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
Введение в Elasticsearch
Elasticsearch - одна из самых популярных enterprise-поисковых систем. Если быть точным, это документоориентированная база данных, которая заточена под полнотекстовый поиск. Базируется на open source библиотеке Apache Lucene, которая предоставляет различные алгоритмы работы с текстом.
Полнотекстовый поиск - это поиск по части текста, по его отдельным словам или даже слогам. Мы часто встречаемся с таким поиском, когда используем поисковые системы в интернете - google, yandex и другие. Вот упрощённый вариант того, что происходит когда вы пишете запрос "Как установить elasticsearch":
1. Слова разбиваются на токены:
2. Происходит поиск по тексту с проиндексированных сайтов на вхождение этих слов. Поиск не учитывает порядок, а просто ищет вхождение каждого слова и их комбинаций
3. Результаты ранжируются по релевантности: сначала те сайты, где есть все три слова в нужном порядке, потом сайты где просто есть эти слова и т.д.
4. Отсортированные результаты выдаются пользователю
Когда мы добавляем документ в эластик, каждое его поле индексируется специальным способом, чтобы по любой комбинации полей поиск был быстрым и эффективным, и поэтому его часто используют, например, в маркетплейсах для поиска товаров.
Однако в качестве основной БД её лучше не использовать:
- Elastic не поддерживает транзакции
- Имеет все проблемы документных БД - отсутствие чёткой структуры и связей
- Субъективно конечно, но он довольно сложен в настройке и обслуживании. Базовые CRUD гораздо проще и надёжнее реализовать на каком-нибудь PostgreSQL.
Кстати, elastic это коммерческая организация, и у неё есть true open source аналог - OpenSearch
Flexible Coding
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
Введение в Elasticsearch
Elasticsearch - одна из самых популярных enterprise-поисковых систем. Если быть точным, это документоориентированная база данных, которая заточена под полнотекстовый поиск. Базируется на open source библиотеке Apache Lucene, которая предоставляет различные алгоритмы работы с текстом.
Полнотекстовый поиск - это поиск по части текста, по его отдельным словам или даже слогам. Мы часто встречаемся с таким поиском, когда используем поисковые системы в интернете - google, yandex и другие. Вот упрощённый вариант того, что происходит когда вы пишете запрос "Как установить elasticsearch":
1. Слова разбиваются на токены:
['Как', 'установить', 'elasticsearch']
2. Происходит поиск по тексту с проиндексированных сайтов на вхождение этих слов. Поиск не учитывает порядок, а просто ищет вхождение каждого слова и их комбинаций
3. Результаты ранжируются по релевантности: сначала те сайты, где есть все три слова в нужном порядке, потом сайты где просто есть эти слова и т.д.
4. Отсортированные результаты выдаются пользователю
Когда мы добавляем документ в эластик, каждое его поле индексируется специальным способом, чтобы по любой комбинации полей поиск был быстрым и эффективным, и поэтому его часто используют, например, в маркетплейсах для поиска товаров.
Однако в качестве основной БД её лучше не использовать:
- Elastic не поддерживает транзакции
- Имеет все проблемы документных БД - отсутствие чёткой структуры и связей
- Субъективно конечно, но он довольно сложен в настройке и обслуживании. Базовые CRUD гораздо проще и надёжнее реализовать на каком-нибудь PostgreSQL.
Кстати, elastic это коммерческая организация, и у неё есть true open source аналог - OpenSearch
Flexible Coding
🔥6✍3🫡2👍1
1. Введение в Elasticsearch
2. Разворачиваем Elasticsearch (вы здесь)
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
Разворачиваем Elasticsearch для тестов
Попробуем развернуть эластик, чтобы в дальнейшем работать с ним. Самый простой способ установить его - использовать docker-compose (У вас он должен быть установлен).
Образы эластика можно брать из двух источников:
- Docker Registry самой компании elastic - недоступен из РФ. https://www.docker.elastic.co/r/elasticsearch
- Стандартный Docker Hub - https://hub.docker.com/_/elasticsearch. Он из РФ как раз доступен
Создайте файл docker-compose.yml и заполните его по аналогии
Далее выполните команду
Другие способы установки
Flexible Coding
2. Разворачиваем Elasticsearch (вы здесь)
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
Разворачиваем Elasticsearch для тестов
Попробуем развернуть эластик, чтобы в дальнейшем работать с ним. Самый простой способ установить его - использовать docker-compose (У вас он должен быть установлен).
Образы эластика можно брать из двух источников:
- Docker Registry самой компании elastic - недоступен из РФ. https://www.docker.elastic.co/r/elasticsearch
- Стандартный Docker Hub - https://hub.docker.com/_/elasticsearch. Он из РФ как раз доступен
Создайте файл docker-compose.yml и заполните его по аналогии
version: '3.8'
services:
test_elasticsearch:
image: elasticsearch:8.0.1
environment:
- bootstrap.memory_lock=true
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
ports:
- "9200:9200"
Далее выполните команду
docker compose up -d
. Если ошибок нет - поздравляю! Теперь у вас на ПК поднят elasticsearch.Другие способы установки
Flexible Coding
🤔6✍3😁1
1. Введение в Elastic
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch (вы здесь)
4. Как Elasticsearch обрабатывает текст?
CRUD-операции в Elasticsearch
Итак, эластик у нас поднят, теперь с ним можно делать всякое. Взаимодействие происходит по протоколу HTTP, поэтому можно воспользоваться утилитами curl или postman. Кстати, про HTTP я уже писал тут, тут и тут.
Вместо привычных таблиц у нас тут индексы. У индекса есть маппинг - описание полей в документе с их типами данных и другими параметрами. Этот маппинг может быть как динамическим (на основе документов, попадающих в индекс), так и ручным, когда мы задаём его самостоятельно.
Создать индекс -
В теле запроса можно передавать различные параметры, например маппинги (настройки полей), параметры индекса и др. Подробнее. Пока что добавим индекс без параметров, но держим в голове, что информацию о полях можно смотреть по запросу
Добавление документов
Добавляем новые документы с помощью POST-запроса, например:
И в ответе получим ID документа, в моём случае это
Кстати, взглянем на маппинг. Каждое наше поле имеет тип данных text для полнотекстового поиска, и дополнительное поле keyword для агрегаций, сортировок и точного поиска.
Удаление документа
Тут всё просто. Удалять документы можно с помощью запроса
Обновление документа
Обновляем документ с помощью POST-запроса: POST
Flexible Coding
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch (вы здесь)
4. Как Elasticsearch обрабатывает текст?
CRUD-операции в Elasticsearch
Итак, эластик у нас поднят, теперь с ним можно делать всякое. Взаимодействие происходит по протоколу HTTP, поэтому можно воспользоваться утилитами curl или postman. Кстати, про HTTP я уже писал тут, тут и тут.
Вместо привычных таблиц у нас тут индексы. У индекса есть маппинг - описание полей в документе с их типами данных и другими параметрами. Этот маппинг может быть как динамическим (на основе документов, попадающих в индекс), так и ручным, когда мы задаём его самостоятельно.
Создать индекс -
PUT /<index-name>
curl --location --request PUT 'http://localhost:9200/my-index'
В теле запроса можно передавать различные параметры, например маппинги (настройки полей), параметры индекса и др. Подробнее. Пока что добавим индекс без параметров, но держим в голове, что информацию о полях можно смотреть по запросу
GET /my-index/_mappings
. Добавление документов
Добавляем новые документы с помощью POST-запроса, например:
curl --location 'http://localhost:9200/my-index/_doc' \
--header 'Content-Type: application/json' \
--data '{
"_id": 1,
"firstName": "Дмитрий",
"lastName": "Бахтенков",
"information": "Имеет канал Flexible Coding"
}'
И в ответе получим ID документа, в моём случае это
5CqcmY8B0U9Sp4Y4qDgB
. А с помощью запроса GET /my-index/_doc/<id>
можем получить наш документ.Кстати, взглянем на маппинг. Каждое наше поле имеет тип данных text для полнотекстового поиска, и дополнительное поле keyword для агрегаций, сортировок и точного поиска.
"mappings": {
"properties": {
"firstName": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"information": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"lastName": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
Удаление документа
Тут всё просто. Удалять документы можно с помощью запроса
DELETE /my-index/_doc/_id
Обновление документа
Обновляем документ с помощью POST-запроса: POST
/<index>/_update/<_id>
. В теле запроса отправляем новый json, который будет содержимым нашего документаFlexible Coding
✍7🏆1
Выступление прошло вполне успешно! Конечно, есть над чем работать (как обычно), но в целом я доволен.
Чуть позже выложу презентацию и другие материалы)
Чуть позже выложу презентацию и другие материалы)
🔥9👍7
EXOZ OIDC Доклад.pdf
9.1 MB
А пока мы ждём запись доклада, держите презентацию
А ещё я написал статью на пространство "ВАЙТИ" в разбором кейса и приложением на гитхабе - которое можно посмотреть, поднять и потыкать)
#статьи
А ещё я написал статью на пространство "ВАЙТИ" в разбором кейса и приложением на гитхабе - которое можно посмотреть, поднять и потыкать)
#статьи
🔥6👍2🍾1
Итак, утро начинается с блокировки российских айпишников Docker Hub
Во-первых, тут в комментах есть разные варианты настроек зеркал (а ещё срачи!)
А ещё порадовал наш ответ - https://huecker.io/ ))) Не знаю, насколько надёжно использовать именно его в качестве прокси, но инструкция там удобная и есть адрес других проксей
Во-первых, тут в комментах есть разные варианты настроек зеркал (а ещё срачи!)
А ещё порадовал наш ответ - https://huecker.io/ ))) Не знаю, насколько надёжно использовать именно его в качестве прокси, но инструкция там удобная и есть адрес других проксей
😢4😭2
1. Введение в Elastic
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст? (вы здесь)
5. ...
Как Elasticsearch обрабатывает текст?
1. Маппинги
Маппинг в Elastic - это набор правил о том, какие поля как обрабатывать. Например мы добавляем объект с полями
Также нам доступен динамический маппинг. При такой настройке эластик сам определяет тип данных на основе первого документа, попавшего в него: если поле текстовое, будет тип text, если это число - тип number и т.д. Динамический маппинг довольно удобен, однако если в документе у вас текстовое поле, которое может содержать как числа, так и строки, и в первом документе было число (или, например, строка, которая эластиком успешно распарсилась в дату), то при попытке добавить новый документ с текстовым значением поля выпадет ошибка.
2. Анализаторы
Они отвечают за обработку и индексацию входящего текста и состоят из:
- Фильтров символов (character filter), которые обрабатывают исходный текст. Например, могут убирать html-теги, удалять спецсимволы и т.д.
- Токенизаторов (tokenizer) - преобразуют текст в массив символов по разделителю и другим правилам. Например: на вход принимаем "Этот текст написан Дмитрием", на выход -
Токенизаторы, кстати, могут быть кастомными. Например, есть токенизатор uax_url_email, который распознаёт электронную почту и вставляет её в отдельный токен, а не разбивает адрес, ну и много других. Подробнее можно почитать тут.
- Фильтр токенов (token filter) - пост-обработка массива токенов. Каждый элемент в массиве токенов может быть как-то преобразован или удалён. Например часто удаляются стоп-слова, такие как предлоги и артикли "an", "a", "the", "but" и т.д.
Flexible Coding
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст? (вы здесь)
5. ...
Как Elasticsearch обрабатывает текст?
1. Маппинги
Маппинг в Elastic - это набор правил о том, какие поля как обрабатывать. Например мы добавляем объект с полями
id
, name
и description
. Для id
и name
нам нужен поиск по точному совпадению, а по description
- полнотекстовый поиск. Мы можем настроить разные типы данных данных для этих полей: "keyword" для точного поиска, а "text" для полнотекстового.Также нам доступен динамический маппинг. При такой настройке эластик сам определяет тип данных на основе первого документа, попавшего в него: если поле текстовое, будет тип text, если это число - тип number и т.д. Динамический маппинг довольно удобен, однако если в документе у вас текстовое поле, которое может содержать как числа, так и строки, и в первом документе было число (или, например, строка, которая эластиком успешно распарсилась в дату), то при попытке добавить новый документ с текстовым значением поля выпадет ошибка.
2. Анализаторы
Они отвечают за обработку и индексацию входящего текста и состоят из:
- Фильтров символов (character filter), которые обрабатывают исходный текст. Например, могут убирать html-теги, удалять спецсимволы и т.д.
- Токенизаторов (tokenizer) - преобразуют текст в массив символов по разделителю и другим правилам. Например: на вход принимаем "Этот текст написан Дмитрием", на выход -
['этот', 'текст', 'написан', 'дмитрием']
Токенизаторы, кстати, могут быть кастомными. Например, есть токенизатор uax_url_email, который распознаёт электронную почту и вставляет её в отдельный токен, а не разбивает адрес, ну и много других. Подробнее можно почитать тут.
- Фильтр токенов (token filter) - пост-обработка массива токенов. Каждый элемент в массиве токенов может быть как-то преобразован или удалён. Например часто удаляются стоп-слова, такие как предлоги и артикли "an", "a", "the", "but" и т.д.
Flexible Coding
✍2🔥2🤯1
1. Введение в Elastic
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
5.Странности Ограничения Elasticsearch и Email (вы здесь)
Странности Ограничения Elasticsearch и Email
Как мы знаем из предыдущего поста - у эластика есть различные встроенные токенизаторы. Один из них -
- Результат стандартной токенизации:
- Результат токенизации uax_url_email:
В одной из систем, над которой я работал, был использован этот токенизатор для полнотекстового поиска по пользователям. И всё было прекрасно, пока не пришли... локальные домены верхнего уровня.
Домены верхнего уровня - это те самые
Поэтому идём в исходники
- Поиск по исходному коду эластика привёл к
- В файлах с реализацией UAX29URLEmailTokenizer ориентироваться довольно тяжело. Они созданы с помощью утилиты jflex, которая генерирует код на основе указанных спецификаций в специальных файлах. Тут нас интересует файл ASCIITLD.jflex, который содержит выражения для всех доменов на основе базы данных глобального регистратора IANA. На основе этих доменов elastic определяет: это собственно часть домена в адресе почты или просто строка. И в этой базе нет различных кастомных доменов, например "qwe", и никак их туда не добавить...
Какие выводы можно сделать? Если вы используете этот токенизатор и вам необходимо распознавать их эл. почту - проверьте, чтобы не использовались локальные домены
Flexible Coding
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как Elasticsearch обрабатывает текст?
5.
Странности
Как мы знаем из предыдущего поста - у эластика есть различные встроенные токенизаторы. Один из них -
uax_url_email_tokenizer
, который работает как стандартный, но при наличии в тексте адреса электронной почты, выделяет его в отдельный токен. Например:- Результат стандартной токенизации:
[ Email, me, at, john.smith, global, international.com ]
- Результат токенизации uax_url_email:
[ Email, me, at, john.smith@global-international.com ]
В одной из систем, над которой я работал, был использован этот токенизатор для полнотекстового поиска по пользователям. И всё было прекрасно, пока не пришли... локальные домены верхнего уровня.
Домены верхнего уровня - это те самые
.ru
, .com
и т.д. А в локальной сети их можно назначить самостоятельно, например .qwe
и использовать. Ну, кто-то их и использовал, и у нас сломался поиск по пользователям. Почему сломался? Непонятно. Адреса просто разбивались как в примере со стандартным механизмом. Документация нам ответа не дала...Поэтому идём в исходники
- Поиск по исходному коду эластика привёл к
UAX29URLEmailTokenizerFactory
, который просто возвращает new UAX29URLEmailTokenizer()
. Судя по импортам в файле, он находится в Lucene. Направляемся туда.- В файлах с реализацией UAX29URLEmailTokenizer ориентироваться довольно тяжело. Они созданы с помощью утилиты jflex, которая генерирует код на основе указанных спецификаций в специальных файлах. Тут нас интересует файл ASCIITLD.jflex, который содержит выражения для всех доменов на основе базы данных глобального регистратора IANA. На основе этих доменов elastic определяет: это собственно часть домена в адресе почты или просто строка. И в этой базе нет различных кастомных доменов, например "qwe", и никак их туда не добавить...
Какие выводы можно сделать? Если вы используете этот токенизатор и вам необходимо распознавать их эл. почту - проверьте, чтобы не использовались локальные домены
Flexible Coding
🔥5❤1🤔1