...

Как достать много данных?

Тема в разделе "Примеры сценариев", создана пользователем ava_var, 10 апр 2023.

  1. ava_var

    ava_var Активный участник

    Я решил посмотреть на производительность разных подходов для получения данных. Собственно, в статье хочу поделиться с вами результатами и опишу эти самые методы.
    Задача.
    Перед нами поставили следующую задачу - нужно найти компании определенной отрасли. Затем пройтись по всем этим компаниям и достать телефоны контактов, чтоб затем их обзвонить.

    Первый метод. Решение в лоб или 1000 search
    Придумываем алгоритм
    • Добавляем контекстную переменную Компания
    • С помощью этой переменной осуществляем поиск.
    • Перебираем результат поиска.
    • Для каждой компании проверяем контакты.
    • Если контакты есть - получаем данные контакта с сервера
    • Извлекаем телефоны контакта и складываем в специальный массив.
    • Возвращаем массив
    Приступаем к реализации и получаем вот такой код
    Код:
    
    const companySearch Context.fields.company.app.search();
    if (
    Context.data.industry) {
        
    companySearch.where(it => it._industries.has(Context.data.industry!.id));
    }
    const 
    companies await companySearch.size(1000).all();
    const 
    phonesTPhone<PhoneType>[] = [];
    for (
    let company of companies) {
       const 
    contactRef company.data._contacts?.find(item => !!item.id);
       if (
    contactRef && contactRef.id != EMPTY_UID) {
          const 
    contact await contactRef.fetch();
          if (
    contact && contact.data._phone && contact.data._phone.length 0) {
              
    phones!.push(...contact.data._phone);
          }
       }
    }
    Context.data.phones phones;
    Запускаем код, убеждаемся что он отрабатывает и возвращает записи.

    Второй способ. Promise.All и 1000 фетчей
    Используем тот же самый алгоритм, но вместо обычного for будем использовать Promise.all и map
    Реализуем идею и получаем следующее решение
    Код:
    
    const companySearch Context.fields.company.app.search();
    if (
    Context.data.industry) {
        
    companySearch.where(it => it._industries.has(Context.data.industry!.id))
    }
    const 
    companies await companySearch.size(1000).all();
    const 
    phonesTPhone<PhoneType>[] = [];
    await Promise.all(companies.map(async (company) => {
        const 
    contactRef company.data._contacts?.find(item => !!item.id);
        if (
    contactRef && contactRef.id != EMPTY_UID) {
            const 
    contact await contactRef.fetch();
            if (
    contact && contact.data._phone && contact.data._phone.length 0) {
                
    phones!.push(...contact.data._phone);
            }
        }
    }));
    Context.data.phones phones;
    В целом, это решение уже быстрее, но количество запросов на сервер всё еще удручает.

    Третий способ. Один search
    Алгоритм
    • Добавляем контекстную переменную Компания
    • С помощью этой переменной осуществляем поиск.
    • Добавляем контекстную переменную Контакт
    • С помощью этой переменной создаем поиск на основе полученных компаний.
    • Сохраняем результат в локальный массив
    • Перебираем список компаний
    • Для каждой компании проверяем контакты.
    • Если контакты есть - получаем данные контакта из сохраненного массива
    • Извлекаем телефоны контакта и складываем в специальный массив.
    • Возвращаем массив
    Приступаем к реализации и получаем вот такой код
    Код:
    
    const companySearch Context.fields.company.app.search();
    if (
    Context.data.industry) {
        
    companySearch.where(it => it._industries.has(Context.data.industry!.id))
    }
    const 
    companies await companySearch.size(1000).all();
    const 
    contacts await Context.fields.contact.app.search()
        .
    where(=> x.__deletedAt.eq(null) && x._companies.link(companies))
        .
    size(10000)
        .
    all();
    const 
    phonesTPhone<PhoneType>[] = [];
    for (
    let company of companies) {
        const 
    contactRef company.data._contacts?.find(item => !!item.id);
        if (
    contactRef && contactRef.id != EMPTY_UID) {
            const 
    contact contacts.find(item => item.id === contactRef.id);
            if (
    contact && contact.data._phone && contact.data._phone.length 0) {
                
    phones!.push(...contact.data._phone);
            }
        }
    }
    Context.data.phones phones;
    Не уверен, что здесь сильно прибавили в скорости, но точно снизили загрузку на сервер, так как в данном примере отправляем только 2 запроса на сервере.

    Четвертый способ. Серверный search
    Этот способ я реализовал только для того, чтоб сравнить скорость клиентского и серверного сценария.
    Суть способа - берем код из третьего способа, переносим на Сервер и вызываем его из клиентского.

    Испытания
    Для испытаний создал через процесс компании и контакты и рандомно заполнил их данными.
    Для испытаний немного доработал код (полный файл сценария будет в приложении).
    Для расчета времени я использовал 10 успешных выполнений.

    Результаты испытаний

    [​IMG]
    Вывод
    Проверяйте свои гипотезы и делитесь свои способами по улучшению производительности.
    Ну и используйте search там, где это подходит.
    Если у вас остались вопросы - пишите комментарии.

    З.Ы. В архиве - клиентский сценарий, серверный сценарий и скриншот контекста

    Вложения:

    • sources.zip
      Размер файла:
      92,7 КБ
      Просмотров:
      12
  2. kurbatov-la

    kurbatov-la Активный участник

    Анатолий, спасибо, что поделились этим примером!
    Вопрос по синтаксису
    Есть ли разница в следующих вариантах использования where в одном запросе?
    Код:
    
    .where(=> x.__deletedAt.eq(null) && x._companies.link(companies))
    
    
    Код:
    
    .where(=> x.__deletedAt.eq(null))
    .
    where(x._companies.link(companies))
  3. alekc_bpm87

    alekc_bpm87 Эксперт

    Спасибо за такую аналитику! Очень полезно! Будем ждать еще подобных экспериментов!)

    такое - через && раньше вообще не работало)
    нужно было писать как в справке:
    Код:
    
    .where((fg) => g.and (
            
    f.__deletedAt.eq(null),
            
    f.service.link(Context.data.service)
            ))
  4. ava_var

    ava_var Активный участник

    не заметил разницы
    как я понимаю условия складываются в запрос при вызове first() или all()
    а как эти условия были добавлены - уже не имеет значения