Nest не справляется с большой моделью базы данных с EF6 / MVC5

Мне предоставили базу данных, с которой должны быть возможны основные операции CRUD. Это было быстро достигнуто с помощью .NET 4.5 / MVC5 и EF6. Это означает подход «сначала база данных».

Новое требование: (эластичный) поиск.

При создании индекса для пользовательского класса (не связанного с другими в модели) все в порядке. Когда я использую класс с большим количеством внешних ключей, все перестает работать. База данных состоит из 100 таблиц с более чем 400 внешними ключами.

Я думаю, что проблема может заключаться в циклических ссылках (у клиента n контрактов, в которых есть ссылка на клиента, у которого есть список контрактов ... Вы понимаете). В конце концов я получаю исключение OutOfMemory, и все рушится.

Код:

public static Uri node;
public static ConnectionSettings settings;
public static ElasticClient client;

public ActionResult TestIndex()
    {
        node = new Uri("http://localhost:9200");
        settings = new ConnectionSettings(node, defaultIndex: "crudapp");
        client = new ElasticClient(settings);

        var indexSettings = new IndexSettings();
        indexSettings.NumberOfReplicas = 1;
        indexSettings.NumberOfShards = 1;

        //The next line causes the OutOfMemoryException
        client.CreateIndex(c => c.Index("crudapp")
                                 .InitializeUsing(indexSettings)
                                 .AddMapping<Customer>(map => map.MapFromAttributes(maxRecursion: 1)));


        foreach (Customer c in db.Customer.Where(a => a.Active == true))
            client.Index(c);

        return View("Index");
    }

Как я могу сказать Nest, чтобы он прекратил рекурсию или не использовал определенные объекты?

Примеры занятий:

    public partial class Customer
    {
        public Customer()
        {
            this.CustomerContract = new HashSet<CustomerContract>();
        }

        public int Customerid { get; set; }
        public string CustomerName { get; set; }
        public string Description { get; set; }
        public bool Active { get; set; }

        public virtual ICollection<CustomerContract> CustomerContract { get; set; }
    }

    public partial class CustomerContract
    {
        public CustomerContract()
        {
            this.Host = new HashSet<Host>();
        }

        public int CustomerContractid { get; set; }
        public string CustomerContractName { get; set; }
        public string Description { get; set; }
        public int CustomerID { get; set; }
        public bool Active { get; set; }

        public virtual Customer Customer { get; set; }
        public virtual ICollection<Host> Host { get; set; }
    }

person Bart De Vos    schedule 10.02.2015    source источник


Ответы (1)


OutOfMemoryException почти наверняка исходит из сериализации JSON вашего Customer объекта. Таким образом, проблема заключается не в функциональности NEST или Elasticsearch, а в функциональности JSON.NET.

Вы можете справиться с этим одним из двух способов:

1. Выборочная сериализация больших объектов

Эта статья автора JSON.NET обсуждает уменьшение размера объектов. Вы можете предоставить свойства с помощью свойства JsonIgnoreAttribute, чтобы указать сериализатору игнорировать определенные характеристики. Или реализация IContractResolver может быть менее навязчивой для определений вашего EF объекты (особенно с учетом того, что они создаются в первую очередь из базы данных), но я не уверен, можно ли это использовать в сочетании с зависимостью NEST от JSON.NET.

Если у вас нет возможностей справиться с зависимостью NEST от JSON.NET, вы всегда можете найти другой способ сериализации вашего объекта и «необработанного», используя синтаксис Elasticsearch.NET вместо NEST (который, по сути, строится поверх Elasticsearch.NET). Поэтому вместо вызова ElasticClient.Index(..) вызовите ElasticClient.Raw.Index(..), где параметр body представляет собой строковое представление JSON (вашей собственной конструкции) объекта, который вы хотите проиндексировать.

2. Проецируйте большой объект на меньший объект передачи данных

Вместо индексации объекта Customer сопоставьте только те свойства, которые вы хотите проиндексировать, в объект передачи данных (DTO), нацеленный на вашу схему / тип документа Elasticsearch.

foreach (Customer c in db.Customer.Where(a => a.Active == true))
    client.Index(new MyElasticsearchTypes.Customer()
        {
            CustomerId = c.CustomerId,
            CustomerName = c.CustomerName,
            Description = c.Description
        });

В C # у вас есть множество вариантов создания такого DTO, в том числе:

  1. Явно типизированные объекты с ручным отображением (как в моем примере).
  2. Явно типизированные объекты с помощью инструмента сопоставления, такого как AutoMapper.
  3. Динамические объекты.

Квартира по дизайну

Имейте в виду, что использование Elasticsearch - это не просто добавление данных в «индекс». Вам нужно начать думать в терминах «документов» и понять, что это означает, когда вы пытаетесь индексировать данные, полученные из реляционной базы данных. Статья руководства Elasticsearch Входящие и исходящие данные - хорошее место для начала чтения. Другая статья под названием Управление отношениями внутри Elasticsearch особенно актуальна для вашей ситуации:

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

person Snixtor    schedule 11.02.2015