← Вернуться к списку тем

Как составить структуру хранения пользовательских фильтров

Всем привет. Пишу для себя конструктор ботов и встала задача сохранять условие/фильтр в базу.

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

Проблема в том, что этих фильтров может быть много. Разных с условием ИЛИ и И

Как всё это сохранять в базу данных и потом красиво делать проверку логики?

Пробовал сохранять в json, но кажется что есть более лучший вариант.

Авторизуйтесь через Telegram, чтобы оставить комментарий.
Откройте по ссылке или QR бот @iMakeBot, нажмите кнопку Старт/Start.
Следуйте инструкциям бота.

  • Денис 💡 Фрилансер [1 год назад]

    Ещё хочу уточнить, что мне надо сделать имено сохранение фильтров. Чтобы сделать создание любого по логике бота для своих целей. Нужно для того, чтобы не писать код ботов постоянно, а сделать, собрать быстро, как это делают многие конструкторы ботов.

    Плизз, выручайте 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Можно и в json хранить, но только вспомогательные данные. Типа какое условие и в каком месте применяется.

    Для самих условий думаю стоит создать класс, где будет логика для каждого типа.

    В бд создать таблицу где записывать условия: название, тип, параметры для сравнения, тип сравнения >,<,==,!= …

    Далее эти условия добавлять к месту, типа в этом шаге учитывать такие то условия, и сделать конструктор комбинирования условий или/и возможно многоуровнего использования 
    Связку условий хранить можно и в json, но лучше просто в бд в строковом формате с привязкой к месту применения

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Можно пример? Про привязку? Не совсем понял.

    У меня реализовано так сейчас, есть поле type=text

    В него пишу выборку-фильтр

    Масив, ключ AND - тут масив фильтра И

    И ключ OR - масив ИЛИ.

    Но решение очень кривое.

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

    Вот как работают конструкторы ботов? Там и логику сохраняют и переменные и параметры. Неужели раздувают таблицы под хранения?

    Сам код обработки я без проблем реализую, а вот с хранением фильтров не могу найти какое-то нормальное решение.

    Буду рад, если хоть простые примерчики, так сказать, натолкнуть на мысль 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Таблица связи это многое ко многим

    То есть, есть множество условий и множество мест, каждое условие может быть использовано во многих местах.

    Чтобы их связать делаем дополнительную таблицу и в ней пишем связи между местом использования и условием, можно еще учесть  какие-либо дополнения для этой связи, но необязательно.


    Вам бы уйти от ручного указания условий в type=text, так как они могут быть переиспользованы в проекте лучше создать конструктор этих условий, и в нужном месте выбирать их через например select.

    В конструкторе условий учесть тип условия, тип сравнения, и данные для сравнения. 

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Многие ко многим - это понятно.

    Например, теги на сайте, делал такое.

    Но вот с фильтрами не понимаю как реализовать. То что json надо убрать, понимаю, кривое решение 

    Конструктор условий? Что из себя представляет? Одна таблица хранит что? Название фильтра и принадлежность у боту? К шагу?

    Вторая, как понимаю типы фильтра, сравнение дат, времени, текста и ещё что-то.

    А третья?

    Либо уже мозги не работают, либо что-то не допонимаю я (((

    Не пойму никак, каким образом хранить фильтры, делать связку и чтобы потом без кучу циклов проходить фильтры

    Может пример есть какой? Тезисно хотя бы для понимания 

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Или дополнительно сделать таблицу по группе фильтров?

    Например, есть таблица фильтров

    id type_filter type_param group_id

    И тогда в другой таблице

    id type писать значение or and?

    Группировки? 

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Тоесть, база содержит так

    id bot_id filer_type filter_param

    Да записать можно

    233 = cyrent_data

    С одним фильтром понятно, а как строить логику из ИЛИ и И?

    Если допустим, мне надо сравнить и дату и переменную и параметр.

    Или через ИЛИ, должна быть переменная ИЛИ такая та дата?! Условия могут быть разные. Вся проблема встала по правильной сборке имено фильтров И и ИЛИ.

    Отдельно фильтры понятно как, а совокупность? 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Совокупность - группа условий в том числе многоуровневая по типу:

    if(true || (true && true && (true || true))) {
        // ...
    }

    В этом случае нужно использовать логику рекурсивного вызова

    Например у вас есть стартовый экран, в котором вы хотите проверить условия, в которых текущая дата больше заданной на 2 дня и (id пользователя равно заданному или в его имени Jack есть буква H)

    Классу фильтрации нужно сгенерировать исходя из наличия в бд примерно вот такой массив фильтров-условий

    // массив проверок
    $filters = [
      [
        "type" => "AND",
        "params" => [],
        "children" => [
            [
                "type" => null,
                "params" => [">=", "2023-06-17", "2023-06-15"],
                "children" => [
                    [
                        "type" => "OR" 
                        "params" => [],
                        "children" => [
                           [
                               "type" => null,
                               "params" => ["==", 12345678, 87654321],
                               "children" => []
                           ],        
                           [
                               "type" => null,
                               "params" => ["include", "Jack", "H"],
                               "children" => []
                           ]        
                    ],
                ]
            ]
       ];

    Далее передать этот массив в рекурсивный метод некого класса проверки условий

    В этом классе необходимы методы типов проверок для простых условий типа >=, == ... и также для кастомных по типу include (проверяет вхождение подстроки)

    Хранение условий нужно организовать так чтобы можно было также в рекурсивном методе генерировать необходимый массив (как на примере выше)

    Делал без проверки на "скорую руку" ))) скорее всего нужно будет где-то логику переделать - но уже оттолкнуться думаю можно

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Ну тут можно хранить получается тот же json полностью фильтр.

    Или поучить из базы фильтры, собрать масив. А дальше уже на обработку. И как я понял, масив нужно составить сразу с данными, а потом рекурсивно проходить проверку?! Но уже есть зарождение мысли, спасибо, хоть не до конца всё уловил. С базы данных получается мы получаем все фильтры. Потом формируем масив. А дальше строим рекурсивно условие

    if(true || (true && true && (true || true))) {
        // ...
    }

    Так получается? 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    JSON вам придется хранить с данными на момент его генерации - но в момент вызова данные могут отличаться.

    Поэтому лучше генерить в момент вызова

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    А как в базе тогда сохранять без данных? Типы? Тесть, есть веб форма добавления условий

    Допустим, поле дата сравнить с текущей

    Далее понадобилось условие И

    Добавляем поле ещё условия.

    Тогда в базу как записать? Это будет уже два условия?

    Первое допустим

    id type children

    Где children равно второму полю? 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Типы условий можно хранить в самом классе например в статичном свойстве в массиве

    Конструктор конечно лучше сделать на html + js, при отправке в обработку направлять массив полей, а хранить все в одной таблице с привязкой вверх к родителю (parent), не вниз к children так как children может не быть, а вот у child точно будет parent

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    А дальше строим рекурсивно условие

    Да, строим, но собирать его необязательно для чтобы потом где-то запускать проверку условий - метод который его строит уже сразу может его обрабатывать и в случае если на каком-то этапе крашиться проверка - то можно выходит и вернуть false

    То есть метод который его строит возвращает булевое значение

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Никак не пойму общую структуру всей логики от структуры хранения фильтров в базе, выборки и постройки. Вроде смотрю на пример, кажется понятно. Но не могу понять как собрать всё воедино. На каком этапе делать запрос. Сразу получать фильтры или по одному и строить масив фильтра?

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Запрос нужно делать там где он привязывается. Вы создаете экран бота - например стартовую страницу к ней создаете условия - по выполнению условий вы что-то делаете дальше - например выводите текст отличный от результата проверки условий

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Мои мозги окончательно сгорели 😢

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Фильтры получаете сразу конечно - зачем их вызывать по одному - вы передете команду на проверку фильтров, если фильтры есть то погнали доставать их из бд, достали сформировали массив для проверки - проверили выдали результат

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Я вот никак не могу понять. Да, фильтры я достану с базы, дерево построить смогу через parent.

    Но никак не могу понять каким образом мне фиксировать фильтр ИЛИ & И

    Ведь запрос проверки может быть таким дата == 21.09.2023 OR (param1==2 AND param2==3)

    Или мне каждую часть фильтра проверять отдельно? Или весь собранный фильтр сразу проверять?

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Обратите внимание на массив, в нем есть место для этим параметров OR, AND или могут быть другие

    "type" => "AND",
        "params" => [],
        "children" => [ ...

    Это нужно учесть в конструкторе при создании

    так как структура элемента массива одинаковая, то храним его также в той же таблице но поля заполнены по другому

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Так, чтобы всё это понять надо идти от базы.

    Каждый фильтр мы сохраняем в таблицу, и по примеру таблица такая

    id - parent - type - children

    Запрос к базе получаем масив этих фильтров

    Теперь цикл (образно)

    Допустим все данные в масиве filters[] 

    Foreach (filters as item_filter ) {И вот тут как тогда строить логику опираясь на OR или AND }

    Мне бы понять на простых фильтрах OR, and, ==, !===

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    children зачем? в нем вы что планируете хранить?

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Так какая тогда структура таблицы?

    Как в таблице записаны должны быть фильтры?

    Допустим, мне надо сделать фильтр

    Пользователь имеет метку "клиент"

    И не имеет метку "про"

    По сути это два фильтра, ну как я понимаю. Как это должно храниться в базе? Фуххх, не доходит что-то до меня 🤔😢🙄

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Что-то типа этого

    Таблица связей: id, screen_id, type, parent, filter_id, params

    Таблица фильтров: id, name, type

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Ну это если screen известен. А если нужно проверить по несколько фильтрам пользователя? Например, сделал запрос пользователь боту и не важно на каком он шаге, надо проверить допустим кучу условий, есть там метки, какие параметры.

    Экран хорошо, когда предыдущий шаг записан. И связная таблица поле param там масив параметров одного экрана (шага) получается?

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Это не важно к чему привязан набор фильтров. Можете назвать не screen_id а moment.

    Все это будет зависеть от того как у вас логика настроена. В любом случае фильтры к пустому месту создавать не будете - смысл от них нулевой будет.

    Например, сделал запрос пользователь боту и не важно на каком он шаге

    Это у вас например набор фильтров который привязан к любому шагу пользователя, поэтому можно в логике бота записать moment = alltime 

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Ааа, понял. Фильтры конкретно привязаны к шагу и общие фильтры. Супер. Это уже хорошо.

    Вроде с базой данных и хранением немного вроде разобрался.

    Теперь по сборке фильтра. Абстрактный класс описывает общие свойства и методы.

    Далее, потом каждый потомок (действие, сравнение, поиск, и так далее) собирает свой масив и возвращает. И общий метод абстрактного класса собирает весь масив фильтра? Так?

    Вроде немного понятно. А как саму проверку реализовать этого собранного массива фильтров? Циклом пробежать по масиву? Или сразу на моменте создания массива делать проверку? Тоже этот момент не пойму.

    Хотя, весь фильтр то проверять не надо. Если есть конструкция And и первое условие не подошло, тогда наверное можно сразу выходить из метода 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Класс необязательно абстрактный, подход абстрактный )) от общего к частному

    Вызываете метод в который передаете массив условий, который сразу же проверяете на выходе возвращаете bool

    Если в момент проверки массива натыкаетесь на false - то смысла дальше проверять нет - останавливаете проверку и возвращаете false

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Класс. 👍 Тоесть, мы частями передаём масив, тоесть фильтры и получаем bool. А если на самом деле вернулось false, то режим всю дальнейшую проверку. 👍

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    передаем весь массив, в режиме рекурсии проходим по нему - и вернем результат

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Да да, и если на каком то шаге рекурсии есть false, то просто остановить выполнение. Смысла нету. Особенно при условии AND 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Даже лучше так: если в момент проверки понимаете что смысла дальше нет проверять так как условия все равно по итогу вернут false - останавливаете и возвращаете false

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

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

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Получаете все фильтры экрана с order by id asc где parent = 0

    Далее проходите по ним циклом, если type не равен null тогда это то очень вы пишите - строка которая говорит что она является оператором объядинения условий - значит идем в бд и получаем ее children по условию где parent = ее id и сортировка order by id asc

  • Денис 💡 Фрилансер [1 год назад]

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

    Обобщенно мне понятно вроде суть, но на практике не могу применить.

    Захотел сделать себе тоже конструктор ботов, ну пусть не сложный, но хотя бы примитивные вещи, чтобы можно было указать текст команды, сравнить её, если пользователь добавился и есть метка то сделать действия. Ух. Мозги пухнут... Чего только не пробовал, медленно всё работает. Если каждый фильтр сохранять отдельной записью, то потом все фильтры перебирать в цикле? Я думал, что можно сразу получить все фильтры, составить запрос... Или я наивно не в ту сторону шагаю? 

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Лучше вам идти в сторону абстракции. Вы же сейчас хотите плодить конкретику.

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Хорошо. Абстрактный класс. Что изначально он делает? Базовый фильтр? И потомки создают конкретно свой тип фильтра, сравнение дат, сравнение времени, сравнение ещё и ещё что-то.

    Стукните мне по голове - не понимаю пока, как реализовать 🙄

  • Денис 💡 Фрилансер [1 год назад → iMakeBots]

    Или хотя бы пример абстракции, может так дойдёт до меня 🙄

  • iMakeBots [1 год назад → Денис 💡 Фрилансер]

    Я про универсальный тип фильтра, типа без привязки к данным. Вы создаете фильтр в который потом можно будет скормить любые данные и он отработает одинаково правильно.

    Для начала нужно определить свойства фильтра-условия, у него есть тип сравнения (простые: ==, !=; "сложные": is_null, isset) и параметры сравнения - что с чем сравнивать или что проверять в случае "сложного"

    Для этого создаем Класс в котором будут описаны типы сравнения и методы обработки у которых на выходе будет булевое значение

    При создании фильтра: выбираем его тип, указываем для него название чтобы в нужном месте его можно было идентифицировать и выбрать из списка.

    Далее в нужном месте в конструкторе выбрать нужный фильтр, указать к нему параметры обработки, также можно универсально предопределить методы подстановки параметров по типу текущая дата минус столько дней, или имя пользователя или id пользователя - это также можно определить в Классе - при этом просто указать методы определения данных

    Записать это в бд - и при вызове проверки генерировать нужный массив данных как указанно в примере 

  • Денис 💡 Фрилансер [1 год назад]

    Ссори, за флуд.

    Просто одно дело многие ко многим выбирать допустим теги для сайта, а другое дело хранение логики, которое может содержать условие И ИЛИ

    Равно, меньше, или не равно, или равно именьше, или не равном больше.... Фильтров же много...