Flexible Coding
148 subscribers
163 photos
2 files
96 links
Download Telegram
1. Введение в Elastic
2. Разворачиваем Elasticsearch
3. CRUD-операции в Elasticsearch
4. Как эластик обрабатывает текст?
5. Странности и ограничения Elasticsearch и Email
6. Тестируем анализаторы Elasticsearch
7. Поисковые запросы в Elasticsearch (вы здесь)

Поисковые запросы в Elasticsearch


Итак, мы развернули Elasticsearch, загрузили туда документы, и теперь пришло время воспользоваться его главной функцией — поиском.

Search API в Elasticsearch предлагает использовать JSON с различными ключевыми словами, которые описывают разные типы поиска. Чтобы выполнить запрос, нужно обратиться к эндпоинту:
GET /my-index-000001/_search. 
{
"query": {
<запрос>
}
}


Как мы помним, в Elasticsearch есть анализаторы. Они обрабатывают текст не только при индексации, но и при выполнении поисковых запросов. Это значит, что текст запроса тоже проходит через анализаторы, что позволяет осуществлять более гибкий и точный поиск.

А что внутри Query?

Переходим к основным типам запросов. Один из самых распространённых — это match: { field: "searchable text" }. Он используется для полнотекстового поиска по тексту, числу, булевому значению или дате. Текст при этом анализируется с помощью соответствующего анализатора.

Если вам нужен поиск по точному совпадению, то используйте term: { field: "value" }. Важно помнить, что для таких запросов поле должно иметь тип данных "keyword".

Есть также terms: { field: ["value1", "value2"] } — он проверяет, является ли значение одним из элементов массива. Это похоже на оператор IN в SQL или $in в MongoDB.

Для проверки существования поля используется exists: { field: "fieldname" }. Этот запрос вернёт false, если в исходном JSON поле было NULL или пустым массивом [].

Помимо ключа поля и значения для поиска, в запросах можно указывать дополнительные параметры. Например:
- operator: AND | OR для полнотекстового поиска. Этот параметр определяет, должны ли все токены запроса присутствовать в документе (AND) или хотя бы один (OR).
- fuzziness: int — расстояние Левенштейна для поиска с ошибками. Это позволяет искать слова с определённым количеством опечаток. Подробнее об этом можно прочитать здесь.

И не забудем о Nested-запросах, которые используются для вложенных массивов объектов в структуре документа. У них свои особенности и нюансы, которые нужно учитывать при работе с ними.

Теперь поговорим о логических операциях, которые используются в запросах. Это операции типа bool, которые могут быть массивом или объектом с вложенным запросом. Например:
- must: {} и filter: {} выполняют роль логического "И". Разница в том, что must влияет на оценку соответствия документа запросу, а filter нет.
- should: {} представляет собой логическое "ИЛИ".
- must_not: {} — это логическое "НЕ".

В логических операциях можно комбинировать несколько поисковых запросов - например terms по нескольким полям

Пример запроса
Допустим, в elastic хранятся списки товаров, и в структуре документов есть четыре поля: Id, Name, Description и Tags.
Для поиска "телефон Samsung" может использоваться следующий запрос:


curl --location --request GET 'http://localhost:9200/my-index/_search' \
--header 'Content-Type: application/json' \
--data '{
"query": {
"bool": {
"should": [
{
"match": {
"Name": "телефон Samsung"
}
},
{
"match": {
"Description": "Телефон Samsung"
}
},
{
"terms": {
"Tags": ["samsung"]
}
}
]
}
}
}'


Flexible Coding
КОРОБКА

Машинный перевод в документации Microsoft иногда удивляет )

Вообще, как будто бы потихоньку русифицировать документацию - не такая и сложная задача, но она позволит избежать новых кринжей машинного перевода. Хотя зная объём доков от микрософт, вероятно, я не прав...

А вы натыкались на странности в документации?)
Please open Telegram to view this post
VIEW IN TELEGRAM
Мультитенантность

Сегодня поговорим про мультитенантность. Но сначала - посмотрим, а что вообще из себя представляют b2b-приложения?

