• Как осуществить поиск по таблице в Postgres с разным регистром?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Есть несколько вариантов, например:
    1. В запросе сделать условие WHERE full_name ilike %иван%. Ключевое здесь - "умный" оператор ilike, указывающий искать без учета регистра.
    2. В запросе сделать условие WHERE lower(full_name) like lower(%иван%) - примерно то же самое, но не так красиво.
    3. Использовать расширение CITEXT (https://www.postgresql.org/docs/9.4/citext.html), удобно если нужно учитывать например контроль уникальности в ключе. В столбцах CITEXT выражение 'foo@BAR.com' == 'FOO@bar.com'.
    Ответ написан
    Комментировать
  • Как составить наиболее оптимальный запрос с поиском по одинаковому полю в разных таблицах PostgreSQL с сортировкой по нему в конечном результате?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Рекомендую приглядеться к materialized view, как уже советовали ранее.
    Особенность в том, что по полю для поиска name в materialized view вы сможете посторить gist/gin index и пользоваться быстрым поиском по %like% (см. https://postgrespro.ru/docs/postgrespro/9.5/pgtrgm...

    Обратите внимание, что materialized view необходимо обновлять вручную, периодически выполняя команду
    REFRESH MATERIALIZED VIEW mymatview;
    Ответ написан
    Комментировать
  • Как убедить клиента что пора переделать проект?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Давайте по порядку.
    1. Про «делать проще» или «закладывать архитектуру». Вы пошли по первому пути вполне закономерно, потому как зачем клиенту платить больше для решения своей задачи, если можно заплатить меньше? Для него это противоречит здравому смыслу. Поделюсь опытом, мы например работаем так всегда, но с небольшой (на самом деле ключевой) поправкой — мы планируем, как проект будет развиваться дальше и обязательно вместе с клиентом. Когда есть этот план работа строится итеративно, сначала выкатывается минимальная работоспособная версия (mvp), которая покрывает критический контур системы (то, без чего этот продукт точно не будет работать), затем v0, v1 и так далее. Мысль в том, что это нормальная практика. На счет «закладывать архитектуру», а откуда вы знаете какая она должна быть? Хватит пальцев одной руки чтобы посчитать сколько я видел клиентов, которые четко знают, что им нужно и в конце проекта что-нибудь не менялось. Зная это, как можно просчитать архитектуру или хотя бы даже желаемый функционал? Лучше идти итеративно и постепенно достраивать систему.

    2. Теперь про постоянные фичи, костыли и прочее. Тут упираемся в бизнес-аналитику и сбор требований. Нельзя работать в режиме Клиент сказал сделать фичу — Сделали фичу. Да ещё и по нескольку раз в день. Важно моделировать бизнес-процессы и встраивать их в существующую систему, вы-то, видно, это понимаете, но клиент-то — нет. Чтобы он понял это, с ним нужно общаться на его языке и строить работу от бизнеса. Задайте на брифинге вопросы «Какую цель вы преследуете, внедряя эту фичу? Как ещё можно достигнуть этой цели, может есть решение, которое вообще не потребует внедрения никаких фич, может это можно сделать бесплатно? Например у нас есть бизнес-аналитик, который, занимается только этим — задаёт нужные вопросы, объясняет последствия тех или иных решений, затем сам формулирует задачи, переведя их с языка бизнеса на язык разработки и начинается работа.

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

    4. Про, собственно, сам вопрос, как быть и как попробовать объяснить, что накопился огромный техдолг и его нужно рефакторить. Возвращаемся к тому же вопросу, с бизнесом нужно говорить на их языке. Сделайте презентацию, позовите клиента на вечерний чай (или чего покрепче) и покажите как сейчас и как могло бы быть. Покажите, что костыли и плохая архитектура замедляют разработку новых фич, что они УЖЕ переплатили вот *столько-то* и дальше будут переплачивать *вот столько-то* каждый месяц (ну хотябы приблизительно посчитайте). Покажите, что качество продукта неуклонно снижается, что в итоге всё работает медленно и из за этого они теряют клиентов вот в таких-то местах. Объясните, что да, сейчас всё работает и держится на ваших офигенно качественных костылях, но может произойти реальный крах вот *в такой-то момент*. Покажите, где для них наоборот есть точки роста, если сделать как вы говорите.

    Клиент должен быть разрабу друг и партнёр, а не мудак :)

    Надеюсь помог и вы что-то извлечёте из этого текста для себя, вообще чтобы более предметно говорить про это нужно больше конкретики :)

    P.S. Картинки — ТОП орал долго, спасибо)

    Павел Паленин
    Head of design in Athanor
    Ответ написан
    1 комментарий
  • Как передать компонент в качестве пропса?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Информация от одно компонента к другому в реакте можете предеаваться через пропсы, как и требуют сделать в задании.
    Вот так это будет работать — Демо
    Ответ написан
    Комментировать
  • "неудача канала CORS preflight" - как решить?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    preflight-запросы делаются с другого хоста и методом является OPTIONS.

    В целом, у вас все написано правильно и проверка будет зависеть от того, где и как вы хостите сервис. В целом, шаги следующие:
    1. Если вы пишете эти директивы в .htaccess, то убедитесь, что у вас выставлен AllowOverride All в конфигах /etc/apache2 и .htaccess подхватывается.
    2. Убедитесь, что у вас включен mod_headers, выполнив команду a2enmod headers.
    3. Воспользуйтесь, например, https://www.test-cors.org/ для проверки того появился ли доступ.

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

    Удачи в решении вопроса )

    С уважением,
    Иван Томилов
    CEO of Athanor
    Ответ написан
    2 комментария
  • Как получить значение после выполнения асинхронной функции?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Что-то странное творится либо в нейминге, либо в логике. Зачем вам там рекурсия? Почему item.name имеет тип Number?

    Так или иначе, лучше вам будет не мутировать в асинхронных функциях какое-либо глобальное свойство. Объявляйте массив прямо там и там же работайте с ним, если его нужно прокинуть внутрь очередного вызова функции, передавайте его как аргумент.
    А затем сделайте так, чтоб функция doMassiv вернула этот массив.
    Далее уже в showMassiv принимайте этот массив и сохраняйте глобально, раз это вам нужно.

    А еще хочу заметить, что вы забыли добавить await перед рекурсивным вызовом this.doMassiv(item.name), вероятно поэтому происходит вот это:
    public async showMassiv() {
        await doMassiv(400)
        console.log(this.massiv.length)  
       //здесь в консоль выводится длинна массива после первого прохода doMassiv
    }


    UPDATE
    Как справедливо заметил Алексей в комментах, await и не будет работать внутри forEach, вот по этим причинам. Следовательно, стоит заменить его на обычный цикл for, или написать asyncForEach, как в статье
    Ответ написан
    2 комментария
  • Почему в photoshop один из шрифтов кривой?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Ps — растровый редактор, а шрифты отрисованы в векторных, поэтому чтобы отобразить текст Ps его «конвертирует» и делает он это по определённым правилам, которые настраиваются вот здесь:
    5e314fec423ca738925983.jpeg

    Попробуйте понастраивать этот параметр — рендеринг будет меняться.
    Ответ написан
    3 комментария
  • Как сделать отступ при скроле страницы?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Самый простой способ: сделать скролл в 2 шага и после scrollIntoView вызывать доскрол на нужное кол-во пикселей, типа такого
    window.scrollBy({
      top: -10,
      left: 0,
      behavior: 'smooth'
    }); //

    Но если хотите плавности, вам придется, вероятно, отказаться от scrollIntoView, получать координаты верхушки вашего anchor, менять их на нужное кол-во пикселей и вызывать window.scrollTo, передавая туда координаты в параметр top

    Добавил демо
    Ответ написан
    1 комментарий
  • Как компилировать ES6 налету?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    У babel на оффсайте есть онлайн транспилер, если открыть консоль, видно, что никакие запросы при транспиляции на сервер не улетают. Далее идем в гитхаб бейбеля и находим там исходники их сайта, ищем там функцию, которая вызывается для транспиляции. Находим и радуемся)
    Ответ написан
    7 комментариев
  • Как подвесить событие vue?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Все таки подозреваю, что вы неправильно подписывались на событие в родителе. Я вынес ваш компонент в песочницу, подписался в родителе на onplus и onminus и все работает. Демо
    Ответ написан
  • Есть ли API для Byte.co?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Открытого API нет, а создание ботов, которые что-то будут делать на сайте запрещено его правилами.
    Ответ написан
    Комментировать
  • Как ускорить процесс исправления перспективы (искажения) на фотографии?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    В Ps есть специальный инструмент Adaptive Wide Angle, он помогает убрать искажение от широкоугольного объектива, как в вашем случае:

    https://www.youtube.com/watch?v=Fk5nTDU1aVw
    Ответ написан
  • Нужен ли Redux, когда есть React Hooks?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Комментировать
  • Как сократить условие цикла?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    for(; i<5; i++) {...}
    будет работать, если у вас где-то выше по коду i объявлена и при этом имеет тип Number.

    Вот так, например:
    var i = 0;
    for(; i<5; i++) {console.log(i)}
    Ответ написан
    2 комментария
  • Какое ваше мнение о Expo для быстрой разработки React Native приложений?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Делали несколько проектов с помощью Expo. Начали первый 1.5 года назад. Тогда технология казалась совсем сырой. Но сейчас могу сказать, что особых проблем не возникает при разработке классических вещей. Мы используем Expo в продакшене, все работает шустро и мы живем счастливо.

    В целом, у нас была задача сделать кроссплатформенное приложение, дизайн под обе платформы был одинаковый, поэтому Expo отлично нам подошел.
    Ответ написан
  • Как в PostgreSQL сделать уникальный идентификатор среди всех таблиц базы (глобально)?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Здравствуйте. То, о чем вы говорите, называется GUID, но какого-то унифицированного одного алгоритма его генерации для всех нет. Но в postgres есть расширения, которые могут вам помочь и вам не придется генерить его на уровне приложения.
    Эта суперкороткая статья может быть вам полезна: https://postgrespro.ru/docs/postgrespro/9.5/dataty... и дать начальный стимул и понимание куда копать дальше. Удачи )

    Update (29.01.20):

    Сразу к делу и примерам. Сделать это можно, например, так:

    -- Начнем со схемы данных. На всякий случай проиллюстрирую как может выглядеть идея генерации guid базой, с которой
    -- я в самом начале и начал. Но вам, наверное, не будет смысла эту функцию использовать, т.к. сценарий использования
    -- у вас другой.
    --
    -- Обратите также внимание, что я поменял тип на BIGINT. У меня была гипотеза, что вы используете TEXT, т.к., возможно,
    -- не знали про BIGINT. Недостаток TEXT в том, что Postgres не построит по нему индекс и выборки по id будут все
    -- медленнее и медленнее. Я бы порекомендовал сразу отрефакторить все так, чтобы использовался именно BIGINT.
    --
    -- Все поля также назвал английскими словами, т.к. это можно считать индустриальным стандартом. NOT NULL добавил
    -- по наитию: логика подсказывает, что PRIMARY KEY у каждой таблице обязателен, как и данные. Просто для полноты.
    
    -- Начнем с функции, которая будет генерировать нам ID для примера.
    
    CREATE OR REPLACE FUNCTION guid()
        RETURNS BIGINT AS
    $BODY$
    BEGIN
        -- Количество секунд с начала эпохи Линукса и домножаем на какой-то множитель, чтобы увеличить точность
        -- и получить 1580307917143.431 вместо 1580307917.143431
        RETURN CAST(EXTRACT(EPOCH FROM NOW()) * 1000 AS BIGINT);
    END;
    $BODY$
        LANGUAGE 'plpgsql' VOLATILE;
    
    -- Теперь перейдем к самой схеме данных и создадим ее.
    
    CREATE TABLE users (
       id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
       pseudonym TEXT NOT NULL
    );
    
    CREATE TABLE posts (
       id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
       content TEXT NOT NULL
    );
    
    CREATE TABLE comments (
      id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
      text TEXT NOT NULL
    );
    
    -- Для того чтобы повесить CONSTRAINT на id целевых таблиц, понадобится сделать VIEW, котоый будет содержать все
    -- id из всех этих таблиц, а также функцию, которую мы сможем использовать для CONSTRAINT. По сути, делаем то же,
    -- что делали бы на уровне приложения, но на уровне БД.
    
    CREATE OR REPLACE VIEW all_ids AS
    SELECT id FROM users UNION
    SELECT id FROM posts UNION
    SELECT id FROM comments;
    
    -- Теперь перейдем к функции, которая и будет выполнять всю грязную работу.
    
    CREATE OR REPLACE FUNCTION is_unique_id (BIGINT)
        RETURNS BOOLEAN AS 'SELECT CASE WHEN
                                       (SELECT 1
                                        FROM all_ids
                                        WHERE  id = $1) > 0
                            THEN FALSE ELSE TRUE END'
        LANGUAGE 'sql' WITH  (isstrict);
    
    -- Осталось только повесить CONSTRAINT
    
    ALTER TABLE users ADD CONSTRAINT id CHECK (is_unique_id(id));
    ALTER TABLE posts ADD CONSTRAINT id CHECK (is_unique_id(id));
    ALTER TABLE comments ADD CONSTRAINT id CHECK (is_unique_id(id));
    
    -- А теперь внимание. Теперь вам придется быть очень внимательным при добавлении таблиц, в рамках которых id должен
    -- быть уникален. При добавлении новой таблцы будет необходимо:
    --   1. Пересоздать VIEW, дополнив запрос новыми таблицами.
    --   2. Не забыть повесить аналогичный CONSTRAINT на новую таблицу.
    --
    -- Также обратите внимание, что при вставке новой записи в любую из таблиц будет проверяться весь созданный VIEW
    -- и очень важно чтобы это был не полнотекстовый поиск, а работали индексы, поэтому так важно отрефакторить все в BIGINT.
    
    -- Пришло время тестирования. Вставляем данные.
    
    INSERT INTO users (pseudonym) VALUES ('Первый');
    INSERT INTO users (pseudonym) VALUES ('Второй');
    
    INSERT INTO posts (content) VALUES ('О том как надо');
    INSERT INTO posts (content) VALUES ('О том как не надо');
    
    INSERT INTO comments (text) VALUES ('Я думаю что решение...');
    INSERT INTO comments (text) VALUES ('Хорошо я пропробую сделать...');
    
    -- И глянем что получилось.
    
    SELECT * FROM users;
    -- 1580326610797	Первый
    -- 1580326611809	Второй
    
    SELECT * FROM posts;
    -- 1580326613690	О том как надо
    -- 1580326613712	О том как не надо
    
    SELECT * FROM comments;
    -- 1580326613779	Я думаю что решение...
    -- 1580326613797	Хорошо я пропробую сделать...
    
    -- Время X: тестируем нашу проверку, пытаясь вставить в таблицу users id из таблицы comments:
    
    INSERT INTO users (id, pseudonym) VALUES (1580326613779, 'tiabc');
    -- [23514] ERROR: new row for relation "users" violates check constraint "id" Detail: Failing row contains (1580326613779, tiabc)
    
    -- Profit!


    Что хочу сказать с точки зрения проектирования архитектуры и вообще. Использование любого нового инструмента должно быть обосновано. Как правило, сложная схема БД ведет к сложностям в поддержке и к тому, что какие-то вещи забывают обновляться, в отличие от уровня приложения.

    В вашем же случае, честно говоря, схему очень сильно хочется упростить, а не усложнить. Хочется добавить поле created_at с DEFAULT CURRENT_TIMESTAMP() и хочется добавить реляционные связи. Либо же в принципе уйти на нереляционную БД и задать структуру сущностей там (что опять же должно быть обосновано).

    И дальше именно на уровне приложения делать эту выборку, т.к. это упростит поддержку кода, а с точки зрения трудозатрат ваших и БД при выборках и вставках будет то же самое.

    Помимо этого, поскольку вы делаете процессинг на основе id и его порядковых номеров, не могу не порекомендовать обратить внимание на одни из базовых принципов проектирования ПО. Конечно, вся картина не видна, но я тут вижу нарушение буквы S (Single Responsibility), что id у вас и за порядок отвечает, и за уникальность (да еще и между несколькими таблицами). Хочется как-то их развязать. Со временем жизни продукта это часто оказывается полезно.

    Опять же, беспокойств о том, что вы хотите достичь, достаточно много, но если отвечать именно конкретно на ваш вопрос и давать дополнительно какие-то рекомендации, то как-то так )

    Желаем удачи, образования и всех благ )

    С уважением,
    Иван Томилов
    CEO of Athanor
    Ответ написан
    2 комментария
  • Где найти курсы по Моушэн дизайн?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Вот хороший курс от опытного практикующего специалиста:
    https://vk.com/motionhero

    Здесь есть информация, которая поможет на старте, но, как известно, бесплатный сыр — часто протухший, так что рекомендую все-таки купить курс, когда будете готовы.
    Ответ написан
    1 комментарий
  • Как обезопасить bitcoin кошелек на сервере?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    С одним из наших партнеров у нас была такая задача: необходимо было сделать хранение как фиата, так и крипты. Когда ставится задача что-то обезопасить, важно правильно сформулировать threat model: от каких именно действий и проникновений должна быть защита и уже на основе этого проектировать архитектуру.

    Например, в случае ответа Dark_Scorpion, threat model можно коротко сформулировать следующим образом:
    1. Злоумышленник получил доступ к виртуалке с API.
    2. Злоумышленник получил доступ к виртуалке с bitcoind и ключами и ко всем ключам.

    Дальше думаем как уменьшить негативный эффект в каждом из случаев.

    1 случай. Злоумышленник теперь может подписывать запросы самостоятельно. Решением может быть, например, multisig, где часть ключа хранится у пользователя, а часть у сервера. Таким образом, злоумышленник уже не сможет эти запросы подписывать. Но тут важно еще понимать в какой юрисдикции будет находится продукт, т.к. есть юридические нюансы. Мы с ними сталкивались. Другим решением может быть возможность принудительного отключения виртуалки с bitcoind каким-то простым способом: даунтайм лучше потерянных денег.

    2 случай. Тут уже ничего не поделаешь. Однако, есть продукты вроде https://www.thalesgroup.com/en, которые предоставляют железку, которая безопасно хранит ключи и которая считается невзламываемой. При этом, ключи доступны до тех пор, пока физическая (или виртуальная) карта вставлена в кардридер. Таким образом, если система была скомпрометирована, то достаточно будет вытащить карту. Есть и другие продукты, более бюджетные.

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

    С точки зрения архитектуры как-то так, удачи в реализации )

    С уважением,
    Иван Томилов
    CEO of Athanor
    Ответ написан
    1 комментарий
  • Где взять вектор plexus для Photoshop?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    А ещё проще сделать самому в Ai:

    1. Рисуем несколько точек разного размера:
    5e2a9d5c841f9220879291.jpeg

    Чем больше их будет и чем сильнее они будут отличаться тем лучше — тогда повторения будут менее заметны.

    2. Превращаем их в Symbol:
    5e2a9e032f986830867511.jpeg

    3. Берём инструмент Symbol Sprayer Tool (Shift+S), выбираем символ, который создали и генерим точки:
    5e2a9f3ac404c188977239.jpeg
    5e2a9fd1c3624107788563.jpeg

    4. Теперь остается только соединить их пером, провести настройки и добавить эффекты:
    5e2aa08be4a0a851111389.jpeg

    P.S. Конечно, проще где-то скачать готовое решение, но сначала его нужно найти и найти не какое-то, а подходящее для конкретной ситуации, что может занять время. Сделать самому сложнее в первый раз, но взамен вы получаете а) свободу; б) экономию времени; в) навык, который останется с вами и будет экономить время в будущем
    Ответ написан
    Комментировать
  • Как сделать обновление объекта внутри массива реактивно?

    Athanor
    @Athanor
    Лайк + Решение: не жмись, нажми
    Ответ написан
    Комментировать