Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,8 +1364,9 @@ public static PipelineStageDefinition<TInput, TInput> Search<TInput>(
operatorName,
(s, sr, linqProvider) =>
{
var renderedSearchDefinition = searchDefinition.Render(s, sr);
renderedSearchDefinition.Add("highlight", () => searchOptions.Highlight.Render(s, sr), searchOptions.Highlight != null);
var renderContext = new SearchDefinitionRenderContext<TInput>(s, sr);
var renderedSearchDefinition = searchDefinition.Render(renderContext);
renderedSearchDefinition.Add("highlight", () => searchOptions.Highlight.Render(renderContext), searchOptions.Highlight != null);
renderedSearchDefinition.Add("count", () => searchOptions.CountOptions.Render(), searchOptions.CountOptions != null);
renderedSearchDefinition.Add("sort", () => searchOptions.Sort.Render(s, sr), searchOptions.Sort != null);
renderedSearchDefinition.Add("index", searchOptions.IndexName, searchOptions.IndexName != null);
Expand Down Expand Up @@ -1400,7 +1401,7 @@ public static PipelineStageDefinition<TInput, SearchMetaResult> SearchMeta<TInpu
operatorName,
(s, sr, linqProvider) =>
{
var renderedSearchDefinition = searchDefinition.Render(s, sr);
var renderedSearchDefinition = searchDefinition.Render(new(s, sr));
renderedSearchDefinition.Add("count", () => count.Render(), count != null);
renderedSearchDefinition.Add("index", indexName, indexName != null);

Expand Down
74 changes: 50 additions & 24 deletions src/MongoDB.Driver/Search/OperatorSearchDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public AutocompleteSearchDefinition(
_fuzzy = fuzzy;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "query", _query.Render() },
Expand Down Expand Up @@ -76,7 +76,7 @@ public CompoundSearchDefinition(
_minimumShouldMatch = minimumShouldMatch;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext)
{
return new()
{
Expand All @@ -88,7 +88,33 @@ private protected override BsonDocument RenderArguments(IBsonSerializer<TDocumen
};

Func<BsonArray> Render(List<SearchDefinition<TDocument>> searchDefinitions) =>
() => new BsonArray(searchDefinitions.Select(clause => clause.Render(documentSerializer, serializerRegistry)));
() => new BsonArray(searchDefinitions.Select(clause => clause.Render(renderContext)));
}
}

internal sealed class EmbeddedDocumentSearchDefinition<TDocument, TField> : OperatorSearchDefinition<TDocument>
{
private readonly SearchDefinition<TField> _operator;

public EmbeddedDocumentSearchDefinition(FieldDefinition<TDocument, IEnumerable<TField>> path, SearchDefinition<TField> @operator, SearchScoreDefinition<TDocument> score)
: base(OperatorType.EmbeddedDocument,
new SingleSearchPathDefinition<TDocument>(path),
score)
{
_operator = Ensure.IsNotNull(@operator, nameof(@operator));
}

private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext)
{
// Add base path to all nested operator paths
var pathPrefix = _path.Render(renderContext).AsString;

var newRenderContext = new SearchDefinitionRenderContext<TField>(
renderContext.SerializerRegistry.GetSerializer<TField>(),
renderContext.SerializerRegistry,
pathPrefix);

return new("operator", _operator.Render(newRenderContext));
}
}

Expand All @@ -102,7 +128,7 @@ public EqualsSearchDefinition(FieldDefinition<TDocument> path, TField value, Sea
_value = ToBsonValue(value);
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new("value", _value);

private static BsonValue ToBsonValue(TField value) =>
Expand Down Expand Up @@ -145,11 +171,11 @@ public FacetSearchDefinition(SearchDefinition<TDocument> @operator, IEnumerable<
_facets = Ensure.IsNotNull(facets, nameof(facets)).ToArray();
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "operator", _operator.Render(documentSerializer, serializerRegistry) },
{ "facets", new BsonDocument(_facets.Select(f => new BsonElement(f.Name, f.Render(documentSerializer, serializerRegistry)))) }
{ "operator", _operator.Render(renderContext) },
{ "facets", new BsonDocument(_facets.Select(f => new BsonElement(f.Name, f.Render(renderContext)))) }
};
}

Expand All @@ -170,7 +196,7 @@ public GeoShapeSearchDefinition(
_relation = relation;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "geometry", _geometry.ToBsonDocument() },
Expand All @@ -192,7 +218,7 @@ public GeoWithinSearchDefinition(
_area = Ensure.IsNotNull(area, nameof(area));
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new(_area.Render());
}

Expand All @@ -206,13 +232,13 @@ public MoreLikeThisSearchDefinition(IEnumerable<TLike> like)
_like = Ensure.IsNotNull(like, nameof(like)).ToArray();
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext)
{
var likeSerializer = typeof(TLike) switch
{
var t when t == typeof(BsonDocument) => null,
var t when t == typeof(TDocument) => (IBsonSerializer<TLike>)documentSerializer,
_ => serializerRegistry.GetSerializer<TLike>()
var t when t == typeof(TDocument) => (IBsonSerializer<TLike>)renderContext.DocumentSerializer,
_ => renderContext.SerializerRegistry.GetSerializer<TLike>()
};

return new("like", new BsonArray(_like.Select(document => document.ToBsonDocument(likeSerializer))));
Expand All @@ -235,7 +261,7 @@ public NearSearchDefinition(
_pivot = pivot;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "origin", _origin },
Expand All @@ -259,7 +285,7 @@ public PhraseSearchDefinition(
_slop = slop;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "query", _query.Render() },
Expand All @@ -269,20 +295,20 @@ private protected override BsonDocument RenderArguments(IBsonSerializer<TDocumen

internal sealed class QueryStringSearchDefinition<TDocument> : OperatorSearchDefinition<TDocument>
{
private readonly FieldDefinition<TDocument> _defaultPath;
private readonly SingleSearchPathDefinition<TDocument> _defaultPath;
private readonly string _query;

public QueryStringSearchDefinition(FieldDefinition<TDocument> defaultPath, string query, SearchScoreDefinition<TDocument> score)
: base(OperatorType.QueryString, score)
{
_defaultPath = Ensure.IsNotNull(defaultPath, nameof(defaultPath));
_defaultPath = new SingleSearchPathDefinition<TDocument>(defaultPath);
_query = Ensure.IsNotNull(query, nameof(query));
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "defaultPath", _defaultPath.Render(documentSerializer, serializerRegistry).FieldName },
{ "defaultPath", _defaultPath.Render(renderContext) },
{ "query", _query }
};
}
Expand All @@ -305,7 +331,7 @@ public RangeSearchDefinition(
_max = ToBsonValue(_range.Max);
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ _range.IsMinInclusive ? "gte" : "gt", _min, _min != null },
Expand Down Expand Up @@ -347,7 +373,7 @@ public RegexSearchDefinition(
_allowAnalyzedField = allowAnalyzedField;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "query", _query.Render() },
Expand All @@ -365,8 +391,8 @@ public SpanSearchDefinition(SearchSpanDefinition<TDocument> clause)
_clause = Ensure.IsNotNull(clause, nameof(clause));
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
_clause.Render(documentSerializer, serializerRegistry);
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
_clause.Render(renderContext);
}