У нас есть клиент - это какая-то организация. И она покупает ваше ПО для своих сотрудников. А чтобы использовать ПО - его нужно установить - и иногда серверную часть тоже. Таким образом появляются два варианта установки приложения:
1. Выделить новые ресурсы под конкретного клиента, закупить сервера или облачные managed ресурсы и установить там приложение
2. Написать приложение так, чтобы единый его экземпляр (с единой БД) поддерживал всех клиентов, и в условной админке завести нового клиента.

Второй вариант и является примером мультитенантной архитектуры. А тот контекст, с которым работает клиент называется тенантом. И такой подход имеет некоторые преимущества:
- При некоторых вариантах нагрузки это может быть дешевле - вы эффективно утилизируете все ресурсы
- Это проще с точки зрения инфраструктуры при создании нового клиента
- Клиентов очень просто отключать (в случае неуплаты подписки, например, или запрета на использование санкционных регионов :))))))) )

Пример известного мультитенантного приложения в России - Bitrix24 - можно купить подписку на SaaS решение и использовать его.

Мультитенантность требует хорошо продуманной архитектуры, и чем сильнее ваше решение подвержено кастомизации под конкретного клиента, тем сложнее это может быть сделать. Столкнулся с этим на собственном опыте - продукт, над которым я сейчас работаю - супер-гибкий и кастомизируемый как в плане кода (под конкретный тенант), так и в плане настроек - и иногда это требует некоторых чуть более сложных архитектурных костылей идей и решений.

Что учесть при разработке мультитенантной системы?
- Продумайте механизмы, которые не позволят увидеть пользователям одного тенанта данные их другого тенанта
- Продумайте админку, чтобы вашей системой можно было удобно управлять
- Следите за нагрузкой и инфраструктурой в целом - так как у вас единое приложение, есть вероятность упереться в стандартные ограничения по размерам БД например, размерам запросов и т.д. При этом сама мультитенантная архитектура открывает путь к большому количеству вариантов репликации или шардирования данных.

Вообще, довольно неплохо идея мультитенантности описана в статье "Мультитенантная архитектура для SaaS приложений от Microsoft"

Flexible Coding
Статические члены класса

Ещё в C#11 нам завезли обобщённую математику - набор интерфейсов, базовых классов и подходов, которые позволяют реализовывать собственные математические, "number-like" структуры данных. И в рамках реализации этой фичи были добавлены статические члены класса (ну чтобы интерфейс мог заставлять реализовывать операторы +, -, == и другие).

Например интерфейс IAdditionOperators, который добавляет в класс операции сложения:


public interface IAdditionOperators<TSelf, TOther, TResult>
{
static abstract TResult operator +(TSelf left, TOther right);
}


И я обнаружил, что static abstract members - это удобная штука и вне контекста математики. В некоторых случаях ими можно заменить атрибуты, и тем самым немного упростить себе (и коллегам) жизнь. Поясняю на примере. На работе занимаюсь no-code решением, в котором для некоторых супер-кастомных кейсов нам нужно реализовать несколько функций в коде, а затем выбрать в интерфейсе решения одну из них по названию. И название хотелось бы нормальное, что-то вроде "функция расчёта штатной позиции", а не DefaultOrgUnitPositionFunction :). А ещё разные функции должны быть доступны конкретным тенантам (см мультитенантность) системы.

Один из вариантов решения такой задачи - использовать интерфейсы/абстрактные классы и атрибуты. К интерфейсу мы приводим конкретную реализацию, найденную по имени чтобы вызвать, а в атрибуте указываем идентификатор тенанта, название и другие параметры:


interface ICustomFunction
{
Task<object> Execute(CustomFunctionContext context);
}

При реализации функции указать этот атрибут:


[CustomFunctionAttribute("Кастомная функция", "tenant-02")]
class ConcreteFunction : ICustomFunction
{
public Task<object> Execute(CustomFunctionContext context)
{
// some logic
}
}


В момент регистрации функции (информацию о них можно хранить в синглтон-классе например, тут всё очень сильно зависит от конкретной реализации системы):


class CustomFunctionHolder
{
private Dictionary<string, List<FunctionDescriptor>> _functions = new();

public void Add<T>() where T : ICustomFunction
{
var attribute = typeof(T).GetCustomAttribute<CustomFunctionAttribute>();

if (attribute is null)
{
// обработка отсутствия атрибута
}

var descriptor = new FunctionDescriptor(attribute.Name, typeof(T));
if (_functions.ContainsKey(attribute.TenantId))
{
_functions[attribute.TenantId].Add(descriptor);
}
else
{
_functions[attribute.TenantId] = new List<FunctionDescriptor> { descriptor };
}
}
}


