Mongodb c# Conditions in query LINQ - c#

My query look like this:
var query = from p in collection
where p.MinStockQuantity >= p.StockQuantity
select p;
I can't run because I have exception: Unsupported filter: ([MinStockQuantity] >= [StockQuantity])
This query also does not work, the same bug.
var collection = database.GetCollection<Product>("Product");
var builder = Builders<Product>.Filter;
var filter = builder.Where(o => o.MinStockQuantity > o.StockQuantity);
var query = collection.Find(filter).ToListAsync().Result;
How can I compare 2 fields ?

I know I am quite late but this works perfectly,try this :
var collection = database.GetCollection<Product>("Product");
var builder = Builders<Product>.Filter;
var filter = builder.Gt(o => o.MinStockQuantity , o.StockQuantity);
var query = collection.Find(filter).ToListAsync().Result;
Gt here is greater than.
there are various other methods like Gte =greater than equal to etc .

So my previous answer was obviously wrong. You shoud use that query
db.products.find({ $where: "this.MinStockQuantity > this.StockQuantity" })
To run this query in c# world you need to use BSON documents:
var doc = MongoDB.Bson.Serialization.BsonSerializer
.Deserialize<BsonDocument>
("{$where: \"this.MinStockQuantity > this.StockQuantity\"}");
var result = collection.Find(new CommandDocument(doc));
I used that query in my test application and it yells proper results.

Related

C# linq operator as variable