internal sealed class TextSearchDefinition<TDocument> : OperatorSearchDefinition<TDocument>
Expand All @@ -385,7 +411,7 @@ public TextSearchDefinition(
_fuzzy = fuzzy;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "query", _query.Render() },
Expand All @@ -409,7 +435,7 @@ public WildcardSearchDefinition(
_allowAnalyzedField = allowAnalyzedField;
}

private protected override BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
private protected override BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) =>
new()
{
{ "query", _query.Render() },
Expand Down
31 changes: 16 additions & 15 deletions src/MongoDB.Driver/Search/SearchDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver.Core.Misc;

namespace MongoDB.Driver.Search
Expand All @@ -26,12 +25,13 @@ namespace MongoDB.Driver.Search
public abstract class SearchDefinition<TDocument>
{
/// <summary>
/// Renders the search definition to a <see cref="BsonDocument"/>.
/// Renders the search definition to a <see cref="BsonDocument" />.
/// </summary>
/// <param name="documentSerializer">The document serializer.</param>
/// <param name="serializerRegistry">The serializer registry.</param>
/// <returns>A <see cref="BsonDocument"/>.</returns>
public abstract BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry);
/// <param name="renderContext">The render context.</param>
/// <returns>
/// A <see cref="BsonDocument" />.
/// </returns>
public abstract BsonDocument Render(SearchDefinitionRenderContext<TDocument> renderContext);

/// <summary>
/// Performs an implicit conversion from a BSON document to a <see cref="SearchDefinition{TDocument}"/>.
Expand Down Expand Up @@ -75,7 +75,7 @@ public BsonDocumentSearchDefinition(BsonDocument document)
public BsonDocument Document { get; private set; }

/// <inheritdoc />
public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
public override BsonDocument Render(SearchDefinitionRenderContext<TDocument> renderContext) =>
Document;
}

