Пн go ошибка при попытке обновить вложенные массивы: не найден фильтр массива для идентификатора - PullRequest
1 голос
/ 21 июня 2020

Я пытаюсь обновить документ в Mon go, который представляет сообщество со следующим сценарием.

  • У сообщества есть набор блоков
  • Блок имеет набор этажей
  • На полу есть набор дверей
  • Дверь имеет набор имен этикеток

Имеется идентификатор документа и информация о этикетках, которые должны быть помещенным в каждую дверь, я хочу использовать драйвер MongoDb C# v2.10.4 и mongo:latest для обновления вложенных списков (несколько уровней). Я читал документацию о фильтрах массивов, но у меня не получается, чтобы они работали.

Я создал репозиторий с нуля, чтобы воспроизвести проблему , с инструкциями в Readme о том, как запустить интеграционный тест и локальную MongoDB с docker.

Но, как итог, мой метод группирует метки, чтобы я мог массово размещать имена на желаемой двери и затем он выполняет итерацию по этим группам и обновляет в понедельник go указанный c документ, устанавливая желаемое значение внутри некоторых уровней глубоко вложенного объекта. Я не мог придумать более эффективного способа.

Весь код в приведенном выше репо.

Документ БД:

public class Community
{
    public Guid Id { get; set; }
    public IEnumerable<Block> Blocks { get; set; } = Enumerable.Empty<Block>();
}

public class Block
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<Floor> Floors { get; set; } = Enumerable.Empty<Floor>();
}

public class Floor
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<Door> Doors { get; set; } = Enumerable.Empty<Door>();
}

public class Door
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<string> LabelNames = Enumerable.Empty<string>();
}

Проблема c метод с фильтрами массива:

public async Task UpdateDoorNames(Guid id, IEnumerable<Label> labels)
{
    var labelsGroupedByHouse =
        labels
            .ToList()
            .GroupBy(x => new { x.BlockId, x.FloorId, x.DoorId })
            .ToList();

    var filter =
        Builders<Community>
            .Filter
            .Where(x => x.Id == id);

    foreach (var house in labelsGroupedByHouse)
    {
        var houseBlockName = house.Key.BlockId;
        var houseFloorName = house.Key.FloorId;
        var houseDoorName = house.Key.DoorId;
        var names = house.Select(x => x.Name).ToList();

        var update =
            Builders<Community>
                .Update
                .Set($"Blocks.$[{houseBlockName}].Floors.$[{houseFloorName}].Doors.$[{houseDoorName}].LabelNames", names);
        await _communities.UpdateOneAsync(filter, update);
    }
}

Исключение равно

MongoDB.Driver.MongoWriteException with the message "A write operation resulted in an error.
  No array filter found for identifier 'Block 1' in path 'Blocks.$[Block 1].Floors.$[Ground Floor].Doors.$[A].LabelNames'"

Вот более наглядный пример того, как вложенная структура выглядит в базе данных. Обратите внимание, что я хочу обновить значение LabelNames, которое представляет собой массив строк. enter image description here

I appreciate any help to have this working and suggestions on whether it's the right approach assuming that I cannot change the repository's method signature.


SOLUTION RESULT: Thanks for the quick answer @mickl, it works perfectly. Result at this спецификация репо c точка истории точно так, как было предложено.

1 Ответ

1 голос
/ 21 июня 2020

$[{houseBlockName}] ожидает идентификатор, который действует как заполнитель и имеет соответствующий фильтр, определенный в arrayfilters ( позиционный фильтр ). Похоже, вы пытаетесь передать значение фильтра напрямую, что неверно.

Ваш C# код может выглядеть так:

var houseBlockName = house.Key.BlockId;
var houseFloorName = house.Key.FloorId;
var houseDoorName = house.Key.DoorId;
var names = house.Select(x => x.Name).ToList();

var update = Builders<Community>.Update.Set("Blocks.$[block].Floors.$[floor].Doors.$[door].LabelNames", names);

var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> blockFilter = new BsonDocument("block.Name", new BsonDocument("$eq", houseBlockName));
ArrayFilterDefinition<BsonDocument> floorFilter = new BsonDocument("floor.Name", new BsonDocument("$eq", houseFloorName));
ArrayFilterDefinition<BsonDocument> doorFilter = new BsonDocument("door.Name", new BsonDocument("$eq", houseDoorName));
arrayFilters.Add(blockFilter);
arrayFilters.Add(floorFilter);
arrayFilters.Add(doorFilter);

var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };

var result = _communities.UpdateOne(filter, update, updateOptions);
...