Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api restructure #125

Merged
merged 28 commits into from
Apr 1, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 50 additions & 140 deletions MongoDB.Entities/Builders/Distinct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,170 +7,80 @@

namespace MongoDB.Entities
{
/// <summary>
/// Represents a MongoDB Distinct command where you can get back distinct values for a given property of a given Entity.
/// </summary>
/// <typeparam name="T">Any Entity that implements IEntity interface</typeparam>
/// <typeparam name="TProperty">The type of the property of the entity you'd like to get unique values for</typeparam>
public class Distinct<T, TProperty> where T : IEntity
public class DistinctBase<T, TProperty, TSelf> : FilterQueryBase<T, TSelf> where T : IEntity where TSelf : DistinctBase<T, TProperty, TSelf>
{
private FieldDefinition<T, TProperty> field;
private FilterDefinition<T> filter = Builders<T>.Filter.Empty;
private readonly DistinctOptions options = new();
private readonly IClientSessionHandle session;
private readonly Dictionary<Type, (object filterDef, bool prepend)> globalFilters;
private bool ignoreGlobalFilters;
private readonly string tenantPrefix;

internal Distinct(
IClientSessionHandle session,
Dictionary<Type, (object filterDef, bool prepend)> globalFilters, string tenantPrefix)
{
this.session = session;
this.globalFilters = globalFilters;
this.tenantPrefix = tenantPrefix;
}

/// <summary>
/// Specify the property you want to get the unique values for (as a string path)
/// </summary>
/// <param name="property">ex: "Address.Street"</param>
public Distinct<T, TProperty> Property(string property)
{
field = property;
return this;
}
internal DistinctOptions _options = new();
internal FieldDefinition<T, TProperty>? _field;

/// <summary>
/// Specify the property you want to get the unique values for (as a member expression)
/// </summary>
/// <param name="property">x => x.Address.Street</param>
public Distinct<T, TProperty> Property(Expression<Func<T, object>> property)
{
field = property.FullPath();
return this;
}

/// <summary>
/// Specify the matching criteria with a filter expression
/// </summary>
/// <param name="filter">f => f.Eq(x => x.Prop, Value) &amp; f.Gt(x => x.Prop, Value)</param>
public Distinct<T, TProperty> Match(Func<FilterDefinitionBuilder<T>, FilterDefinition<T>> filter)
internal DistinctBase(DistinctBase<T, TProperty, TSelf> other) : base(other)
{
this.filter &= filter(Builders<T>.Filter);
return this;
_options = other._options;
_field = other._field;
}

/// <summary>
/// Specify the matching criteria with a lambda expression
/// </summary>
/// <param name="expression">x => x.Property == Value</param>
public Distinct<T, TProperty> Match(Expression<Func<T, bool>> expression)
internal DistinctBase(Dictionary<Type, (object filterDef, bool prepend)> globalFilters) : base(globalFilters)
{
return Match(f => f.Where(expression));
_globalFilters = globalFilters;
}

/// <summary>
/// Specify the matching criteria with a template
/// </summary>
/// <param name="template">A Template with a find query</param>
public Distinct<T, TProperty> Match(Template template)
{
filter &= template.RenderToString();
return this;
}

/// <summary>
/// Specify a search term to find results from the text index of this particular collection.
/// <para>TIP: Make sure to define a text index with DB.Index&lt;T&gt;() before searching</para>
/// </summary>
/// <param name="searchType">The type of text matching to do</param>
/// <param name="searchTerm">The search term</param>
/// <param name="caseSensitive">Case sensitivity of the search (optional)</param>
/// <param name="diacriticSensitive">Diacritic sensitivity of the search (optional)</param>
/// <param name="language">The language for the search (optional)</param>
public Distinct<T, TProperty> Match(Search searchType, string searchTerm, bool caseSensitive = false, bool diacriticSensitive = false, string language = null)
{
if (searchType == Search.Fuzzy)
{
searchTerm = searchTerm.ToDoubleMetaphoneHash();
caseSensitive = false;
diacriticSensitive = false;
language = null;
}
private TSelf This => (TSelf)this;

return Match(
f => f.Text(
searchTerm,
new TextSearchOptions
{
CaseSensitive = caseSensitive,
DiacriticSensitive = diacriticSensitive,
Language = language
}));
}

/// <summary>
/// Specify criteria for matching entities based on GeoSpatial data (longitude &amp; latitude)
/// <para>TIP: Make sure to define a Geo2DSphere index with DB.Index&lt;T&gt;() before searching</para>
/// <para>Note: DB.FluentGeoNear() supports more advanced options</para>
/// Specify an option for this find command (use multiple times if needed)
/// </summary>
/// <param name="coordinatesProperty">The property where 2DCoordinates are stored</param>
/// <param name="nearCoordinates">The search point</param>
/// <param name="maxDistance">Maximum distance in meters from the search point</param>
/// <param name="minDistance">Minimum distance in meters from the search point</param>
public Distinct<T, TProperty> Match(Expression<Func<T, object>> coordinatesProperty, Coordinates2D nearCoordinates, double? maxDistance = null, double? minDistance = null)
/// <param name="option">x => x.OptionName = OptionValue</param>
public TSelf Option(Action<DistinctOptions> option)
{
return Match(f => f.Near(coordinatesProperty, nearCoordinates.ToGeoJsonPoint(), maxDistance, minDistance));
option(_options);
return This;
}

/// <summary>
/// Specify the matching criteria with a JSON string
/// Specify the property you want to get the unique values for (as a string path)
/// </summary>
/// <param name="jsonString">{ Title : 'The Power Of Now' }</param>
public Distinct<T, TProperty> MatchString(string jsonString)
/// <param name="property">ex: "Address.Street"</param>
public TSelf Property(string property)
{
filter &= jsonString;
return this;
_field = property;
return This;
}

/// <summary>
/// Specify the matching criteria with an aggregation expression (i.e. $expr)
/// Specify the property you want to get the unique values for (as a member expression)
/// </summary>
/// <param name="expression">{ $gt: ['$Property1', '$Property2'] }</param>
public Distinct<T, TProperty> MatchExpression(string expression)
/// <param name="property">x => x.Address.Street</param>
public TSelf Property(Expression<Func<T, object>> property)
{
filter &= "{$expr:" + expression + "}";
return this;
_field = property.FullPath();
return This;
}
}

/// <summary>
/// Specify the matching criteria with a Template
/// </summary>
/// <param name="template">A Template object</param>
public Distinct<T, TProperty> MatchExpression(Template template)
{
filter &= "{$expr:" + template.RenderToString() + "}";
return this;
}
/// <summary>
/// Represents a MongoDB Distinct command where you can get back distinct values for a given property of a given Entity.
/// </summary>
/// <typeparam name="T">Any Entity that implements IEntity interface</typeparam>
/// <typeparam name="TProperty">The type of the property of the entity you'd like to get unique values for</typeparam>
public class Distinct<T, TProperty> : DistinctBase<T, TProperty, Distinct<T, TProperty>>, ICollectionRelated<T> where T : IEntity
{
public DBContext Context { get; }
public IMongoCollection<T> Collection { get; }

/// <summary>
/// Specify an option for this find command (use multiple times if needed)
/// </summary>
/// <param name="option">x => x.OptionName = OptionValue</param>
public Distinct<T, TProperty> Option(Action<DistinctOptions> option)
internal Distinct(
DBContext context,
IMongoCollection<T> collection,
DistinctBase<T, TProperty, Distinct<T, TProperty>> other) : base(other)
{
option(options);
return this;
Context = context;
Collection = collection;
}

/// <summary>
/// Specify that this operation should ignore any global filters
/// </summary>
public Distinct<T, TProperty> IgnoreGlobalFilters()
internal Distinct(
DBContext context,
IMongoCollection<T> collection) : base(globalFilters: context.GlobalFilters)
{
ignoreGlobalFilters = true;
return this;
Context = context;
Collection = collection;
}

/// <summary>
Expand All @@ -179,14 +89,14 @@ public Distinct<T, TProperty> IgnoreGlobalFilters()
/// <param name="cancellation">An optional cancellation token</param>
public Task<IAsyncCursor<TProperty>> ExecuteCursorAsync(CancellationToken cancellation = default)
{
if (field == null)
if (_field == null)
throw new InvalidOperationException("Please use the .Property() method to specify the field to use for obtaining unique values for!");

var mergedFilter = Logic.MergeWithGlobalFilter(ignoreGlobalFilters, globalFilters, filter);
var mergedFilter = Logic.MergeWithGlobalFilter(_ignoreGlobalFilters, _globalFilters, _filter);

return session == null
? DB.Collection<T>(tenantPrefix).DistinctAsync(field, mergedFilter, options, cancellation)
: DB.Collection<T>(tenantPrefix).DistinctAsync(session, field, mergedFilter, options, cancellation);
return Context.Session is IClientSessionHandle session
? Collection.DistinctAsync(session, _field, mergedFilter, _options, cancellation)
: Collection.DistinctAsync(_field, mergedFilter, _options, cancellation);
}

/// <summary>
Expand Down
Loading