Expand All @@ -100,7 +100,7 @@ public JsonSearchDefinition(string json)
public string Json { get; private set; }

/// <inheritdoc />
public override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) =>
public override BsonDocument Render(SearchDefinitionRenderContext<TDocument> renderContext) =>
BsonDocument.Parse(Json);
}

Expand Down Expand Up @@ -131,8 +131,8 @@ private protected enum OperatorType

private readonly OperatorType _operatorType;
// _path and _score used by many but not all subclasses
private readonly SearchPathDefinition<TDocument> _path;
private readonly SearchScoreDefinition<TDocument> _score;
protected readonly SearchPathDefinition<TDocument> _path;
protected readonly SearchScoreDefinition<TDocument> _score;

private protected OperatorSearchDefinition(OperatorType operatorType)
: this(operatorType, null)
Expand All @@ -152,15 +152,16 @@ private protected OperatorSearchDefinition(OperatorType operatorType, SearchPath
_score = score;
}

public sealed override BsonDocument Render(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry)
/// <inheritdoc />
public sealed override BsonDocument Render(SearchDefinitionRenderContext<TDocument> renderContext)
{
var renderedArgs = RenderArguments(documentSerializer, serializerRegistry);
renderedArgs.Add("path", () => _path.Render(documentSerializer, serializerRegistry), _path != null);
renderedArgs.Add("score", () => _score.Render(documentSerializer, serializerRegistry), _score != null);
var renderedArgs = RenderArguments(renderContext);
renderedArgs.Add("path", () => _path.Render(renderContext), _path != null);
renderedArgs.Add("score", () => _score.Render(renderContext), _score != null);

return new(_operatorType.ToCamelCase(), renderedArgs);
}

private protected virtual BsonDocument RenderArguments(IBsonSerializer<TDocument> documentSerializer, IBsonSerializerRegistry serializerRegistry) => new();
private protected virtual BsonDocument RenderArguments(SearchDefinitionRenderContext<TDocument> renderContext) => new();
}
}
35 changes: 35 additions & 0 deletions src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,41 @@ public SearchDefinition<TDocument> Autocomplete<TField>(
public CompoundSearchDefinitionBuilder<TDocument> Compound(SearchScoreDefinition<TDocument> score = null) =>
new CompoundSearchDefinitionBuilder<TDocument>(score);

/// <summary>
/// Creates a search definition that performs a search for documents where
/// the specified query <paramref name="operator"/> is satisfied from a single element
/// of an array of embedded documents specified by <paramref name="path"/>.
/// </summary>
/// <param name="path">The indexed field to search.</param>
/// <param name="operator">The operator.</param>
/// <param name="score">The score modifier.</param>
/// <returns>
/// An embeddedDocument search definition.
/// </returns>
public SearchDefinition<TDocument> EmbeddedDocument<TField>(
FieldDefinition<TDocument, IEnumerable<TField>> path,
SearchDefinition<TField> @operator,
SearchScoreDefinition<TDocument> score = null) =>
new EmbeddedDocumentSearchDefinition<TDocument, TField>(path, @operator, score);

/// <summary>
/// Creates a search definition that performs a search for documents where
/// the specified query <paramref name="operator"/> is satisfied from a single element
/// of an array of embedded documents specified by <paramref name="path"/>.
/// </summary>
/// <typeparam name="TField">The type of the field.</typeparam>
/// <param name="path">The indexed field to search.</param>
/// <param name="operator">The operator.</param>
/// <param name="score">The score modifier.</param>
/// <returns>
/// An embeddedDocument search definition.
/// </returns>
public SearchDefinition<TDocument> EmbeddedDocument<TField>(
Expression<Func<TDocument, IEnumerable<TField>>> path,
SearchDefinition<TField> @operator,
SearchScoreDefinition<TDocument> score = null) =>
EmbeddedDocument(new ExpressionFieldDefinition<TDocument, IEnumerable<TField>>(path), @operator, score);

/// <summary>
/// Creates a search definition that queries for documents where an indexed field is equal
/// to the specified value.
Expand Down
Loading