(In theory) We have the following query:
var variableDate = DateTime.Parse("Nov 2, 2021")
var results = (from x in db.FooBar
where x.Date == variableDate
select x).ToList();
We can simply modify the variableDate to 11/3/21, 11/4/21, etc. to return results for different dates.
The question is can we use this same query to return all results after variableDate with some modification to the operator(s)/variable(s)? For example including the operator (> or ==) within variableDate (let's call it variableDateFormula):
> 11/2/2021
or
== 11/2/2021
This way we can simply call the same query with the same variable to return results for different operators:
var results = (from x in db.FooBar
where x.Date variableDateFormula
select x).ToList();
I know the suggestion above will not work, it is just for visualization. I have a query that takes up 9 lines and has 8 different iterations depending on 3 values passed into the function. I would love to avoid writing basically the same query 8 times and taking up ~70 lines, and instead dynamically change the operators/criteria to have only 1 query.
Yes, the operator is just syntactic sugar for a function call and you can change that function, for example with a lambda:
var equalFunc = ((d1,d2) => d1 == d2);
var greaterFunc = ((d1,d2) => DateTime.Compare(d1, d2) > 0);
var actualFunc = greaterFunc; // Insert logic here to choose the appropriate function
Then your select becomes
from x in db.FooBar
where actualFunc(x.Date, variableDate)
select x
Yes. The LINQ is just building an ExpressionTree that Entity Framework translates in to SQL, so you can use other expressions or lambdas as parameters that get inserted into the tree, like in this LINQpad example:
void Main()
{
var aDate = new DateTime(2002, 1, 1);
GetResults(d => d.TheDate == aDate).Dump();
GetResults(d => d.TheDate <= aDate).Dump();
GetResults(d => d.TheDate > aDate).Dump();
}
IEnumerable<Data> GetResults(Func<Data, bool> op)
{
var data = new List<Data>
{
new Data{TheDate = new DateTime(2000,1,1)},
new Data{TheDate = new DateTime(2001,1,1)},
new Data{TheDate = new DateTime(2002,1,1)},
new Data{TheDate = new DateTime(2003,1,1)},
};
return data.Where(d => op(d));
}
public class Data
{
public DateTime TheDate { get; set; }
}
Which produces the following results:
If you are doing more complex logic in your expressions, the EF Database Provider may not be able to translate the expression to SQL though. There will be limitations. You couldn't for example, do this:
GetResults(d => d.TheDate.ToString().Reverse() == "1234");
because it wouldn't understand the custom Reverse extension method.
I would suggest to use LINQKit for such task. EF Core cannot translate local variable as expression function.
var variableDate = DateTime.Parse("Nov 2, 2021");
Expression<Func<DateTime, DateTime, bool>> compareFunc = (d1, d2) => d1 > d2;
var results = (from x in db.FooBar
where compareFunc.Invoke(x.Date, variableDate)
select x).ToList();
For enabling LINQKit for EF Core add the following to the options:
builder
.UseSqlServer(connectionString)
.WithExpressionExpanding(); // enabling LINQKit extension
Or if you use other LINQ provider, add AsExpandable() at the top of the query:
var results = (from x in db.FooBar.AsExpandable()
where compareFunc.Invoke(x.Date, variableDate)
select x).ToList();

MongoDB .NET Driver - StartsWith & Contains with loosely typed data

I can use the following for exact matches on loosely typed data in MongoDB:
var mongoClient = new MongoClient(con);
IMongoDatabase mongoDatabase = mongoClient.GetDatabase("mydb");
var profile = mongoDatabase.GetCollection<BsonDocument>("profiles");
var query = profile.AsQueryable();
var results = query.Where(x => x["first_name"] == "john").Take(10);
But how do I use the same approach to do StartsWith and Contains?
I tried:
var results = query.Where(x => x["first_name"].AsString.Contains("john")).Take(10);
But I get the error:
Method 'Boolean Contains(System.String)' declared on type 'System.String' cannot be called with an instance of type 'MongoDB.Bson.BsonValue'
How do I use these filters?
If you cast to string instead of using AsString, it should work:
var results = query.Where(x => ((string) x["first_name"]).Contains("john")).Take(10);
MongoDB .NET Driver provides LinqExtensions.Inject that you can inject FilterDefinition into a LINQ where clause.
Start with Filter
using MongoDB.Driver.Linq;
var filter = Builders<BsonDocument>
.Filter
.Regex("first_name", "^" + "john" + ".*");
var results = query.Where(x => filter.Inject())
.Take(10);
Contains Filter
using MongoDB.Driver.Linq;
var filter = Builders<BsonDocument>
.Filter
.Regex("first_name", "john");
var results = query.Where(x => filter.Inject())
.Take(10);

C# Linq Query Bad Translates to the RQL in RavanDB

I have an old project that was developed by the .Net framework and RavenDB 3 ...
I'm trying to migrate the RavenDB to the 5.* version
there is weird behavior in translate a Linq query to the RQL query
The below codes in c#
var query = documentSession
.Query<Documents_ByOrgan.Result, Documents_ByOrgan>()
.Where(x => x.OrganisationId == request.OrganisationId);
query = (from result in query
let onboarding = RavenQuery.Load<IOnboarding>(result.OnboardingDocumentId)
let onboradGroup = RavenQuery.Load<IGroup>(onboarding.Group.DocumentId)
let invitedBy = RavenQuery.Load<User>(onboarding.InvitedByUser.DocumentId)
let siteDoc = RavenQuery.Load<Site>(((Team)onboradGroup).Site.DocumentId)
select new
{
OnboardingId = onboarding.Id,
...
}).ProjectInto<T>();
Translates to the RQL in this way :
declare function output(result, onboarding) {
var onboradGroup = load(id(onboarding.Group));
var invitedBy = load(id(onboarding.InvitedByUser));
var siteDoc = load(onboradGroup.Site.DocumentId);
return { ... };
}
from index 'Onboardings/All' as result
result.OnboardingDocumentId as onboarding
select output(result, onboarding)
Why
let siteDoc = RavenQuery.Load<Site>(onboradGroup.Site.DocumentId)
translates to :
var siteDoc = load ( onboradGroup.Site.DocumentId );
BUT
RavenQuery.Load<IGroup>(onboarding.Group.DocumentId)
translates to the:
var onboradGroup = load ( id ( onboarding.Group ) );
how can I prevent from generating id(..) in RQL?
id(..) doesn't load any document
there is a lot of these queries in my project and all of them works fine with the 3.x version
In addition, I have the below config :
documentStore.Conventions.FindIdentityProperty = memberInfo => memberInfo.Name == "DocumentId";
there is a discussion here that helps me to find the problem
This seems to be related:
https://github.com/ravendb/ravendb/pull/12475
It was released in 4.2.116/5.2.2.

MongoDB C# Driver query always returns null

I'm trying to make a simple query to mongoDB using C# Driver.
I've been trying a lot of things and then I came to this but it Always returns null.
Does anyone know if what could it be?
IMongoCollection<BsonDocument> users = Utilities.getCicCollection("Users");
var builder = Builders<BsonDocument>.Filter;
var filter= builder.Eq("Username", "test#test.it") & builder.Eq("Password", "testing");
var result = users.Find(filter).FirstOrDefault();
return (result!=null?true:false);
Use the standard snippet
var collection = _database.GetCollection<BsonDocument>("Users");
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Eq("Username", "test#test.it") & builder.Eq("Password", "testing");
var list = await collection.Find(filter).ToListAsync()
var result = list.FirstOrDefault();

Is there an "Explain Query" for MongoDB Linq?

Is there a way to run .explain() or equivalent on Linq queries? I would want to know
The text of the actual JSON query
The output of .explain() (indexes used, etc)
It would also be nice to have the execution time of the query
You can get the Json easily enough if you have a query wrapper;
var qLinq = Query<T>.Where(x => x.name=="jim");
Console.WriteLine(qLinq.ToJson());
There's also an Explain() method on MongoCursor, so you could do this;
var exp = Collection.FindAs<T>(qLinq).Explain()
Console.WriteLine(exp.ToJson());
So if you want the time taken, "millis" is in there;
var msTaken = exp.First(x => x.Name == "millis").Value.AsInt32;
If you have an IQueryable, try something like this;
void Do(MongoCollection col, IQueryable iq)
{
// Json Mongo Query
var imq = (iq as MongoQueryable<Blob>).GetMongoQuery();
Console.WriteLine(imq.ToString());
// you could also just do;
// var cursor = col.FindAs(typeof(Blob), imq);
var cursor = MongoCursor.Create(typeof(Blob), col, imq, ReadPreference.Nearest);
var explainDoc = cursor.Explain();
Console.WriteLine(explainDoc);
}//Do()
If you want this functionality in a library, I just created a GitHub project entitled
MongoDB query helper for .NET
https://github.com/mikeckennedy/mongodb-query-helper-for-dotnet
It will:
Explain a LINQ query as a strongly typed object (does it use an index for example)
Convert a LINQ query to the JavaScript code run in MongoDB
Check it out and contribute if you find it interesting.
Yes, there is. It shows everything .explain does and has a boolean for verbosity (it includes the time it took to execute):
var database = new MongoClient().GetServer().GetDatabase("db");
var collection = database.GetCollection<Hamster>("Hamsters");
var explanation = collection.AsQueryable().Where(hamster => hamster.Name == "bar").Explain(true);
Console.WriteLine(explanation);
It doesn't show the query though. Here's an extension method for that:
public static string GetMongoQuery<TItem>(this IQueryable<TItem> query)
{
var mongoQuery = query as MongoQueryable<TItem>;
return mongoQuery == null ? null : mongoQuery.GetMongoQuery().ToString();
}
Usage:
var query = collection.AsQueryable().Where(hamster => hamster.Name == "bar").GetMongoQuery();
Console.WriteLine(query);
In mongodb 3 C# I used following:
var users = Mongo.db.GetCollection<User>("Users");
var r = users(m => m._id == yourIdHere)
.Project(m => new { m._id, m.UserName, m.FirstName, m.LastName })
.Limit(1);
Console.WriteLine(r.ToString());
Result:
find({ "_id" : ObjectId("56030e87ca42192008ed0955") }, { "_id" : 1, "UserName" : 1, "FirstName" : 1, "LastName" : 1 }).limit(1)

Categories