# Array operators

## Overview

There are many times when you want to match documents based on array field values. Luckily, MongoDB and the C# driver provide the following 3 array query operators that help you build array based queries.

| Operator      | Description                                                          |
| ------------- | -------------------------------------------------------------------- |
| **Size**      | Match documents based on the array's size                            |
| **ElemMatch** | Match documents when array's elements match specified conditions     |
| **All**       | Match documents when all specified values are contained in the array |

{% hint style="info" %}
These operators may seem simple at first :innocent: , but when combined with other MongoDB features such as **projection** or **unwind**, you will find that you can build quite complex queries! :muscle:&#x20;
{% endhint %}

> Other than the operators themselves, you can create array field base queries using lambda expressions and the methods provided by `Enumerable`, such as `Enumerable.Any`

![Array operators](/files/-M4u2Hzk5qXndpxR9Ivw)

## *Size* operator - *$size*

The *$size* operator is applied on array fields and matches documents when an array has a specific number of elements. MongoDB C# driver, doesn't contain a dedicated method for the *$size* operator but can resolve it from the `Count`property or `Count()` method of `IEnumerable`types.

The following sample finds:

* &#x20;`Traveler` documents having *VisitedCountries* array with exact 5 elements&#x20;
* `Traveler` documents having *VisitedCountries* array with more than 10 elements&#x20;

{% tabs %}
{% tab title="C#" %}
{% code title="ArrayOperators.cs" %}

```csharp
var collection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

var fiveVisitedCountriesFilter = await collection
    .Find(t => t.VisitedCountries.Count == 5).ToListAsync();
    
var moreThan10VisitedCountries = await collection
    .Find(t => t.VisitedCountries.Count > 10).ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
    .GetCollection<BsonDocument>(Constants.TravelersCollection);

// exactly 5
var bsonFiveVisitedCountriesFilter = await bsonCollection.Find(
    new BsonDocument
    { 
        {"visitedCountries", new BsonDocument {{ "$size", 5 } }}
    }).ToListAsync();

// more than 10
var bsonMoreThan10VisitedCountries = await bsonCollection
    .Find(new BsonDocument
    {
        {"visitedCountries.10", new BsonDocument {{ "$exists", true } }}
    }).ToListAsync();
```

{% endtab %}

{% tab title="Shell" %}

```javascript
// exactly 5
db.travelers.find({ visitedCountries : { $size: 5 } })

// more than 10
db.travelers
	.find({ "visitedCountries.10" : { "$exists" : true } })

--------------------------- 
        
// sample matched document (5 visited countries)
{
	"_id" : ObjectId("5e9621f95cb0474d40845dab"),
	"name" : "Bobbie Murazik",
	"age" : 45,
	"activities" : [
		"Photography",
		"Hacking",
		"Wine tourism",
		"Wildlife watching"
	],
	"visitedCountries" : [
		{
			"name" : "Chad",
			"timesVisited" : 4,
			"lastDateVisited" : ISODate("2016-08-17T16:52:59.566+03:00"),
			"coordinates" : {
				"latitude" : -34.2869,
				"longitude" : 134.3729
			}
		},
		{
			"name" : "Syrian Arab Republic",
			"timesVisited" : 4,
			"lastDateVisited" : ISODate("2018-03-09T19:55:12.084+02:00"),
			"coordinates" : {
				"latitude" : -59.1648,
				"longitude" : -163.6818
			}
		},
		{
			"name" : "Guadeloupe",
			"timesVisited" : 6,
			"lastDateVisited" : ISODate("2018-11-02T02:23:55.274+02:00"),
			"coordinates" : {
				"latitude" : -23.8004,
				"longitude" : -169.4041
			}
		},
		{
			"name" : "Bhutan",
			"timesVisited" : 10,
			"lastDateVisited" : ISODate("2018-02-25T13:23:50.974+02:00"),
			"coordinates" : {
				"latitude" : 67.0804,
				"longitude" : -125.1589
			}
		},
		{
			"name" : "Somalia",
			"timesVisited" : 7,
			"lastDateVisited" : ISODate("2020-01-02T15:48:37.721+02:00"),
			"coordinates" : {
				"latitude" : 61.3348,
				"longitude" : -83.9703
			}
		}
	]
}
```

