Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sub-Query or Join with embedded/nested document using C# LINQ with MongoDB

I am trying to do something like bellow example but getting exception as - System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable1 ' cannot be used for parameter of type 'System.Linq.IQueryable1' of method. Here is my code and related classes . how can i resolve this issue, is there any way except which a trying to do.

    var channels = _channelService.Collection;
    var tracks = _trackService.Collection;

    var query = from b in tracks.AsQueryable()
                select b;

    var data = (from q in channels.AsQueryable()
               from p in q.Episodes
               //from x in  trackcoll.AsQueryable()
               select new
               {
                   p,
                   Tracks = query.Where(w => p.Tracks.Contains(w.Id))
               }).ToList();

// Related classes
    public class ContentBase : IAuditable
    {
        public string Id { get; set ; }
        public string CreatedBy { get ; set ; }
        public string CreatedOn { get ; set ; }
        public string UpdatedBy { get ; set ; }
        public string UpdatedOn { get; set; }
    }

    public class Channel: ContentBase
    {
        public List<Episode> Episodes { get; set; }
    }

    public class Episode: ContentBase
    {
        // List of track Id
        public List<string> Tracks { get; set; }
    }
    public class Track: ContentBase
    {
        public string TrackUrl { get; set; }
        public string Duration { get; set; }
        public string Size { get; set; }
        public string ContentType { get; set;

    }
like image 673
Khairul Alam Avatar asked Mar 09 '20 04:03

Khairul Alam


2 Answers

MongoDB's LINQ support for joins is limited to equal joins as described here. Your expression cannot be translated into Aggregation Framework's $lookup since there's no equivalent syntax for .Contains().

Therefore you have to run an operation that's closer to Aggregation Framework syntax. One example is a fluent aggregate interface which allows you to run extension methods having the same name as Aggregation Framework's operators. Try:

var q = _channels.Aggregate()
                    .Unwind(x => x.Episodes)
                    .Lookup(
                        foreignCollectionName:"tracks", 
                        localField:"Episodes.Tracks", 
                        foreignField:"_id", 
                        @as:"Tracks");

var result = q.ToList();

Above code will return a List<BsonDocument>

like image 159
mickl Avatar answered Oct 19 '22 16:10

mickl


mickl's answer will get you there with the official driver, but if you don't like dealing with bsondocuments and would like to have some degree of type-safety, you can simply do the following with mongodb.entities library (which i'm the author of):

    public class EpisodeWithTracks
    {
        public Track[] Tracks { get; set; }
    }
var pipeline = new Template<Channel, EpisodeWithTracks>(@"
[
    {
        $unwind: '$<Episodes>'
    },
    {
        $lookup: {
            from: '<track_collection>',
            localField: '<Episodes.Tracks>',
            foreignField: '_id',
            as: '<Tracks>'
        }
    }
]")

.Path(c => c.Episodes)
.Tag("track_collection", collectionName)
.Path(c => c.Episodes[0].Tracks)
.PathOfResult(ewt => ewt.Tracks);

var result = DB.Aggregate(pipeline)
               .ToList();

here's the wiki page explaining how it works.

like image 33
Dĵ ΝιΓΞΗΛψΚ Avatar answered Oct 19 '22 17:10

Dĵ ΝιΓΞΗΛψΚ