Linq

collinsauve edited this page Sep 24, 2010 · 3 revisions
Clone this wiki locally

Linq

Querying using linq will be most natural fit for .NET developers. It means that you won’t have to learn MongoDB’s query language unless you need some more advanced queries or fine tuning. For the best overview of the MongoDB query capabilities, you should reference the mongodb-docs.

Modes

There are 2 modes in which querying MongoDB can occur. Our linq provider will make the choice for you based on certain criteria.

Native Mode

Native mode will be used when your query only uses the supported (native) MongoDB operators. It allows MongoDB to make the best use of indexes so that your query performs well. As MongoDB supports more operators, we will also support them as well.

An example Native Mode query is below (using Typed Collections)

  var people = from p in collection.Linq()
               where p.Age >= 18 && p.FirstName.StartsWith("J")
               orderby p.FirstName
               select p.FirstName + " " + p.LastName;

Javascript Mode

It is very easy to push a query into Javascript Mode. Simply using a disjunction (or) would do the trick. MongoDB can make limited use of the indexes when Javascript Mode is used which results in “table” scans to grab the correct data. However, there can be advantages to using this mode if your query is complex and performance is unnecessary. See the mongodb-docs for more information.

An example Javascript Mode query is below (using Typed Collections)

  var people = from p in collection.Linq()
               where p.FirstName.StartsWith("J") || p.LastName.StartsWith("J")
               orderby p.FirstName
               select p.FirstName + " " + p.LastName;

In addition to disjunctions, any legal Javascript is allowed in a Javascript query. Therefore, addition, subtraction, etc…, left shifts, right shifts, etc… are all supported making it very powerful.

Supported Linq Operations

Not all Linq operations are supported. Currently, there is not support for GroupBy or aggregate operations (other than Count). We will be filling these holes in due time.

Filtering

Any combination of the supported operators are valid to remain in Native Mode. In addition, there are certain native .NET isms that are automatically translated into their appropriate MongoDB syntax.

Equals

x => x.Age == 21 will translate to {"Age": 21}

Greater Than, $gt:

x => x.Age > 18 will translate to {"Age": {$gt: "Bob"}}

Greater Than Or Equal, $gte:

x => x.Age >= 18 will translate to {"Age": {$gte: "Bob"}}

Less Than, $lt:

x => x.Age < 18 will translate to {"Age": {$lt: "Bob"}}

Less Than Or Equal, $lte:

x => x.Age <= 18 will translate to {"Age": {$lte: "Bob"}}

Not Equal, $ne:

x => x.Age != 18 will translate to {"Age": {$ne: "Bob"}}

Mod, $mod

Not currently supported.

Regular Expressions

All regular expressions must be in accordance with the Javascript regular expression syntax. There are some similarities to the .NET syntax, but if you find a query misbehaving, it might be that the syntax is incorrect. In any case, below is how MongoDB-CSharp supports regular expressions in Linq queries.

String.StartsWith

This type of regex query will allow mongodb to use an index.

x => x.FirstName.StartsWith("J") will translate to {"FirstName": /^J/}

String.Contains

x => x.FirstName.Contains("oe") will translate to {"FirstName": /oe/}

String.EndsWith

x=> x.FirstName.EndsWith("oe") will translate to {"FirstName": /oe$/}

Regex.IsMatch

x => Regex.IsMatch("^J.*e$", x.FirstName) will translate to {"FirstName": /^J.*e$/}

Other Query Operators

$in clause

Limited or support can be gained through the use of an $in clause. We support this through local collections.

  var firstNames = new[] { "Joe", "Bob" };
  var people = collection.Linq().Where(x => firstNames.Contains(x => x.FirstName));

This will translate to {"FirstName": {$in: [“Joe”, “Bob”]}}. Essentially, we are asking to find all the people whose FirstName is “Joe” or “Bob”.

$elemMatch

Matching on embedded collections can be done through the use of the Any operator. It will translate into an $elemMatch query.

  var people = collection.Linq().Where(x => x.Addresses.Any(a => a.City == "Paris"));

This will tranlate to {"Addresses": {$elemMatch: {"City": "Paris"}}}. Essentially, we are asking to find all the
people that have an address whose city is Paris.

$size

Size is supported by using the Count() extension method, the Count property on an ICollection instance, or the Length property on an Array.

x => x.Addresses.Count() == 4) would translate to {"Addresses": {$size: 4}}

Note that $size queries only allow the use of equality and no other operators.

List Indexers

If you want to query the 4th element in a list, MongoDB has support for this. Either use an indexer off IList, an indexer off an array, or the ElementAt extension method.

x => x.Addresses[4].City == "Paris" will translate to{"Addresses.4.City": "Paris"}.

All operators are supported here.

Projections

MongoDB-CSharp supports projections as completely as MongoDB supports them. That is to say that we can pull back only the subset of fields that are requested. In addition to that, it is possible to perform some client side manipulation of the results.

A few examples

...Linq().Select(x => x.FirstName + " " + x.LastName)

This will bring back only the FirstName and LastName fields from MongoDB and then concatenate them together client side, giving you a single string to work with.

A more complex example is below showing that derived fields will still perform correctly in the case where a linq query is composed in pieces. This example is a little trite, but it illustrates the point.

  var people = from p in collection.Linq()
               select new {Name = p.FirstName + " " + p.LastName, Age = p.Age};

  //other code

  var names = from p in people
              where p.Age >= 21
              select p.Name;

This query would result in a single query being issued to MongoDB for all people whose Age is greater than or equal to 21 and it would only pull back the FirstName and LastName fields. Client side, we’d stitch together the names, giving you a single string to work with.

OrderBy

Order by is supported, including direction. Simply specify the fields to order by and you are good to go.

  var people = from p in collection.Linq()
               orderby p.Age descending
               select p;

Aggregates

The Count aggregate is supported natively utilizing MongoDB. All other aggregates require the use of Map-Reduce.

Count

To use, simply use the Count linq operator as the last operator in the query. It is supported both with and without the predicate.

...Linq().Count(x => x.Age > 21)

Skip and Take

Paging can be accomplished using the skip and take operators.

...Linq().Skip(10).Take(10)