Вступление


Совсем недавно я начал работу над новым проектом, который включал в себя работу с уже достаточно известным сервисом (AI) от Google — DialogFlow. Суть заключалась в том, что у нас был бот (Telegram) который работал с API нескольких сайтов, а сам же DialogFlow помогал нам структурировать само общение с человеком, для чего он в принципе и создавался. Нужно сказать, что статья предназначена для людей, которые только начали знакомство с сервисом или уже имеют небольшой опыт, так далее возможны термины: Intent, Context, Action, Event ну и то ради чего мы и собрались — Entities. Надеюсь, что статья будет полезна тем, кто немного не понял как именно через код можно проделывать CRUD операции с Entities.

Entities в DialogFlow


В DialogFlow Entities представляют собой некие сущности, которые состоят из ReferenceValue и Synonyms. Что-то похожее на ключ-значение, только с той разницей, что синонимов может быть много, и чем их больше — тем лучше, поскольку это «упрощает боту жизнь» и ему легче понять о каком значении идет сейчас речь, а поставленная галочка на Fuzzy matching поможет боту еще и понимать о чём идет речь, даже если вы упустили одну букву или другой символ. Выглядит это примерно так:

Москва — москва, Москва, Мсква…

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

EntityTypes


Вот тут и был для меня первый, хоть и небольшой, подводный камень. Ведь в документации есть методы для работы с Entity, а есть для работы с EntityType — что из них что?

На самом деле всё не так и сложно, просто путаница возникает из-за того, что в самом DialogFlow вкладка с EntityTypes называется Entities:

image

EntityType:image

Entitie:image

Хотя в самом DialogFlow считается, что есть Entity и в ней есть Entries (т.е. Сущность и записи в ней).

Перейдём дальше.

Можно создать EntityType вручную прямо на DialoFlow, но я покажу, как это возможно сделать через код (ведь потом, чтобы создавать Entities нам нужно будет знать в каком именно EntityType это делать).

Сперва подключим NuGet пакет Google.Cloud.Dialogflow.V2. На момент написания статьи, была установлена версия 1.1.0:

image

Создадим сперва класc и назовём его EntityTypeManagement в котором будут основные операции:

using Google.Api.Gax;
using Google.Cloud.Dialogflow.V2;

public class EntityTypeManagement
    {
      //Create - создание нового EntityType по типу Test1 (на картинке выше) 
      //projectId - Id вашего агента с DialogFlow
      //displayName - имя которые вы хотите задать для нового  
        public async Task Create(string projectId, string displayName, EntityType.Types.Kind kind = 
      EntityType.Types.Kind.Map)
        {
            var client = await EntityTypesClient.CreateAsync();

            var entityType = new EntityType();
            entityType.DisplayName = displayName;
            entityType.Kind = kind;

            var createdEntityType = client.CreateEntityType(
                parent: new ProjectAgentName(projectId),
                entityType: entityType
            );

            Console.WriteLine($"Created EntityType: {createdEntityType.Name}");
        }
       
      //List - получение всех EntityTypes от вашего агента
      //projectId - Id вашего агента с DialogFlow
        public async Task<PagedEnumerable<ListEntityTypesResponse,EntityType>> List(string projectId)
        {
            var client = await EntityTypesClient.CreateAsync();
            var response = client.ListEntityTypes(
                parent: new ProjectAgentName(projectId)
            );
            return response;
        }

       //GetEntityTypeId - получение EntityType за именем
       //projectId - Id вашего агента с DialogFlow
       //targetEventTypeName - имя EntityType, которое вы ищите 
        public async Task<string> GetEntityTypeId(string projectId,string targetEventTypeName)
        {
            var client = await EntityTypesClient.CreateAsync();
            var response = client.ListEntityTypes(
                parent: new ProjectAgentName(projectId)
            );
            string id = response.Where(x => x.DisplayName == targetEventTypeName).FirstOrDefault().Name;
            string returningId = id.Replace($"projects/{projectId}/agent/entityTypes/", "");
            return returningId;
        }

       //Delete и BatchDelete удаление одного и нескольких сразу EntityType
       //Удаление происходит по entityTypeId
        public async Task Delete(string projectId, string entityTypeId)
        {
            var client = await EntityTypesClient.CreateAsync();

            client.DeleteEntityType(new EntityTypeName(projectId, entityTypeId: entityTypeId));

            Console.WriteLine($"Deleted EntityType: {entityTypeId}");

        }

        
        public async Task BatchDelete(string projectId, IEnumerable<string> entityTypeIds)
        {
            var client = await EntityTypesClient.CreateAsync();
            var entityTypeNames = entityTypeIds.Select(
                id => new EntityTypeName(projectId, id).ToString());
            client.BatchDeleteEntityTypes(new ProjectAgentName(projectId),
                entityTypeNames);
        }
    }