При таком подходе мы сталкиваемся с двумя проблемами:
- У нас появляется лишняя сущность - атрибут, который нужно доставать с помощью рефлексии
- Мы не можем ЗАСТАВИТЬ разработчиков использовать этот атрибут. Всё что мы можем - выкидывать исключение в рантайме, если его нет, да оставить коммент в интерфейсе

И статические абстрактные члены решают эти проблемы! Вместо атрибута мы просто делаем два поля:


interface ICustomFunction
{
static abstract string Name { get; }
static abstract string TenantId { get; }
Task<object> Execute(CustomFunctionContext context);
}


И теперь компилятор обязывает нас их реализовать:


class ConcreteFunction : ICustomFunction
{
public static string Name { get; }
public static string TenantId { get; }
// ...
}

При регистрации типов можно использовать статические поля прямо с generic-параметром:


class CustomFunctionHolder
{
private Dictionary<string, List<FunctionDescriptor>> _functions = new();

public void Add<T>() where T : ICustomFunction
{
var tenant = T.TenantId;
var name = T.Name;
// ...
}
}


Flexible Coding
Облачные архитектуры

Прочитал тут очередную техническую книжку... И вам я её не рекомендую.

Это просто обзор облачных технологий на момент издания, то есть на 2021 год. Там буквально написано "Облака это хорошо, микросервисы это хорошо, амазон так делает и вы делайте тоже" плюс "вот что есть в амазоне, гугле и ms azure в плане облаков". Всё.

Ноль практики, какие-то минимальные базовые примеры и рассуждения о том, как же хороши облака.

Кому эта книга может подойти? Возможно, людям не с технической стороны, которые хотят получить некоторую насмотренность на тему того, а что вообще есть в облаках. И то проще будет зайти в популрные облака (Yandex Cloud, Selectel, sber cloud) и посмотреть что там есть.

А ещё есть опечатки в переводе, ну это классика )
Давно хотел провести эксперимент: написать такое приложение, чтобы и инфраструктурная часть была написана на .NET, и само приложение. И вот, наконец, дошли руки

https://vaiti.io/pishem-prilozhenie-na-c-steke/
6-го октября буду выступать с докладом про опыт наладки процессов в команде! Приходите, кто будет в Вологде!
https://ojti.ru/

А вот немного спойлеров к докладу)
Оверинжиниринг на Stack Overflow?

Недавно в рамках своей работы наткнулся на забавную штуку...

Итак, задача: у нас есть подсеть в формате CIDR (ip-адрес/маска) и массив ip-адресов. Нужно выбрать те адреса, которые принадлежат текущей подсети. (Подробнее про IP-адреса можно почитать тут)

Как мы знаем, подсеть - это диапазон ip-адресов, который рассчитывается определённым образом на основе маски, например:
- /24 - 254 хоста
- /25 - 126 хостов
- /26 - 62 хоста
- …

Ну и я конечно же просто пошёл гуглить - кто-то явно уже придумал решение. И ответы на разных источников предлагали написать свой алгоритм с побитовыми операциями и прочими рассчётами принадлежности адреса к подсети. Уверен, это отличный код, надёжный, как швейцарские часы...

ДА ЛАДНО, В ДОТНЕТЕ НЕТ ГОТОВОГО КЛАССА ДЛЯ ТАКИХ ШТУК?

Не верю.

Немного более глубокого исследования, и я обнаружил отличную структуру System.Net.IPNetwork, которая как раз описывает подсеть и содержит прекрасный метод Contains.

Итак, чтобы проверить принадлежность адреса к подсети, нужно всего лишь…
var network = IPNetwork.Parse("10.10.10.0/24");
network.Contains(IPAddress.Parse("10.10.10.2")); // True


И помните: Cамое лучшее решение - самое простое

Flexible Coding
Доклад о процессах на IT-форум.pptx
3.9 MB
Как и обещал, презентация с доклада

Спасибо всем кто пришёл, кто задавал вопросы, и отдельное спасибо Skillaz за то, что организовали и позвали! Было круто!
10-11 октября проходил Positive Security Day, где я был стендистом. Это новый для меня опыт - я почувствовал себя настоящим сейлом.

