Примечание. Если вы находитесь здесь исключительно для ознакомления с технической проблемой, указанной в заголовке, перейдите к «Решение».

Днем я работаю в семейном бизнесе, который предоставляет программное обеспечение и услуги библиотекам. Некоторое время назад я заинтересовался созданием экспериментальной интеграции между нашим программным обеспечением и голосовым помощником. Недавно у меня появилась пропускная способность, чтобы избавиться от этого зуда. Я решил начать с Google Assistant, полагая, что обладание телефоном Pixelbook и Pixel 2 даст мне некоторые преимущества.

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

Вот цель, с которой я начал:

  • Пользователь просит выполнить поиск в каталоге библиотеки.
  • Помощник использует ключевые фразы, такие как «кем» или «о», для выбора из следующих значений: заголовок, автор, тема или серия.
  • Помощник выполняет поиск и указывает количество результатов. На данный момент помощник ограничен одной страницей до 10 результатов.
  • Для голосовых интерфейсов пользователь может указать номер результата, чтобы получить более подробную информацию о записи заголовка.
  • Для экранных интерфейсов набор результатов будет отображаться в виде карусели, включая скрытую графику для каждого заголовка, если таковая имеется.

Проблема

Чтобы это сработало, мне нужно было динамически построить карусель с использованием недетерминированных элементов. Однако все руководства и примеры кода, предоставленные Google, показывают только карусели, основанные на фиксированном количестве уже известных элементов. Хуже того, после длительного поиска в Google, чтения статей о StackOverflow и изучения документации API я не смог найти рабочий пример. Все найденные мной примеры либо использовали устаревший API, либо предположительно представляли собой спекулятивные подходы, которые люди добросовестно предлагали, но явно не тестировали сами.

Вот два общих подхода (с несколькими вариантами), которые я пробовал по ходу дела:

  • Создайте пустую карусель, постройте каждый элемент в цикле, а затем добавьте каждый элемент в карусель. Вы не можете создать пустую карусель; он должен быть построен из уже подготовленного набора элементов.
  • Создайте карусель с одним элементом, затем немедленно удалите этот элемент и добавьте дополнительные элементы. Вы не можете удалять элементы из созданной карусели, даже если вы можете добавлять элементы.

Если вам неясно различие между детерминированными и недетерминированными наборами элементов, рассмотрите эти два варианта использования:

1. Создайте карусель для пунктов меню в вашем ресторане.

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

Между прочим, в учебнике Google вы создаете карусель, чтобы кто-то мог выбрать свой любимый цвет. Им дается только три варианта, и каждый раз одни и те же три варианта. К счастью, двое из них были синими, так что я не чувствовал себя обделенным!

2 - Создайте карусель для результатов поиска по ключевым словам.

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

Решение

В конечном итоге сработало создание пустого объекта items, создание каждого элемента как другого объекта в цикле, добавление их к объекту items и затем, наконец, создайте карусель из объекта items.

Теперь более опытный разработчик javascript мог бы понять это быстрее. У меня было два физических недостатка. Во-первых, я занимаюсь Python и C # целую вечность (игры), так что я очень устарел в javascript, и он существенно изменился с тех пор, как в последний раз я много с ним делал. Во-вторых, я попытался следовать документации API, в которой классы OptionItem и OptionInfo упоминались как элементы карусели.

Итак, я какое-то время пытался импортировать эти классы, создавать их с помощью конструкторов, а затем помещать их в карусель. Как оказалось, у них нет конструкторов - вы не можете создать их напрямую, а только предоставить общие объекты javascript в правильном формате для преобразования конструктором карусели. Ой.

Вот что у меня получилось:

function searchResultCarousel(results) {
  var items = {};
results.forEach((result, index) => {
    console.log(`Building item from result: ${result.title}`);
items[index] = {
      optionInfo: {
        key: (index + 1).toString(),
        synonyms: result.title,
      },
      title: result.title,
      url: ``,
      image: new Image({
        url: `[REDACTED DUE TO TRADE SECRETS]`,
        alt: `Cover art for: ${result.title} - ${result.year}`
      }),
    }
  })
return new Carousel({
    title: 'Search Results',
    items: items
  });
};

где объект results - это объект JSON, возвращенный поисковым API.

А вот как это выглядит в действии:

Если бы я мог порекомендовать одно улучшение документации по Actions on Google API, это было бы так:

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

Также благодарю этого парня за то, что он нашел решение, но также помог мне исправить серьезную проблему с [] s, которая должна была быть {} s. К тому моменту я действительно понял правильный подход к сборке элементов карусели, но его петля была намного элегантнее, так что я тоже извлек из этого урок.

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