3
votes

MongoDB c # récupère tous les éléments correspondants dans un tableau dans un document à l'aide du générateur de définition

J'ai un document qui ressemble à ceci dans la structure avec un sous-document imbriqué

{  
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      },
      {  
         "plot":"circle",
         "color":"red",
      }
   ]
}

Et je veux récupérer tous les éléments correspondants dans fooArray dans ce document qui a un tracé circulaire. p >

C'est ce que j'ai essayé

{  
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      }
   ]
}

C'est ce que j'obtiens

var filter = FilterBuilder.filter.Eq(doc => doc.User, User);
var projection = ProjectionBuilder
                .Exclude(doc => doc.Id)
                .Exclude(doc => doc.User)
                .Include(doc => doc.FooArray)
                .ElemMatch(x => x.FooArray, y => y.Plot == "circle");

var definition = new OperationDefinitions<ShapeDocument> { Filter = filter };
            return await Performer.Perform(definition, async (def, collection) =>
            {
                var findResult = collection.Find(def.Filter).Project(projection);

                var result = await findResult.SingleOrDefaultAsync();
            });

Mais ça me donne seulement le premier élément correspondant au lieu de tous les éléments dont l'intrigue est égale à cercle

{  
   "_id":ObjectId("50419077c2e6a1e18a489a0f"),
   "user":"Jone Doe",
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      },
      {  
         "plot":"circle",
         "color":"red",
      },
      {  
         "plot":"square",
         "color":"green",
      }
   ]
}

J'ai lu la documentation de mongodb qui mentionne

"L'opérateur $ elemMatch limite le contenu d'un champ des résultats de la requête à ne contenir que le premier élément correspondant à la condition $ elemMatch. "

Je ne sais pas trop comment y parvenir!


0 commentaires

3 Réponses :


4
votes

La question ne décrit pas complètement le cas d'utilisation, j'ai donc proposé quelques options potentielles à explorer en fonction de quelques hypothèses, en particulier elles dépendent de la disponibilité de LINQ et du ciblage d'un seul document à l'adresse une fois (et que vous ne voulez probablement pas plus de code que ce dont vous avez vraiment besoin):

1) Une variation de ce que vous avez. Utilisez une find standard avec une projection et une expression LINQ.

var filter = new BsonDocument {
 {"input", "$items"},
 {"as", "item" },
 {"cond", new BsonDocument {
     // Fill in the condition values
     { "", new BsonArray { "", xxx } } }
   }
 };

var project = new BsonDocument {
 { "items", new BsonDocument { { "$filter", filter} } }
};

var pipeline = collection.Aggregate().Project(project);

2) Utilisez le pipeline d'agrégation (vous pouvez utiliser la même projection que ci-dessus)

var items3 = collection.AsQueryable()
    .SingleOrDefault(x => x.user == "Jone Doe")
    .fooArray.Where(x => x.plot == "circle");

3) Récupérez le document avec tous les éléments du tableau puis filtrez localement en utilisant LINQ. Sur le plan positif, il s'agit d'une petite quantité de code lisible, cependant, il ramène tout le document avant le filtrage. En fonction de votre utilisation exacte, cela peut être acceptable.

var pipeline = collection
    .Aggregate()
    .Match(x => x.user == "Jone Doe")
    .Project(i => new
            {
                x = i.fooArray.Where(x => x.plot == "circle")
            });

var items2 = pipeline.SingleOrDefault();

Si LINQ vraiment n'est pas une option, il existe un exemple ici qui montre comment vous pourriez convertir la projection pas nous LINQ. Totalement non testé mais serait quelque chose du genre:

var projection = Builders<ShapeDocument>.Projection
    .Expression(x => x.fooArray.Where(y => y.plot == "circle"));

var items1 = collection
    .Find(x => x.user == "Jone Doe")
    .Project(projection)
    .ToList();


0 commentaires

0
votes

voici une solution simple et douce utilisant MongoDB.Entities qui n'est qu'un wrapper bibliothèque pour le pilote c #.

  {
    "$match": {
      "Name": "Jone Doe"
    }
  },
  {
    "$unwind": "$Foos"
  },
  {
    "$project": {
      "Foos": "$Foos",
      "_id": 0
    }
  },
  {
    "$match": {
      "Foos.Plot": "circle"
    }
  }

voici la requête agrégée qu'elle génère:

using MongoDB.Driver.Linq;
using MongoDB.Entities;
using System.Linq;

namespace StackOverflow
{
    public class User : Entity
    {
        public string Name { get; set; }
        public Foo[] Foos { get; set; }
    }

    public class Foo
    {
        public string Plot { get; set; }
        public string Color { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            new DB("test");

            var user = new User
            {
                Name = "Jone Doe",
                Foos = new[]
                {
                    new Foo{ Plot = "circle", Color="yellow"},
                    new Foo{ Plot = "circle", Color="red"},
                    new Foo{ Plot = "square", Color="green"},
                }
            };

            user.Save();

            var circularFoos = DB.Collection<User>()
                                 .Where(u => u.Name == "Jone Doe")
                                 .SelectMany(u => u.Foos)
                                 .Where(f=>f.Plot=="circle").ToArray();
        }
    }
}


2 commentaires

Vous pouvez tout aussi bien faire cela sans cette bibliothèque en utilisant simplement IQueryable var circularFoos = collection.AsQueryable (). Where (u => u.user == "Jone Doe"). SelectMany (u => u. fooArray) .Where (f => f.plot == "circle"). ToArray (); comme l'a dit Greg Stanley.


vrai, mais une fois que je me suis habitué aux commodités offertes par cette bibliothèque, il n'y avait plus de retour en arrière. maintenant je ne toucherais pas le pilote officiel avec un poteau de 10 pieds ;-)



0
votes

J'ai trouvé une bonne façon de le faire

var filter = FilterBuilder.filter.Eq(doc => doc.User, User);
var definition = new OperationDefinitions<ShapeDocument> { Filter = filter };
return await Performer.Perform(definition, async (def, collection) =>
{
var findResult = collection.Find(def.Filter).Project(doc => doc.fooArray.Where(x => x.Plot == "Circle"));
var result = await findResult.SingleOrDefaultAsync();
}


0 commentaires