[CSHARP-1633] Allow FindDefinition to call .Find and append as an AND Created: 21/Apr/16  Updated: 31/Mar/22

Status: Backlog
Project: C# Driver
Component/s: API, Read Operations
Affects Version/s: 2.2.3
Fix Version/s: None

Type: New Feature Priority: Major - P3
Reporter: Chad Kreimendahl Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified


 Description   

We have a scenario in which we are currently using a Queryable version of the collection as an override to another method, which "pre-filters" a search.

Given the performance gains of Builders vs Linq, We're attempting to migrate away from Linq / Queryable on calls which are made with some frequency.

We'd love to do the same equivilent with a FilterDefinition

class BaseClass<TRoot>

{ ... public IQueryable<TRoot> Queryable => Collection.AsQueryable(); }

class SomeRepository : BaseClass<SomeType>

{ public override IQueryable<SomeType> Queryable => base.Queryable.Where(st => st.Feature.In(listOfAllowedFeatures); }

 Comments   
Comment by Chad Kreimendahl [ 21/Apr/16 ]

I was able to test and verify that the following works. Feel free to incorporate it with all the checks and other magic you do... or, I'm happy to use github to pull request it (and try to match your style of code)

        public static IFindFluent<T, T> Find<T>(this IFindFluent<T, T> find, FilterDefinition<T> filterDef)
        {
            find.Filter = find.Filter & filterDef;
            return find;
        }
 
        public static IFindFluent<T, T> Find<T>(this IFindFluent<T, T> find, Expression<Func<T, bool>> filterDef)
        {
            var efilterDef = new ExpressionFilterDefinition<T>(filterDef);
            find.Filter = find.Filter & efilterDef;
            return find;
        }
 

Comment by Chad Kreimendahl [ 21/Apr/16 ]

I'm thinking it may be as simple as creating the following (for the filterDefinition method)... you tell me?

I've created an extension method that might basically give a .Find on an existing IFindFluent, allowing you to .Find().Find().Find().Sort().Limit(). I'll attempt to test and see if the results are as expected.

        public static IFindFluent<T, T> Find<T>(this IFindFluent<T, T> find, FilterDefinition<T> filterDef)
        {
            find.Filter = find.Filter & filterDef;
            return find;
        }

Comment by Chad Kreimendahl [ 21/Apr/16 ]

I think I really meant that IFindFluent doesn't do it, not filterdef...

protected override IQueryable<Recent> Queryable
{
  return base.Queryable.Where(mr => mr.SubFeature.In(allowedSubFeatures));
}
public IEnumerable<Recent> GetRecent(int userId, int maxCount)
{
  return Queryable.Where(mr => mr.UserId == userId).OrderByDescending(mr => mr.UsageTime).Take(maxCount).ToArray();
}

doesn't have the equiv in Find:

protected override IFindFluent<A, A> Filterable
{
  return base.Collection.Find(mr => mr.SubFeature.In(allowedSubFeatures));
}
public IEnumerable<A> GetRecent(int userId, int maxCount)
{
  return Filterable.Find(mr => mr.UserId == userId).OrderByDescending(mr => mr.UsageTime).Limit(maxCount).ToEnumerable();
}

Comment by Craig Wilson [ 21/Apr/16 ]

I'm sorry. I'm still not understanding. The sample code above I posted is identical. In the base class, set the filter to an empty filter. Then, you don't need to care whether it's been overridden already or not. Either use Builders<SomeType>.Filter.And(base.Filter, newFilter) or use the &. Either way, adding a conjunction to an empty filter will result in the new filter, and subsequent conjunctions will simply be added as well.

If I'm still not answering your question, I'm going to need an example that specifically does not work.

Comment by Chad Kreimendahl [ 21/Apr/16 ]

When we do:

Queryable.Where(a => a.thing == true). Whether or not we override it, we can still call .Where(), and extend the filtering even further.

We're wanting to be able to call "Filter" with some additional items to filter, on the override. And that we can't currently do, since we're not always certain whether or not it's overridden (and thus can't cleanly throw &'s around).

Comment by Craig Wilson [ 21/Apr/16 ]

You can already do this with a FilterDefinition, unless I'm misunderstanding your requirement.

 
class BaseClass<TRoot>
{ ... public FilterDefinition<TRoot> Filter => Builders<TRoot>.Filter.Empty; }
 
class SomeRepository : BaseClass<SomeType>
{ public override FilterDefinition<SomeType> Filter => base.Filter & Builders<SomeType>.Filter.In(st => st.Feature, listOfAllowedFeatures); }

You could also use Builders<SomeType>.Filter.And(...) as well if you don't want to use the & operator.

Is this what you are looking for? If not, could you provide a more comprehensive example?

Generated at Wed Feb 07 21:40:13 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.