{% endtab %}

{% tab title="Models" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}

public class VisitedCountry
{
    public string Name { get; set; }
    public int TimesVisited { get; set; }
    public DateTime LastDateVisited { get; set; }
    public GeoLocation Coordinates { get; set; }
}

public class GeoLocation
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Notice that using the `BsonDocument` approach, you need to write the exact query you would write in the shell.&#x20;

```javascript
db.travelers
	.find({ "visitedCountries.10" : { "$exists" : true } })
```

:man\_mage: This uses the `<array>.<index>` notation to check if the array contains an element at *11th* position, which would also mean that has more than 10 documents
{% endhint %}

## *ElemMatch* operator - *$elemMatch*

The *$elemMatch* operator is used to match elements inside array fields based on one or more criteria.

{% tabs %}
{% tab title="Syntax" %}

```csharp
Builders<T>.Filter
    .ElemMatch(doc => doc.<array-field>,<expressions>[])
```

{% endtab %}
{% endtabs %}

The sample filters `Traveler` documents that their *VisitedCountries* array field contains a `VisitedCountry` element with name *Greece* and *TimesVisited = 3*.&#x20;

{% tabs %}
{% tab title="C#" %}
{% code title="ArrayOperators.cs" %}

```csharp
var collection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

var visitedGreeceExactly3Times = Builders<Traveler>.Filter
    .ElemMatch(t => t.VisitedCountries,
    country => country.Name == "Greece" 
        && country.TimesVisited == 3);

var visitedGreeceExactly3TimesTravelers = await collection
    .Find(visitedGreeceExactly3Times).ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
    .GetCollection<BsonDocument>(Constants.TravelersCollection);

var bsonVisitedGreeceExactly3Times = Builders<BsonDocument>.Filter
    .ElemMatch<BsonValue>("visitedCountries", 
        new BsonDocument { { "name", "Greece" }, 
                            { "timesVisited", 3 } });

var bsonVisitedGreeceExactly3TimesTravelers = await bsonCollection
    .Find(bsonVisitedGreeceExactly3Times).ToListAsync();
```

{% endtab %}

{% tab title="Shell" %}

```javascript
db.travelers.find({
    visitedCountries: {
        $elemMatch: {
            name : "Greece",
            timesVisited: 3
        }
    }})
    
---------------------------
// sample result

{
	"_id" : ObjectId("5e96c32ad9d34468c0a772b2"),
	"name" : "Ari Hessel",
	"age" : 30,
	"activities" : [
		"Wine tourism",
		"Blogging",
		"Scuba diving",
		"Golf",
		"Photography"
	],
	"visitedCountries" : [
		{
			"name" : "Greece", // match here
			"timesVisited" : 3, // match here
			"lastDateVisited" : ISODate("2020-02-20T08:18:31.292+02:00"),
			"coordinates" : {
				"latitude" : 61.2458,
				"longitude" : -131.4814
			}
		},
		{
			"name" : "Italy",
			"timesVisited" : 1,
			"lastDateVisited" : ISODate("2018-11-26T02:42:05.395+02:00"),
			"coordinates" : {
				"latitude" : -17.3031,
				"longitude" : -171.7654
			}
		}
	]
}
```

{% endtab %}

{% tab title="Models" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}

public class VisitedCountry
{
    public string Name { get; set; }
    public int TimesVisited { get; set; }
    public DateTime LastDateVisited { get; set; }
    public GeoLocation Coordinates { get; set; }
}

public class GeoLocation
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="danger" %}
You might be temped to match array elements using the **$and** operator as follow:

{% code title="ArrayOperators.cs" %}

```csharp
var greeceVisitedFilter = Builders<Traveler>.Filter
    .AnyEq("visitedCountries.name", "Greece");

var visitedTimesFilter = Builders<Traveler>.Filter
    .AnyEq("visitedCountries.timesVisited", 3);

// shell
db.travelers.find({$and: [ 
    {"visitedCountries.name" : "Greece"}, 
    {"visitedCountries.timesVisited":3}])
```

{% endcode %}

This is **wrong** because it doesn't apply the criteria on each array element at a time but at all elements. This means that it might match documents that indeed  contain a visited country with name "*Greece*" which hasn't *TimesVisited = 3*, but a document matched because it also contains another visited country, *e.g. Italy* with *TimesVisited = 3*.
{% endhint %}

