#c# #mongodb #linq #iqueryable
#c# #mongodb #linq #iqueryable
Вопрос:
Если я чего-то не упустил, в документации для драйвера C # MongoDB, похоже, есть большие пробелы.
Я пытаюсь взять IMongoQueryable
(который является результатом различных операций LINQ Where, Select и т. Д.) И сохранить результаты в коллекции на стороне базы данных. Конечно, я мог бы выполнить итерацию на стороне клиента и сохранить ее таким образом, но даже в пакетах это неэффективно, а в оболочке это довольно простая операция для использования $merge
или $out
.
В коллекции есть метод AggregateToCollection<TResult>()
, который, как я полагаю, является именно тем, что я хочу, но он принимает PiplineDefinition<TDocument, TResult>
параметр, который я не очень представляю, как на самом деле генерировать из IMongoQueryable
.
Я пробирался по кроличьей норе, используя GetExecutionModel()
IMongoQueryable
для получения BsonDocument
строки or или Json, но тогда я все еще понятия не имею, как превратить это в PipelineDefinition
то, что мне нужно!
Я бы подумал, что найду метод расширения IMongoQueryable
, который позволил бы мне отправить его для объединения в коллекцию.
Пока что, чтобы иметь возможность отправлять IMongoQueryable
в коллекцию, у меня есть что-то вроде:
var executionModelDocument = queryable.GetExecutionModel().ToBsonDocument();
// somehow turn the document into pipeline stages and a pipeline??
await _database.GetCollection<TDocument>().AggregateToCollectionAsync<TResult>(pipeline);
Как мне на самом деле это сделать?
Ответ №1:
Хорошо, в конечном итоге это было проще, чем ожидалось, но все равно разочаровывало.
Прежде всего, оказывается, вы можете получить конвейер из IMongoQueryable
as JSON, просто вызвав ToString()
его. Хотя я говорю JSON, это не совсем так — вывод содержит полный JSON для конвейера в виде массива этапов, но имеет метку впереди и содержит JSON в круглых скобках. Я воспользовался ярлыком здесь и просто взял грязную подстроку:
var queryableJson = queryable.ToString();
var trimmedDocument = queryableJson.Substring(10, queryableJson.Length - 11); // TODO: more reliably get the true json rather than blindly removing what should be "aggregate(" and ")"
Затем я повторно сериализовал JSON обратно в массив BsonDocument
и сделал PipelineDefinition
из него a (a BsonDocument[]
может быть неявно преобразован в a PipelineDefinition
):
PipelineDefinition<TDocument, TResult> pipelineQueryable = BsonSerializer.Deserialize<BsonDocument[]>(trimmedDocument);
Обратите внимание, что в то время как документация на http://mongodb.github.io/mongo-csharp-driver/2.4/reference/driver/definitions/#pipelines говорит, что я мог бы неявно использовать один BsonDocument
элемент для конвейера, а не массив этапов, это неверно, если только в другом пространстве имен нет перегрузки, которую я не нашел.
Теперь, когда у нас есть определенный конвейер для IMongoQueryable, мы можем просто добавлять к нему этапы для достижения желаемого результата (в данном случае для объединения результатов конвейера в другую коллекцию). Вы можете указать свойства MergeStageOptions<TResult>
объекта для управления поведением, но для меня значения по умолчанию работали нормально:
var stageMerge = PipelineStageDefinitionBuilder.Merge<TResult, TResult>(_database.GetCollection<TResult>(), new MergeStageOptions<TResult>());
var mergePipeline = pipelineQueryable.AppendStage(stageMerge);
Используя наш недавно улучшенный конвейер, мы можем применить его к исходной коллекции, чтобы объединить выходные данные с целевой коллекцией:
_database.GetCollection<TDocument>().AggregateToCollection(mergePipeline);
Для простоты то, что я продемонстрировал здесь, не является асинхронным, однако я воспользовался ожидаемыми методами в драйвере в моем реальном коде, поскольку синхронные методы просто переносят ожидаемые версии.
Это действительно все, что нужно было сделать! Когда у меня будет больше времени, я вернусь и попытаюсь пропустить этап сериализации-десериализации, поскольку он заметно медленный и не должен быть необходимым. Я также планирую превратить это в метод расширения для удобства.
Комментарии:
1. Я приму свой собственный ответ через несколько дней, если никому нечего сказать — я немного удивлен, что не смог найти никого, кто столкнулся с этой проблемой, что заставляет меня нервничать, что есть веская причина не делать этого, поэтому, если вы знаете какие-либо такие причины, сообщите нам всемздесь 🙂