Тут сразу нужно сказать, что функции Update нет лишь потому, что Create уже выполняет её функцию.

Теперь когда мы имеем базовые методы для работы с EntityTypes перейдём к Entity и сразу же создадим класc EntityManagement:

public class EntityManagement
    {
       using Google.Cloud.Dialogflow.V2;        


      //Create - создание только одной Entity
      //entityTypeId - Id EntityType в котором мы хотим создать Entity
      //entityValue - ReferenceValue для Entity
      // synonyms - массив синонимов для Entity
        public async Task Create(string projectId,
                                 string entityTypeId,
                                 string entityValue,
                                 string[] synonyms)
        {
            var client = await EntityTypesClient.CreateAsync();
            var entity = new EntityType.Types.Entity() { Value = entityValue};
            entity.Synonyms.AddRange(synonyms);
            
            var operation = await client.BatchCreateEntitiesAsync(
                parent: new EntityTypeName(projectId, entityTypeId),
                entities: new[] { entity }
            ); 
            operation.PollUntilCompleted();
        }

       //CreateMany - Создание многих Entities 
       //entityTypeId - Id EntityType в котором мы хотим создать Entity
       //entities - коллекция Entities для создания
        public async Task CreateMany(string projectId,string entityTypeId, IEnumerable<Entity> entities)
        {
            var client = await EntityTypesClient.CreateAsync();
            List<EntityType.Types.Entity> results = new List<EntityType.Types.Entity>();
            foreach (var item in entities)
            {
                var entity = new EntityType.Types.Entity() { Value = item.value };
                entity.Synonyms.AddRange(item.synonyms);
                results.Add(entity);
            }
           
            var operation = await client.BatchCreateEntitiesAsync(
                parent: new EntityTypeName(projectId, entityTypeId),
                entities: results.ToArray());
            operation.PollUntilCompleted();
        }

      //Delete - для удаления одной Entity, но если "string entityValue" заменить на 
     //string[] entityValue и в коде просто присвоить entityValues: entityValue
     //то будет для нескольких сразу.
        public async Task Delete(string projectId, string entityTypeId, string entityValue)
        {
            var client = await EntityTypesClient.CreateAsync();

            var operation = await client.BatchDeleteEntitiesAsync(
                parent: new EntityTypeName(projectId, entityTypeId),
                entityValues: new[] { entityValue }
            );

            Console.WriteLine("Waiting for the entity deletion operation to complete.");
            operation.PollUntilCompleted();

            Console.WriteLine($"Deleted Entity: {entityValue}");

        }
        
      //List - Получение всех Entities с EntityType 
     public async Task<List<Entity>> List(string projectId, string entityTypeId)
        {
            var client = await EntityTypesClient.CreateAsync();
            var entityType = await client.GetEntityTypeAsync(new EntityTypeName(
                projectId, entityTypeId
            ));
            List<Entity> EntitiesList = new List<Entity>();
            
            foreach (var entity in entityType.Entities)
            {
                List<string> Synonyms = new List<string>();
                foreach (var item in entity.Synonyms)
                {
                    Synonyms.Add(item);
                }
                EntitiesList.Add(new Entity { value = entity.Value, synonyms = Synonyms.ToArray() });
                Synonyms = null;
            }

            return EntitiesList;
        }
    }

Добавим нашу модель, которая будет представлять Entity:

public class Entity
    {
        public string value { get; set; }
        public string[] synonyms { get; set; }
    }

Готово, все базовые функции CRUD готовы. Опять-таки, возвращаясь к Update, ее можно отдельно реализовать, но она попросту не нужна, так как Create работает и как Update тоже.

Примеры достаточно просты и, нужно согласиться, тут есть таки «хардкод», но надеюсь статья пригодится тем, кто, как и я, не мог понять как же правильно проделывать CRUD операции через код.

static void Main(string[] args)
        {
      EntityTypesManagement entityTypesManagement = new EntityTypesManagement();
      EntityManagement entityManagement = new EntityManagement();
       string ProjectId = "Your Project`s id";
       string EntityName = "Your EntityName";
      var entityId = await entityTypesManagement.GetEntityTypeId(ProjectId, EntityName);
       Entity entity =  new Entity{value = "1", synonyms = new[]{"1","1"}};
      /*await*/ entityManagement.Create(ProjectId,entityId,entity);
 }