В задачи стендиста входило рассказывать заинтересованным людям (потенциальным заказчикам) про продукт - желательно так чтобы:
1. Люди поняли, что это и зачем
2. Люди захотели это купить

И сначала я был просто в ступоре... у нас не было заготовленных речей, надо было вывозить на импровизации. Но к счастью мои коллеги это дело подхватили и у них всё получилось! Тогда я послушал ребят, дообучил свою нейронку в голове, набрался смелости и присоединился к общению с людьми, которые интересовались продуктом. Было прикольно! Реально новый опыт, интересные вопросы, интересные люди из интересных компаний - всё это драйвило меня, и с каждым новым человеком я общался всё увереннее и увереннее.

Конечно же были и сложности, но по более сложным вопросам всегда можно попросить помощи у коллег - поэтому всё прошло хорошо.

А что такое мы разработали???

А это настоящий автопентестер! Я пришёл в команду чуть больше месяца назад и принял участие в разработке "мозгов" программы - непосредственной реализации техник и тактик, которые используют хакеры в рамках своих атак. За этот месяц я очень сильно погрузился в продукт, в ИБ и пентест - ведь чтобы его автоматизировать, надо понимать как это всё делается вручную. Супер интересная область, супер сложная задача эмулировать действия хакера - фактически нелинейное мышление человека перекладывать в программный код.

Но к счастью, у нас драйвовая команда, готовая принять этот вызов!

Кстати, запись мероприятия доступна тут (Зал Молекула, есть таймкоды, PT Dephaze на 05:23:00): https://psd.ptsecurity.com/#live

Flexible Coding
Итак, коллеги, важный опрос - я в смятении.

Чем отличается Acceptance Test Driven Development от Behaviour Driven Development c точки зрения технической реализации (написания тестов на основе бизнес-требований)?

Судя по тому, что читал в интернете - на практике это буквально одно и то же под разными названиями. Конечно есть уверждения, что одно это процесс взаимодействия с заказчиком, а другое это написание тестов... А с технической точки зрения это те же тесты на Gherkin Language... или нет?

Короче, го в комменты!
Akka .NET

По производственной необходимости я сейчас изучаю фреймворк Akka. Это реализация акторной модели - архитектура приложения строится на некоторых единицах исполнения заданий, которые общаются друг с другом посредством сообщений. Реактивная модель, получается (кстати, можете почитать про неё в Reactive Manifesto).

Сами акторы появились в Erlang и часто используются в телекоммуникационной индустрии. И так получилось, что они отлично ложатся на задачи в рамках моей текущей предметной области.

Как изучать?

По материалам именно по дотнету - всё плохо, поэтому тут я советую почитать оригинальные доки с примерами на Scala и Java.

(Но если что - вот документация и по Akka.NET)

Послушать можно доклады от автора порта акки на .NET - https://www.youtube.com/watch?v=MY1iPY78_fs. Ну или поискать другие.

Но самое главное
Я наткнулся на отличный курс от Petabridge - Akka Bootcamp. Он доступен на гитхабе - https://github.com/petabridge/akka-bootcamp. Тут гораздо более понятные и реальные примеры использования акторной модели - и есть практика, которую можно выполнять, а затем сравнивать свой результат с эталоном. Русификации нет, но я всё равно рекомендую изучать акку именно по этому курсу.

Flexible Coding
Ради эксперимента поставил заметочник affine.pro. Однако у него довольно плохая мобильная версия, поэтому для быстрых записей я захотел написать телеграм-бота. А уже об этом - моя новая статья :)
Behaviour Driven Development && Dotnet

Так получилось, что в рамках своего опыта работы я не встречал проектов с хорошими юнит-тестами и большим покрытием ими проектов.

Зато на всех проектах использовался подход Behaviour Driven Development, в рамках которого писались интеграционные тесты.

Behaviour Driven Development - это подход к разработке, при котором вы пишете интеграционные тесты на основе User Story, используя язык Gherkin как со стороны бизнеса, так и со стороны разработки. Чем-то похоже на ATDD

Каждый тест состоит из трех этапов:

- Given (дано) - подготовка окружения (может быть несколько)
- When (когда) - выполнение действия, которое как-то это окружение меняет (желательно, чтобы этот шаг был только один)
- Then (тогда) - проверка того, что окружение изменилось так как надо (тоже может быть несколько)

