Query projection with MongoDB 10gen driver - c#

Recently i was playing with mongodb official driver.
The problem that i've encountered was how to make query projection.
Example if i have a persisted object
class A{
id
PropA
PropB
List<LargeObjects>
}
How can i only retrieve id, PropA and PropB instead of retrieving the whole object ?
How can be done with the mongodb official c# driver ?

Query projection is available through:
MongoCollection<>.Find().SetFields(include/exclude);

As of v1.8 of the official 10gen MongoDB C# driver, (and as Zambonilli eluded to in a previous answer) the Select linq operator will always be performed on the client side, not on the db server.
Documentation (also provided by Sunil Raj in a previous answer): http://docs.mongodb.org/ecosystem/tutorial/use-linq-queries-with-csharp-driver/
Almost half way down the page, under the "Select" linq query operator, is a large red box that reads:
Warning: Select does not result in fewer fields being returned from the server. The entire document is pulled back and passed to the native Select method. Therefore, the projection is performed client side.

You can use the below linq query for this:
//NB: Not tested
MongoCollection<BsonDocument> Acollection = _db.database.GetCollection<BsonDocument>("A");
var resultlist = (from k in Acollection.AsQueryable<A>()
select k.id,k.PropA,k.PropB);
More information on the linq queries can be found here:
http://www.mongodb.org/display/DOCS/CSharp+Driver+LINQ+Tutorial#CSharpDriverLINQTutorial-SupportedLINQqueryoperators

Take a look at FluentMongo:
https://github.com/craiggwilson/fluent-mongo/wiki/Linq
It is available on Nuget aswell, search for "fluentmongo"

Use FindAs<> () with a type that includes only the fields you want. See docs.
Add the [BsonIgnoreExtraElements] or the [BsonExtraElements] attribute on that class. See docs.

Using the Mongo profiler I was able to determine that sometimes the Linq results are projected on the client. Therefore it depends on what your client needs are. If you want to return a resulting document with partial data from the Mongo server then you'll want to use Marjan or Ian's answers. Otherwise, if you want to read the record and project it to a different data type then use Linq.

Related

Mongodb c# driver: view MQL bson query generated from linq