The following sample filters `Traveler` documents that their *VisitedCountries* array field contains a `VisitedCountry` element *TimesVisited = 3* but this time, the country's name can be either *Greece* or *Italy*.&#x20;

{% tabs %}
{% tab title="C#" %}
{% code title="ArrayOperators.cs" %}

```csharp
var collection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

// filter on country name
var countryNameFilter = Builders<VisitedCountry>.Filter
    .In(c => c.Name, new[] {"Greece", "Italy"});

// filter on times visited  
var countryTimesVisitedFilter = Builders<VisitedCountry>.Filter
    .Eq(c => c.TimesVisited, 3);

var visitedGreeceOrItalyExactly3Times = Builders<Traveler>.Filter
    .ElemMatch(t => t.VisitedCountries,
            Builders<VisitedCountry>.Filter
            .And(countryNameFilter, countryTimesVisitedFilter));
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
    .GetCollection<BsonDocument>(Constants.TravelersCollection);

var bsonVisitedGreeceOrItalyExactly3Times = Builders<BsonDocument>.Filter
    .ElemMatch<BsonValue>("visitedCountries", new BsonDocument
    {
        { "name", new BsonDocument("$in", 
            new BsonArray { "Greece", "Italy" }) }, 
        { "timesVisited", 3 }
    });
```

{% endtab %}

{% tab title="Shell" %}

```javascript
db.travelers.find(
            {
                visitedCountries: {
                    $elemMatch: {
                        name : { $in: [ "Greece", "Italy" ] },
                        timesVisited: 3
                    }
                }
            })
    
---------------------------
// sample result

{
	"_id" : ObjectId("5e96dd21e8f8181a3cc1675b"),
	"name" : "Chyna Haag",
	"age" : 27,
	"activities" : [
		"Canyoning",
		"Wildlife watching",
		"Hacking",
		"Horseback riding"
	],
	"visitedCountries" : [
		{
			"name" : "Greece",
			"timesVisited" : 5,
			"lastDateVisited" : ISODate("2019-04-07T18:10:31.513+03:00"),
			"coordinates" : {
				"latitude" : 49.2476,
				"longitude" : 82.7277
			}
		},
		{
			"name" : "Italy", // matched here
			"timesVisited" : 3, // matched here
			"lastDateVisited" : ISODate("2015-07-30T23:26:05.754+03:00"),
			"coordinates" : {
				"latitude" : -70.8608,
				"longitude" : -128.5726
			}
		}
	]
}
```

{% endtab %}

{% tab title="Models" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}

public class VisitedCountry
{
    public string Name { get; set; }
    public int TimesVisited { get; set; }
    public DateTime LastDateVisited { get; set; }
    public GeoLocation Coordinates { get; set; }
}

public class GeoLocation
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}
```

{% endtab %}
{% endtabs %}

### *Enumerable.Any - AnyEq*

To check if an array field contains a specified value you can use the `Enumerable.Any` or the `FilterDefinitionBuilder<T>.AnyEq` methods.

The sample finds the `Traveler` documents where *"Greece"* is contained in the *VisitedCountries* array field.

{% tabs %}
{% tab title="C#" %}
{% code title="ArrayOperators.cs" %}

```csharp
var collection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

var greeceTravelers = await collection
    .Find(t => t.VisitedCountries
        .Any(c => c.Name == "Greece")).ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
            .GetCollection<BsonDocument>(Constants.TravelersCollection);

var bsonGreeceVisitedFilter = Builders<BsonDocument>.Filter
            .AnyEq("visitedCountries.name", "Greece");
            
var bsonGreeceTravelers = await bsonCollection
            .Find(bsonGreeceVisitedFilter).ToListAsync();
```

{% endtab %}

{% tab title="Traveler" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}
```

{% endtab %}
{% endtabs %}

You can go further, and add an **||** operator in the `Any` method. This will combine $el*emMatch* and $in operators to build the query.

{% tabs %}
{% tab title="C#" %}

```csharp
var collection = database
  .GetCollection<Traveler>(Constants.TravelersCollection);

var greeceItalyTravelers = await collection
  .Find(t => t.VisitedCountries
      .Any(c => c.Name == "Greece" || c.Name == "Italy")).ToListAsync();
```

