N psql: создание индексов для столбца jsonb с EF Core - PullRequest
0 голосов
/ 22 апреля 2020

У меня есть стандартная таблица № SQL в PostgreSQL: ID (UUID) и Данные (jsonb). Я хотел бы создать индекс для одного из свойств документа JSON, который входит в столбец данных. Обычно я использую EF Core следующим образом:

modelBuilder.Entity<T>().HasIndex(e => e.ColumnToPutAnIndexOn);

Моя презумпция создания индекса для свойства JSON была такой:

modelBuilder.Entity<T>().HasIndex(e => e.Data.PropertyToPutAnIndexOn);

Однако, это дает мне ошибку:

The expression should represent a simple property access: 't => t.MyProperty'.

Документация Npg sql все еще находится в стадии разработки.

Кто-нибудь придумал, как лучше всего создать индексы для JSON Документы, за исключением написания необработанных SQL заявлений?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 22 апреля 2020

Шей, спасибо за информацию. Основываясь на ответе «это невозможно», я сказал себе: держи свое пиво.

Итак, я что-то сделал. Что-то некрасивое, но я сделал это. И это работает.

Возьмем, в нашем случае, саму сущность, содержащую столбец ID и jsonb Data:

    public class Entity<T> where T : EntityData
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public Guid Id { get; set; }

        [Column(TypeName = "jsonb")]
        public T Data { get; set; }

        public Entity() : base()
        {
        }
    }

Примечание: обобщенный c T - это EntityData, который ничто но пустой базовый класс для будущих целей. В действительности это может быть что угодно.

Затем мы можем использовать класс Entity<T> следующим образом:

Entity<User> userEntity = new Entity<User>();
userEntity.Data.Username = "myusername"; // the .Data property is now of type User (the actual POCO/Model/JSON document)

Класс User имеет свойства:

    public class User : EntityData
    {
        [Index] // custom IndexAttribute class
        public string Username { get; set; }
    }

Теперь Я создал функцию EnsureIndexes (), которую можно разместить внутри собственного класса DbContext / UnitOfWork или где угодно. Эта функция использует отражение и перебирает свойства сущности внутри Entity<T>.Data и вызывает необработанный NpgsqlCommand:

            // get DbSet<> properties of the context
            PropertyInfo[] dbsets = context.GetType().GetProperties().Where(x => x.PropertyType.Name == "DbSet`1").ToArray();
            foreach (PropertyInfo dbset in dbsets)
            {
                // get the generic type of the DbSet<> property. 
                // in this case we will get the Entity<T> type
                string lowerCaseDbSetName = dbset.Name.ToLower();
                Type dbsetDataType = dbset.PropertyType.GetGenericArguments()[0];

                // try to obtain the Entity<T>.Data PropertyInfo
                PropertyInfo dataProperty = dbsetDataType.GetProperty("Data");
                if (dataProperty == null)
                {
                    continue;
                }
                Type dataPropertyType = dataProperty.PropertyType;

                // Now iterate over the properties of the POCO/Document stored in Entity<T>.Data
                PropertyInfo[] jsonProperties = dataPropertyType.GetProperties().ToArray();
                foreach (PropertyInfo jsonProperty in jsonProperties)
                {
                    // If we can find an IndexAttribute....
                    IndexAttribute attribute = Attribute.GetCustomAttribute(jsonProperty, typeof(IndexAttribute)) as IndexAttribute;
                    if (attribute == null)
                    {
                        continue;
                    }

                    string camelCaseProperty = jsonProperty.Name.ToCamelCase();
                    string lowerCaseProperty = jsonProperty.Name.ToLower();

                    // We will then create an SQL statement to create an index on that property. Mainaining the standard Npgsql naming format of IX_DbSet_Property
                    string sql = $"CREATE INDEX IX_{dbset.Name}_{lowerCaseProperty} ON public.\"{dbset.Name}\" ((public.\"{dbset.Name}\".\"Data\" ->> '{camelCaseProperty}'));";
                    int result = context.ExecuteNonQuery(sql); // my custom function returns -2 on an exception
                    if (result != -2)
                    {
                        Console.WriteLine(sql);
                    }
                }
            }
0 голосов
/ 22 апреля 2020

К сожалению, индексирование свойств внутри документов jsonb не так просто, как индексация столбцов таблицы ... Посмотрите на документы PostgreSQL, чтобы получить представление .

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

Из-за сложности и различных вариантов, предложения еще ничего не делают для вас автоматически - вам придется использовать raw SQL в ваших миграциях, чтобы определить нужный вам индекс (с этим проблем нет - это приветствуется). Это может измениться в будущем, если мы сможем предоставить логичные логики создания индекса c для другого сценария jsonb ios.

...