web-dev-qa-db-fra.com

MongoDb c # driver find item in array by field value

j'ai trouvé le moyen de vérifier si la valeur contient dans un tableau simple:

var filter = Builders<Post>.Filter.AnyEq(x => x.Tags, "mongodb");

Mais comment trouver un élément complexe avec plusieurs champs par un champ concret? J'ai trouvé le moyen de l'écrire via l'approche de la notation par points avec le générateur BsonDocument, mais comment puis-je le faire avec des notations lambda typées?

mise à jour

je pense que c'est une sorte de

builderInst.AnyIn(p => p.ComplexCollection.Select(ml => ml.Id), mlIds)

mais je ne peux pas vérifier en ce moment, est-ce que quelqu'un pourrait aider?

25
Vladislav Furdak

Il y a ElemMatch

var filter = Builders<Post>.Filter.ElemMatch(x => x.Tags, x => x.Name == "test");
var res = await collection.Find(filter).ToListAsync()
34
rnofenko

Vous avez besoin du $elemMatch opérateur. Vous pouvez utiliser Builders<T>.Filter.ElemMatch ou une expression Any:

Find(x => x.Tags.Any(t => t.Name == "test")).ToListAsync()

http://mongodb.github.io/mongo-csharp-driver/2.0/reference/driver/expressions/#elemmatch

5
hansmaad

Voici un exemple qui renvoie un seul élément complexe à partir d'un tableau (à l'aide de MongoDB.Driver v2.5.0):

modèle de données simple

public class Zoo
{
    public List<Animal> Animals { get; set; }
}

public class Animal
{
    public string Name { get; set; }
}

Option 1 (agrégation)

public Animal FindAnimalInZoo(string animalName)
{
    var zooWithAnimalFilter = Builders<Zoo>.Filter
        .ElemMatch(z => z.Animals, a => a.Name == animalName);

    return _db.GetCollection<Zoo>("zoos").Aggregate()
        .Match(zooWithAnimalFilter)
        .Project<Animal>(
            Builders<Zoo>.Projection.Expression<Animal>(z => 
                z.Animals.FirstOrDefault(a => a.Name == animalName)))
        .FirstOrDefault(); // or .ToList() to return multiple
}

Option 2 (Filter & Linq) C'était environ 5 fois plus lent pour moi

public Animal FindAnimalInZoo(string animalName)
{
    // Same as above
    var zooWithAnimalFilter = Builders<Zoo>.Filter
        .ElemMatch(z => z.Animals, a => a.Name == animalName);

    var zooWithAnimal = _db.GetCollection<Zoo>("zoos")
        .Find(zooWithAnimalFilter)
        .FirstOrDefault();

    return zooWithAnimal.Animals.FirstOrDefault(a => a.Name == animalName);
}
4
JBond

Depuis la version 2.4.2 des pilotes C #, l'interface IFindFluent peut être utilisée pour interroger l'élément de tableau. ElemMatch ne peut pas être utilisé directement sur un tableau de chaînes, alors que l'interface de recherche fonctionnera sur des types simples ou complexes (par exemple 'Tags.Name') et est fortement typée.

            FilterDefinitionBuilder<Post> tcBuilder = Builders<Post>.Filter;
            FilterDefinition<Post> tcFilter = tcBuilder.Eq("Tags","mongodb") & tcBuilder.Eq("Tags","asp.net");
               ...
            await myCollection.FindAsync(tcFilter);

Le pilote Linq utilise le cadre d'agrégation, mais pour une requête sans opérateur d'agrégation, la recherche est plus rapide.

Notez que cela a été brisé dans les versions précédentes du pilote de sorte que la réponse n'était pas disponible au moment de la publication d'origine.

2
Aaron Newman