{% endtab %}

{% tab title="Shell" %}

```javascript
db.travelers.find(
{
    visitedCountries: {
        $elemMatch: {
            name : { $in: [ "Greece", "Italy" ] }
        }
    }
}).count()

---------------------

// sample matched result
{
	"_id" : ObjectId("5e973a0beaaccd581ce6c752"),
	"name" : "Gregg Wyman",
	"age" : 55,
	"activities" : [
		"Orienteering",
		"Baking"
	],
	"visitedCountries" : [
		{
			"name" : "Italy", // matched here
			"timesVisited" : 10,
			"lastDateVisited" : ISODate("2019-06-03T13:09:08.540+03:00"),
			"coordinates" : {
				"latitude" : 56.967,
				"longitude" : -156.941
			}
		},
		{
			"name" : "Thailand",
			"timesVisited" : 7,
			"lastDateVisited" : ISODate("2018-02-05T13:53:00.552+02:00"),
			"coordinates" : {
				"latitude" : -38.2652,
				"longitude" : -159.6134
			}
		}
	]
}
```

{% endtab %}

{% tab title="Traveler" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}
```

{% endtab %}
{% endtabs %}

## *All* operator - *$all*

The *$all* operator is applied on array fields and matches documents when the array field **contains all** the items specified. You use the *All* operator when you want to ensure that an array contains *(or doesn't)* a list of values.

{% tabs %}
{% tab title="Syntax" %}

```csharp
Builders<T>.Filter
    .All(doc => doc.<array-field>,<values>[])
```

{% endtab %}
{% endtabs %}

The sample finds all `Traveler` documents having *"Backpacking"* and *"Climbing"* values on their *Activities* list. *Activities* is an array of **string** values.

{% tabs %}
{% tab title="C#" %}
{% code title="ArrayOperators.cs" %}

```csharp
var collection = database
    .GetCollection<Traveler>(Constants.TravelersCollection);

var climbingAndBackpackingFilter = Builders<Traveler>.Filter
    .All(t => t.Activities, 
        new List<string> { "Backpacking", "Climbing" });

var climbingAndBackpackingTravelers = await collection
    .Find(climbingAndBackpackingFilter).ToListAsync();
```

{% endcode %}
{% endtab %}

{% tab title="Bson" %}

```csharp
var bsonCollection = database
    .GetCollection<BsonDocument>(Constants.TravelersCollection);

var bsonClimbingAndBackpackingFilter = Builders<BsonDocument>.Filter
    .All("activities", new List<string> { "Backpacking", "Climbing" });

var bsonClimbingAndBackpackingTravelers = await bsonCollection
    .Find(bsonClimbingAndBackpackingFilter).ToListAsync();
```

{% endtab %}

{% tab title="Shell" %}

```javascript
db.travelers.find({ activities: 
    { $all : [ "Climbing", "Backpacking" ] } }

------------------------

// sample result
{
	"_id" : ObjectId("5e9733b056412635045cad72"),
	"name" : "Marcella Thiel",
	"age" : 61,
	"activities" : [
		"Scuba diving",
		"Canyoning",
		"Wine tourism",
		"Hacking",
		"Orienteering",
		"Blogging",
		"Backpacking", // matche
		"Climbing", // match
		"Reading",
		"Running",
		"Bowling",
		"Collecting",
		"Horseback riding"
	],
	"visitedCountries" : [
		{
			"name" : "Netherlands",
			"timesVisited" : 10,
			"lastDateVisited" : ISODate("2018-04-13T07:14:40.942+03:00"),
			"coordinates" : {
				"latitude" : 11.9843,
				"longitude" : 79.5859
			}
		}
	]
}
```

{% endtab %}

{% tab title="Traveler" %}

```csharp
public class Traveler
{
    [BsonId]
    public ObjectId Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public List<string> Activities { get; set; }
    public List<VisitedCountry> VisitedCountries { get; set; }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
The order of the array values passed in the **`All`** method doesn't matter, in the same way it doesn't matter when writing the query in the shell with the *$all* operator
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chsakell.gitbook.io/mongodb-csharp-docs/crud-basics/read-documents/array-operators.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