Using the latest version (2.14) is there any way to view the bson query document generated by a specific linq query?
I want to do this for two reasons:
debugging queries
copying them to run in another mongo client like compass
I know I can enable profiling, but I can't see any way to guarantee a query you find in the mongo log was generated by a specific line of code or query. Plus it's a bit long winded to do it via profiling.
You have 2 options to get MQL query from LINQ request:
Install lately released query analyzer. As I know it may not be 100% accurate if you use global static serialization configuration.
Configure CommandStartedEvent event subscriber and analyze Command document. Pay attention that you may need to remove some technical fields like $db (maybe few more) that might not be parsed by compass correctly, you will see it in the exception message if any.
#dododo answer is the right and best one, I'm just adding some code here which works for option 2:
var settings = MongoClientSettings.FromUrl(new MongoUrl(#"mongodb://localhost"));
settings.ClusterConfigurator = builder =>
{
builder.Subscribe<CommandStartedEvent>(x =>
{
var queryDocument = x.Command;
});
};
var client = new MongoClient(settings);

IQueryable Intersect is currently not supported

When I trying to do this
//data.Photos it's IEnumerable<Photo>. Comparer worked by Id.
List<Photo> inDb = db.Photos
.Intersect(data.Photos, new PhotoComparer())
.ToList();
I get an exception:
NotSupportedException: Could not parse expression
'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ReportViewer.Models.DbContexts.Photo]).Intersect(__p_0, __p_1)'
This overload of the method #x27;System.Linq.Queryable.Intersect' is currently not supported.
// This works
List<Photo> inDb = db.Photos
.ToList()
.Intersect(data.Photos, new PhotoComparer())
.ToList();
// But will it take a long time - or not ?
What did I need to use Intersect with IQueryable and IEnumerable collection?
Due to the "custom comparer", although it's functionality might be trivial, the framework is currently not able to translate your statement to SQL (which I suspect you are using).
Next, it seems that you have a in memory collection, on which you want to perform this intersect.
So if you're wondering about speed, in order to get it working you'll need to send your data to the database server, and based on the Id's retrieve your data.
So basically, you are looking for a way to perform an inner join, which would be the SQL equivalent of the intersect.
Which you could do with the flowing linq query:
//disclaimer: from the top of my head
var list= from dbPhoto in db.Photos
join dataPhoto in data.Photos on dbPhoto.Id equals dataPhoto.Id
select dbPhoto;
This will not work though, since as far as I know EF isn't able to perform an join against an in-memory dataset.
So, alternatively you could:
fetch the data as IEnumerable (but yes, you'll be retrieving the whole set first)
use a Contains, be carefull though, if you're not using primitive types this can translate to a bunch of SQL OR statements
But basically it depends on the amount of data you're querying. You might want to reconsider your setup and try to be able to query the data based on some ownership, like user or other means.

How to do a LINQ query for count of entity whose owner == current user?

I'm using Microsoft Dynamics CRM 2011 and ADX Studios. On one of the pages I'm trying to make a widget that will display the user's number of current leads. I want to do a LINQ query that selects the count of lead where the owner of the lead entity in the CRM database is equal to the current user. I'm new to LINQ, so I'm still trying to get a grasp on the semantics of the queries. Below I have my code so far which pulls in all leads. I'm not quite sure how to work in the "where" clause that checks to see if the owner id equals that of the current user.
count = context.LeadSet.ToList().Count();
See this answer:
Linq to CRM doesn't support any aggregate expressions, so it would be better from a performance standpoint to use Fetch XML.
One of the main issues was that the query also had to include the owner of the lead. This was a custom field added to the crm. Therefore the XRM file that is used by ADX Studios had to be rebuild with the CrmSvcUtil. I used the following batch file which can be altered depending on your project:
cd\"Program Files (x86)\Adxstudio\XrmPortals\6.0.0009\Framework"
CrmSvcUtil.exe /codeCustomization:"Microsoft.Xrm.Client.CodeGeneration.CodeCustomization, Microsoft.Xrm.Client.CodeGeneration" /url:https://contoso.com/XRMServices/2011/Organization.svc /username:user /password:password /out:"C:\Xrm.cs" /namespace:Xrm /servicecontextprefix:Xrm /servicecontextname:XrmServiceContext
pause
This must be run as administrator to complete making the new XRM File. That file must then be copied and pasted into the ADX Studio solution. Rebuild the solution and your custom fields will now be used by intellisense when you do your query.
var context = new XrmServiceContext();
var leadList = (from a in context.LeadSet
where a.customFieldOwnerId.Id == Id
select a).ToList().Count();
int count = leadList;
To get a count when using Microsoft Dynamics CRM you must use .ToList() before .Count().
It may seem redundant, but you will get an error if you do not.
I'm not sure your the data structure of LeadSet but i'd imagine something like:
context.LeadSet.Count(ls => ls.OwnerID == ownerId);
This above pulls the count of all leads where the OwnerId equals your parameter ownerId.
If you want the objects that match, just use .Where in place of .Count
try this using lambda expression
int count = context.LeadSet.Where(a=>a.currentUserName==currentUserName").Count();
or linq query syntax
int count=(from item in context.LeadSet.ToList()
where item.currentUserName==currentUserName
select item).count();

ASP.NET MVC Search Page - Integer StartsWith On Linq + EF4

So, in my last post I was asking how to build a dynamic search filter using LINQ and EF4 (See Here) and finally came up with the solution of building the expression as a string and parse it to an expression using the Dynamic LINQ library.
I that solved the problem. I was able to generate a Expression<Func<TSource, out bool>> and pass it to the Where() method of the DbSet. I am also trying to do this using MySql as a database behind EF4.
The problem came when I tried to apply string operations to integers, like searching a database record which consecutive number starts with 1234.
My initial expression was something like: record.ConsecutiveNumber.ToString().StartsWith("1234"). Sadly, as expected, things were not that easy as EF4 fails to query the DbSet with exception:
"LINQ to Entities does not recognize
the method 'System.String ToString()'
method, and this method cannot be
translated into a store expression."
After some Google search I found that this is a common problem. But C'MON! Is there a way to perform a search function that can search records with a consecutive number starting by "1234"?
How pros implement search features with EF4? This is with a single property filter. What if I wanna add multiple filters? God, my head hurts... :/
Thanks!
EDIT:
Thought #1: What about a stored procedure? What about calling a MySql stored procedure from Linq? Am I aiming way too high?
You can use the SqlFunctions.StringConvert method. It requires a double (or decimal) so you'll have to cast your int ConsecutiveNumber.
Replace:
record.ConsecutiveNumber.ToString().StartsWith("1234")
With:
SqlFunctions.StringConvert((double)record.ConsecutiveNumber).StartsWith("1234")
Have you looked at the Dynamic LinQ Library:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
And for your question
How to use "contains" or "like" in a dynamic linq query?
Previously I have gotten the code for this lib and just taken a look inside, it is pretty easy to follow.
This would be my thought process on getting it to work. Hopefully it points you in the right direction.
According to other posts SqlFunctions.StringConvert((double)record.ConsecutiveNumber) works for Sql Server.
Problem with converting int to string in Linq to entities
And here is relevant information on linq conversions.
Linq int to string
And here is an answer hinting at writing your own sql function for stringconvert
Using a SQL Function in Entity Framework Select
If SqlFunctions.StringConvert doesn't work for you I'd suggest looking at figuring out how to do it in Sql and then writing your own [EdmFunction()] attribute based method.
I haven't got a clue if this will work over Linq to EF or not but presuming that they mapped the Math operations, this might solve your need:
record.ConsecutiveNumber / Math.Pow(10, Math.Truncate(Math.Log10(record.ConsecutiveNumber) - 3)) == 1234
This is basically dividing the number by a power of 10 just big enough to leave the first 4 digits.
I know this is very hacky and inefficient even if it works, but there you go. :)
Any method calls in a LINQ to Entities query that are not explicitly mapped to a canonical function will result in a runtime NotSupportedException exception being thrown.
Check mapping canonical function here:
http://msdn.microsoft.com/en-us/library/bb738681.aspx
In this case, you can use Math function. (I don't think code first can use in product project at that time)

Does LINQ to SQL support the t-sql "in" statement

I dont't know how to describe the title better (so feel free to change) but essntial I want to produce the equivilent of the following statement in LINQ to SQL:
select * from products where category in ('shoes','boots')
The fields will be coming from the query string essentially (more secure than this but for ease of explanation) i.e
string[] myfields = request.querystring["categories"].split(',');
Thanks
Yes, you use Contains:
db.Products.Where(product => myFields.Contains(product.Category))
aka
from product in db.Products
where myFields.Contains(product.Category)
select product
As other have mentioned, yes it does using the .Contains method. To benefit the other random people that may arrive here via Bing (or any of the other search engines): Linq-To-Entities does not support the .Contains method in the current version. However, with a simple extension method, you can do so:
http://george.tsiokos.com/posts/2007/11/30/linq-where-x-in/
In theory you would use contains:
var productList = from product in context.Products
where myfields.contains(product.category)
select product
However you'll need to test this - I seem to recall there being a bug in the Linq2Sql optimizer when trying to deal with List<> and array values being used like this (it may have only occured if you tried to cast them as IQueryable)

Categories

Resources