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
2 changes: 1 addition & 1 deletion samples/BasicSample/BasicSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.18.0" />
<PackageReference Include="MongoDB.Driver" Version="2.19.0" />
<PackageReference Include="MongoDB.Analyzer" Version="1.1.0" />
</ItemGroup>

Expand Down
13 changes: 13 additions & 0 deletions samples/BasicSample/BuildersSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ namespace BasicSample
{
public class BuildersSample
{
public void AtlasSearch()
{
var mongoClient = new MongoClient(@"mongodb://localhost:27017");
var db = mongoClient.GetDatabase("testdb");
var moviesCollection = db.GetCollection<Movie>("movies");

// Search definition (analyzer provides mql as information message)
var searchTitle = Builders<Movie>.Search.Wildcard(p => p.Title, "Green D*");

// MQL is displayed for 'searchTitle' variables
moviesCollection.Aggregate().Search(searchTitle);
}

public async Task<List<Movie>> GetMovies(double minScore, Genre genre, string titleSearchTerm)
{
var mongoClient = new MongoClient(@"mongodb://localhost:27017");
Expand Down
2 changes: 1 addition & 1 deletion src/MongoDB.Analyzer.Helpers/Builders/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

namespace MongoDB.Analyzer.Helpers.Builders
{
public static class Renderer
public static partial class Renderer
{
public static string Render<T>(FilterDefinition<T> filterDefinition)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2021-present MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using MongoDB.Bson.Serialization;
using MongoDB.Driver.Search;

namespace MongoDB.Analyzer.Helpers.Builders
{
public static partial class Renderer
{
public static string Render<T>(SearchDefinition<T> searchDefinition)
{
var renderedBuildersDefinition = searchDefinition.Render(BsonSerializer.LookupSerializer<T>(), BsonSerializer.SerializerRegistry);
return renderedBuildersDefinition.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.14.0-beta1" />
<PackageReference Include="MongoDB.Driver" Version="2.19.0" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/MongoDB.Analyzer/Core/Builders/AnalysisCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ namespace MongoDB.Analyzer.Core.Builders;
internal static class AnalysisCodeGenerator
{
private static readonly SyntaxTree[] s_helpersSyntaxTrees;
private static readonly SyntaxTree s_renderer_2_19_and_higher;
private static readonly BuildersMqlGeneratorTemplateBuilder.SyntaxElements s_mqlGeneratorSyntaxElements;
private static readonly ParseOptions s_parseOptions;

static AnalysisCodeGenerator()
{
s_helpersSyntaxTrees = GetCommonCodeResources(ResourceNames.Builders.Renderer);
s_renderer_2_19_and_higher = GetCodeResource(ResourceNames.Builders.Renderer_2_19_and_higher);

var mqlGeneratorSyntaxTree = GetCodeResource(ResourceNames.Builders.MqlGenerator);
s_mqlGeneratorSyntaxElements = BuildersMqlGeneratorTemplateBuilder.CreateSyntaxElements(mqlGeneratorSyntaxTree);
Expand All @@ -50,6 +52,11 @@ public static CompilationResult Compile(MongoAnalyzerContext context, Expression
mqlGeneratorSyntaxTree
};

if (referencesContainer.Version >= BuildersAnalysisConstants.Version_2_19_and_higher)
{
syntaxTrees.Add(s_renderer_2_19_and_higher);
}

var compilation = CSharpCompilation.Create(
BuildersAnalysisConstants.AnalysisAssemblyName,
syntaxTrees,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ namespace MongoDB.Analyzer.Core.Builders;
internal static class BuildersAnalysisConstants
{
public const string AnalysisAssemblyName = "DynamicProxyGenAssembly2";

public static readonly Version Version_2_19_and_higher = Version.Parse("2.19.0");
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal static class Builders
{
public const string MqlGenerator = $"Builders.{nameof(MqlGenerator)}";
public const string Renderer = $"Builders.{nameof(Renderer)}";
public const string Renderer_2_19_and_higher = $"Builders.{nameof(Renderer_2_19_and_higher)}";
}

public const string EmptyCursor = nameof(EmptyCursor);
Expand Down
8 changes: 5 additions & 3 deletions src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ public static bool IsBuilder(this ITypeSymbol typeSymbol) =>
"FilterDefinitionBuilder" or
"IndexKeysDefinitionBuilder" or
"IndexKeysDefinitionExtensions" or
"SortDefinitionBuilder" or
"SortDefinitionExtensions" or
"ProjectionDefinitionBuilder" or
"ProjectionDefinitionExtensions" or
"PipelineDefinitionBuilder" or
"SearchDefinitionBuilder" or
"SortDefinitionBuilder" or
"SortDefinitionExtensions" or
"UpdateDefinitionBuilder" => true,
_ => false
};
Expand All @@ -82,9 +83,10 @@ public static bool IsBuilderDefinition(this ITypeSymbol typeSymbol) =>
{
"FilterDefinition" or
"IndexKeysDefinition" or
"SortDefinition" or
"ProjectionDefinition" or
"PipelineDefinition" or
"SearchDefinition" or
"SortDefinition" or
"UpdateDefinition" => true,
_ => false
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Import Project="..\MongoDB.Analyzer.Tests.Common\MongoDB.Analyzer.Tests.Common.projitems" Label="Shared" />

<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.11.0">
<PackageReference Include="MongoDB.Driver" Version="2.19.0">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright 2021-present MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using MongoDB.Analyzer.Tests.Common.DataModel;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.GeoJsonObjectModel;
using MongoDB.Driver.Search;

namespace MongoDB.Analyzer.Tests.Common.TestCases.Builders
{
public sealed class BuildersAtlasSearch : TestCasesBase
{
private static readonly GeoJsonPolygon<GeoJson2DGeographicCoordinates> s_testPolygon =
new GeoJsonPolygon<GeoJson2DGeographicCoordinates>(
new GeoJsonPolygonCoordinates<GeoJson2DGeographicCoordinates>(
new GeoJsonLinearRingCoordinates<GeoJson2DGeographicCoordinates>(
new List<GeoJson2DGeographicCoordinates>()
{
new GeoJson2DGeographicCoordinates(-161.323242, 22.512557),
new GeoJson2DGeographicCoordinates(-152.446289, 22.065278),
new GeoJson2DGeographicCoordinates(-156.09375, 17.811456),
new GeoJson2DGeographicCoordinates(-161.323242, 22.512557)
})));

[BuildersMQL("{ \"autocomplete\" : { \"query\" : \"My address\", \"path\" : \"Address\" } }", DriverVersions.V2_19_AndHigher, 1, 2, 3)]
public void Autocomplete()
{
_ = Builders<User>.Search.Autocomplete(m => m.Address, "My address");
_ = Builders<User>.Search.Autocomplete("Address", "My address");
_ = Builders<BsonDocument>.Search.Autocomplete("Address", "My address");
}

[BuildersMQL("{ \"equals\" : { \"value\" : true, \"path\" : \"IsRetired\" } }", DriverVersions.V2_19_AndHigher, 1, 2, 3)]
public void Equals()
{
_ = Builders<Person>.Search.Equals(m => m.IsRetired, true);
_ = Builders<Person>.Search.Equals("IsRetired", true);
_ = Builders<BsonDocument>.Search.Equals("IsRetired", true);
}

[BuildersMQL("{ \"near\" : { \"origin\" : 1, \"pivot\" : 2, \"path\" : \"SiblingsCount\" } }", DriverVersions.V2_19_AndHigher)]
[BuildersMQL("{ \"near\" : { \"origin\" : NumberLong(5), \"pivot\" : NumberLong(2), \"path\" : \"SiblingsCount\" } }", DriverVersions.V2_19_AndHigher, 2, 3)]
public void Near()
{
Builders<Person>.Search.Near(p => p.SiblingsCount, 1, 2);
Builders<Person>.Search.Near("SiblingsCount", 5L, 2L);
Builders<BsonDocument>.Search.Near("SiblingsCount", 5L, 2L);
}

[BuildersMQL("{ \"phrase\" : { \"query\" : \"Columbia\", \"path\" : \"Address.Province\" } }", DriverVersions.V2_19_AndHigher, 1, 2, 3)]
public void Phrase()
{
_ = Builders<Person>.Search.Phrase(m => m.Address.Province, "Columbia");
_ = Builders<Person>.Search.Phrase("Address.Province", "Columbia");
_ = Builders<BsonDocument>.Search.Phrase("Address.Province", "Columbia");
}

[BuildersMQL("{ \"queryString\" : { \"defaultPath\" : \"Name\", \"query\" : \"constant string\" } }", DriverVersions.V2_19_AndHigher, 1, 2, 3)]
public void QueryString()
{
_ = Builders<Person>.Search.QueryString(m => m.Name, "constant string");
_ = Builders<Person>.Search.QueryString("Name", "constant string");
_ = Builders<BsonDocument>.Search.QueryString("Name", "constant string");
}

[BuildersMQL("{ \"wildcard\" : { \"query\" : [\"foo\", \"bar\"], \"path\" : \"Name\" } }", DriverVersions.V2_19_AndHigher)]
[BuildersMQL("{ \"wildcard\" : { \"query\" : \"A\", \"path\" : \"Name\" } }", DriverVersions.V2_19_AndHigher, 2, 3)]
public void Wildcard()
{
_ = Builders<Person>.Search.Wildcard(m => m.Name, new[] { "foo", "bar" });
_ = Builders<Person>.Search.Wildcard("Name", "A");
_ = Builders<BsonDocument>.Search.Wildcard("Name", "A");
}

[BuildersMQL("{ \"regex\" : { \"query\" : [\"Donald\", \"Mike\"], \"path\" : \"Name\" } }", DriverVersions.V2_19_AndHigher)]
[BuildersMQL("{ \"regex\" : { \"query\" : \"Alice\", \"path\" : \"Name\" } }", DriverVersions.V2_19_AndHigher, 2, 3)]
public void Regex()
{
_ = Builders<Person>.Search.Regex(m => m.Name, new[] { "Donald", "Mike" });
_ = Builders<Person>.Search.Regex("Name", "Alice");
_ = Builders<BsonDocument>.Search.Regex("Name", "Alice");
}

[BuildersMQL("{ \"span\" : { \"first\" : { \"operator\" : { \"term\" : { \"query\" : \"foo\", \"path\" : \"Name\" } }, \"endPositionLte\" : 5 } } }", DriverVersions.V2_19_AndHigher)]
[BuildersMQL("{ \"span\" : { \"or\" : { \"clauses\" : [{ \"term\" : { \"query\" : \"a\", \"path\" : \"Name\" } }, { \"term\" : { \"query\" : \"b\", \"path\" : \"Name\" } }, { \"term\" : { \"query\" : \"c\", \"path\" : \"Name\" } }] } } }", DriverVersions.V2_19_AndHigher)]
public void Span()
{
_ = Builders<Person>.Search.Span(Builders<Person>.SearchSpan
.First(Builders<Person>.SearchSpan.Term(p => p.Name, "foo"), 5));

_ = Builders<Person>.Search.Span(Builders<Person>.SearchSpan.Or(
Builders<Person>.SearchSpan.Term(p => p.Name, "a"),
Builders<Person>.SearchSpan.Term(p => p.Name, "b"),
Builders<Person>.SearchSpan.Term(p => p.Name, "c")));
}

[BuildersMQL("{ \"text\" : { \"query\" : \"My address\", \"path\" : \"Address\" } }", DriverVersions.V2_19_AndHigher, 1, 2, 3)]
public void Text()
{
_ = Builders<User>.Search.Text(m => m.Address, "My address");
_ = Builders<User>.Search.Text("Address", "My address");
_ = Builders<BsonDocument>.Search.Text("Address", "My address");
}

[NoDiagnostics(DriverVersions.V2_19_AndHigher)]
public void Valid_but_ignored()
{
_ = Builders<Person>.Search.GeoShape(
"location",
GeoShapeRelation.Disjoint,
s_testPolygon);

_ = Builders<Person>.Search.GeoWithin(
"location",
s_testPolygon);

_ = Builders<Person>.Search.Range(m => m.SiblingsCount, SearchRangeBuilder.Gt(1).Lt(10));
_ = Builders<Person>.Search.Near("Date", DateTime.Now, 1);

_ = Builders<Person>.Search.Wildcard(new FieldDefinition<Person>[]
{
new ExpressionFieldDefinition<Person, string>(x => x.Name),
new ExpressionFieldDefinition<Person, string>(x => x.LastName)
}, "A");
}

[NoDiagnostics(DriverVersions.V2_18_AndLower)]
public void Search_should_be_ignored_in_older_drivers()
{
// Technically Atlas Search code can't appear with older drivers, via normal C# usage,
// so this test covers isoteric edge cases.
_ = Builders<User>.Search.Text(m => m.Address, "My address");
_ = Builders<Person>.Search.Phrase(m => m.Address.Province, "Columbia");
}
}
}
2 changes: 2 additions & 0 deletions tests/MongoDB.Analyzer.Tests.Common/DataModel/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ public class Person

public int SiblingsCount { get; set; }
public long TicksSinceBirth { get; set; }

public bool IsRetired { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@ public DiagnosticRuleTestCaseAttribute(
string version = null,
LinqVersion linqProvider = LinqVersion.V2,
DriverTargetFramework targetFramework = DriverTargetFramework.All,
Location[] locations = null)
int[] codeLines = null)
{
RuleId = ruleId;
Message = message;
Version = version;
LinqProvider = linqProvider;
TargetFramework = targetFramework;
Locations = locations ?? new[] { Location.Empty };
Locations = codeLines?.Any() == true ? codeLines.Select(l => new Location(l, -1)).ToArray() : new[] { Location.Empty };
}
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class NoDiagnosticsAttribute : DiagnosticRuleTestCaseAttribute
{
public NoDiagnosticsAttribute() : base(DiagnosticRulesConstants.NoRule, null) { }
public NoDiagnosticsAttribute(string version = null) : base(DiagnosticRulesConstants.NoRule, null, version: version) { }
}

public class MQLAttribute : DiagnosticRuleTestCaseAttribute
Expand All @@ -70,7 +70,7 @@ public MQLAttribute(
version,
linqProvider,
targetFramework,
codeLines.Any() ? codeLines.Select(l => new Location(l, -1)).ToArray() : null)
codeLines)
{
}
}
Expand Down Expand Up @@ -122,14 +122,15 @@ public sealed class BuildersMQLAttribute : DiagnosticRuleTestCaseAttribute
public BuildersMQLAttribute(string message, params int[] codeLines) :
base(DiagnosticRulesConstants.Builders2MQL,
message,
locations: codeLines.Any() ? codeLines.Select(l => new Location(l, -1)).ToArray() : null)
codeLines: codeLines)
{
}

public BuildersMQLAttribute(string message, string version) :
public BuildersMQLAttribute(string message, string version, params int[] codeLines) :
base(DiagnosticRulesConstants.Builders2MQL,
message,
version: version)
version: version,
codeLines: codeLines)
{
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/MongoDB.Analyzer.Tests/Builders/BuildersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public sealed class BuildersTests : DiagnosticsTestCasesRunner
[CodeBasedTestCasesSource(typeof(BuildersArrays))]
public Task Arrays(DiagnosticTestCase testCase) => VerifyTestCase(testCase);

[DataTestMethod]
[CodeBasedTestCasesSource(typeof(BuildersAtlasSearch))]
public Task AtlasSearch(DiagnosticTestCase testCase) => VerifyTestCase(testCase);

[DataTestMethod]
[CodeBasedTestCasesSource(typeof(BuildersBasic))]
public Task Basic(DiagnosticTestCase testCase) => VerifyTestCase(testCase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.MSTest" Version="1.1.0" />
<PackageReference Include="NuGet.Versioning" Version="5.11.0" />

<PackageReference Include="MongoDB.Driver" Version="2.13.0">
<PackageReference Include="MongoDB.Driver" Version="2.19.0">
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="System.Text.Json" Version="5.0.2" />
Expand Down