Например
- Дано: созданный аккаунт пользователя с ролью администратор
- Когда пользователь авторизуется в системе
- Тогда пользователь видит страницу администратора

Такие тесты более сложны в сопровождении, дольше пишутся и дольше прогоняются, однако имеют очень важное преимущество - они не просто тестируют изолированный кусок логики, как юнит-тесты, а фактически подтверждают выполнение бизнес-требований.

В дотнете есть много популярных фреймворков для написания таких тестов - LightBDD, SpecFlow, MSpec. Однако наиболее популярны первые два - о них и поговорим в следующих постах

Flexible Coding
SLNX-формат или как Microsoft меня расстроили

Всем привет! Сегодня поговорим про "решения" проектов на .NET.

Как мы знаем, приложение на дотнете - это некоторое решение (Solution) с набором проектов. В одном из проектов может быть слой бизнес-логики, в другом - инфраструктурный, ну и так далее. Всё это может быть и в одном проекте, но это редкость.

Так вот, проекты объединяются в решение через sln-файлы - файлы решений. Внутри этого файла находится страшный сплошной текст с информацией о проектах, их расположением в Solution Folders и прочими метаданными. И его довольно трудно читать - там довольно странные GUID-ы, куча каких-то маппингов, повторов...

А проблемы начинаются, когда разработчики параллельно в разных ветках добавляют много проектов, а потом надо решать merge-конфликты. И после очередного разбора конфликтов на работе, итогом которого стала отвязка части проектов и ручная их перепривязка к решению, стало понятно, что так дальше продолжаться не может.

В .NET8 был представлен новый формат файлов решений - slnx. Данные в нём уже структурированы и довольно удобно читаются - xml-формат, там нет GUID-ов и прочего мусора, который мешает пониманию содержимого файла - то, что надо!

И вот мы уже перешли на новый формат решений, счастливы - конфликты в этом файле решаются просто, коллизий нет... и тут выясняется, что dotnet cli этот формат полностью не поддерживает. То есть dotnet restore, dotnet build в нашем dockerfile тупо не отрабатывает и мы не можем задеплоиться!

Собственно, благодаря тому, что xml удобно парсить, не составило труда написать скрипт, который парсит список проектов в решении и выполняет dotnet restore для них по отдельности, но это не так удобно. Вместо dotnet restore мы выполнеяем команду

dotnet fsi list-projects.fsx | sed 's|\\|/|g' | xargs -I {} sh -c 'if [ -f "{}" ]; then dotnet restore "{}"; fi'

> Да, я написал F#-скрипт для парсинга slnx-файла

А ещё забавно, что даже последняя версия Visual Studio 2022 отказалась открывать этот файл :)

Какой же статус работы по slnx?
- Для dotnet cli - выйдет в .net9.0.2
- Для vs code - есть открытый issue
- Для Visual Studio - да вроде уже работает, но у меня не работает :(
- И только JetBrains Rider стабильно открывает и понимает новый формат солюшнов

Морали не будет, просто будьте внимательнее при переходе на что-то новое и крутое :)

Flexible Coding
Итак, 2025 год (с наступившим!), автор вышел из цепочки событий разной степени безумия и готов с вами познакомиться!

Меня зовут Дмитрий Бахтенков, уже несколько лет я занимаюсь коммерческой разработкой на .NET, а также увлекаюсь различными аспектами саморазвития - очень много времени уделяю тому, как быть продуктивным, организованным, эффективным и т.д.

Этот канал я создал, чтобы делиться с вами своими мыслями, идеями и личным опытом, непосредственно связанным с программированием и IT в целом.

Что здесь есть?
- Статьи. Последнее время я публикую их на портале вАЙТИ
- Обзоры книг. Я много читаю, в том числе техническую литературу. Об интересных книгах я пишу отзывы, и думаю о том, чтобы делать более развёрнутые конспекты
- Интересные технические кейсы. Что-то непонятное, занятное и странное
- Обучающие посты
- Личный опыт, публичные выступления, советы по организации и многое другое

А ещё я завёл бусти. Он пока пустой, но в ближайшее время я буду его наполнять по чуть чуть. Подписывайтесь, если хотите меня поддержать, я буду рад!