-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2536 from idg10/field-builder-pr
Add reflection-based Search index definition
- Loading branch information
Showing
14 changed files
with
1,239 additions
and
96 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
src/Search/Microsoft.Azure.Search/Customizations/Indexes/AnalyzerAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates that the <see cref="Field"/> generated by <see cref="FieldBuilder"/> for | ||
/// the target property should have its <see cref="Field.Analyzer"/> property set to the | ||
/// specified analyzer. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class AnalyzerAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// Indicates that the specified analyzer should be used. | ||
/// </summary> | ||
/// <param name="analyzerName"> | ||
/// The name of the analyzer. Use one of the names in <see cref="AnalyzerName.AsString"/> | ||
/// or the name of a custom analyzer. | ||
/// </param> | ||
public AnalyzerAttribute(string analyzerName) | ||
{ | ||
Name = analyzerName; | ||
} | ||
|
||
public string Name { get; } | ||
} | ||
} |
176 changes: 176 additions & 0 deletions
176
src/Search/Microsoft.Azure.Search/Customizations/Indexes/FieldBuilder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Microsoft.Azure.Search.Models; | ||
using Microsoft.Spatial; | ||
using Newtonsoft.Json.Serialization; | ||
|
||
public static class FieldBuilder | ||
{ | ||
/// <summary> | ||
/// Creates a collection of <see cref="Field"/> objects corresponding to | ||
/// the properties of the type supplied. | ||
/// </summary> | ||
/// <typeparam name="T"> | ||
/// The type for which fields will be created, based on its properties. | ||
/// </typeparam> | ||
/// <returns>A collection of fields.</returns> | ||
public static IList<Field> BuildForType<T>() | ||
{ | ||
bool useCamelCase = SerializePropertyNamesAsCamelCaseAttribute.IsDefinedOnType<T>(); | ||
IContractResolver resolver = useCamelCase | ||
? JsonUtility.CamelCaseResolver | ||
: JsonUtility.DefaultResolver; | ||
return BuildForType<T>(resolver); | ||
} | ||
|
||
/// <summary> | ||
/// Creates a collection of <see cref="Field"/> objects corresponding to | ||
/// the properties of the type supplied. | ||
/// </summary> | ||
/// <typeparam name="T"> | ||
/// The type for which fields will be created, based on its properties. | ||
/// </typeparam> | ||
/// <param name="contractResolver"> | ||
/// Contract resolver that the <see cref="SearchIndexClient"/> will use. | ||
/// This ensures that the field names are generated in a way that is | ||
/// consistent with the way the model will be serialized. | ||
/// </param> | ||
/// <returns>A collection of fields.</returns> | ||
public static IList<Field> BuildForType<T>(IContractResolver contractResolver) | ||
{ | ||
var contract = (JsonObjectContract) contractResolver.ResolveContract(typeof(T)); | ||
var fields = new List<Field>(); | ||
foreach (JsonProperty prop in contract.Properties) | ||
{ | ||
DataType dataType = GetDataType(prop.PropertyType, prop.PropertyName); | ||
|
||
var field = new Field(prop.PropertyName, dataType); | ||
|
||
IList<Attribute> attributes = prop.AttributeProvider.GetAttributes(true); | ||
foreach (Attribute attribute in attributes) | ||
{ | ||
IsRetrievableAttribute isRetrievableAttribute; | ||
AnalyzerAttribute analyzerAttribute; | ||
SearchAnalyzerAttribute searchAnalyzerAttribute; | ||
IndexAnalyzerAttribute indexAnalyzerAttribute; | ||
if (attribute is IsSearchableAttribute) | ||
{ | ||
field.IsSearchable = true; | ||
} | ||
else if (attribute is IsFilterableAttribute) | ||
{ | ||
field.IsFilterable = true; | ||
} | ||
else if (attribute is IsSortableAttribute) | ||
{ | ||
field.IsSortable = true; | ||
} | ||
else if (attribute is IsFacetableAttribute) | ||
{ | ||
field.IsFacetable = true; | ||
} | ||
else if ((isRetrievableAttribute = attribute as IsRetrievableAttribute) != null) | ||
{ | ||
field.IsRetrievable = isRetrievableAttribute.IsRetrievable; | ||
} | ||
else if ((analyzerAttribute = attribute as AnalyzerAttribute) != null) | ||
{ | ||
field.Analyzer = AnalyzerName.Create(analyzerAttribute.Name); | ||
} | ||
else if ((searchAnalyzerAttribute = attribute as SearchAnalyzerAttribute) != null) | ||
{ | ||
field.SearchAnalyzer = AnalyzerName.Create(searchAnalyzerAttribute.Name); | ||
} | ||
else if ((indexAnalyzerAttribute = attribute as IndexAnalyzerAttribute) != null) | ||
{ | ||
field.IndexAnalyzer = AnalyzerName.Create(indexAnalyzerAttribute.Name); | ||
} | ||
else | ||
{ | ||
// Match on name to avoid dependency - don't want to force people not using | ||
// this feature to bring in the annotations component. | ||
Type attributeType = attribute.GetType(); | ||
if (attributeType.FullName == "System.ComponentModel.DataAnnotations.KeyAttribute") | ||
{ | ||
field.IsKey = true; | ||
} | ||
} | ||
} | ||
|
||
fields.Add(field); | ||
} | ||
|
||
return fields; | ||
} | ||
|
||
private static DataType GetDataType(Type propertyType, string propertyName) | ||
{ | ||
if (propertyType == typeof(string)) | ||
{ | ||
return DataType.String; | ||
} | ||
if (propertyType.IsConstructedGenericType && | ||
propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||
{ | ||
return GetDataType(propertyType.GenericTypeArguments[0], propertyName); | ||
} | ||
if (propertyType == typeof(int)) | ||
{ | ||
return DataType.Int32; | ||
} | ||
if (propertyType == typeof(long)) | ||
{ | ||
return DataType.Int64; | ||
} | ||
if (propertyType == typeof(double)) | ||
{ | ||
return DataType.Double; | ||
} | ||
if (propertyType == typeof(bool)) | ||
{ | ||
return DataType.Boolean; | ||
} | ||
if (propertyType == typeof(DateTimeOffset) || | ||
propertyType == typeof(DateTime)) | ||
{ | ||
return DataType.DateTimeOffset; | ||
} | ||
if (propertyType == typeof(GeographyPoint)) | ||
{ | ||
return DataType.GeographyPoint; | ||
} | ||
Type elementType = GetElementTypeIfIEnumerable(propertyType); | ||
if (elementType != null) | ||
{ | ||
return DataType.Collection(GetDataType(elementType, propertyName)); | ||
} | ||
TypeInfo ti = propertyType.GetTypeInfo(); | ||
var listElementTypes = ti | ||
.ImplementedInterfaces | ||
.Select(GetElementTypeIfIEnumerable) | ||
.Where(p => p != null) | ||
.ToList(); | ||
if (listElementTypes.Count == 1) | ||
{ | ||
return DataType.Collection(GetDataType(listElementTypes[0], propertyName)); | ||
} | ||
|
||
throw new ArgumentException( | ||
$"Property {propertyName} has unsupported type {propertyType}", | ||
nameof(propertyType)); | ||
} | ||
|
||
private static Type GetElementTypeIfIEnumerable(Type t) => | ||
t.IsConstructedGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>) | ||
? t.GenericTypeArguments[0] | ||
: null; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IndexAnalyzerAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates that the <see cref="Field"/> generated by <see cref="FieldBuilder"/> for | ||
/// the target property should have its <see cref="Field.IndexAnalyzer"/> property set to the | ||
/// specified analyzer. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IndexAnalyzerAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// Indicates that the specified analyzer should be used. | ||
/// </summary> | ||
/// <param name="analyzerName"> | ||
/// The name of the analyzer. Use one of the names in <see cref="AnalyzerName.AsString"/> | ||
/// or the name of a custom analyzer. | ||
/// </param> | ||
public IndexAnalyzerAttribute(string analyzerName) | ||
{ | ||
Name = analyzerName; | ||
} | ||
|
||
public string Name { get; } | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IsFacetableAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates that it is possible to facet on this field. Not valid for | ||
/// geo-point fields. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IsFacetableAttribute : Attribute | ||
{ | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IsFilterableAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates that the field can be used in filter expressions. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IsFilterableAttribute : Attribute | ||
{ | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IsRetrievableAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates whether the field can be returned in a search result. This | ||
/// defaults to true, so this attribute only has any effect if you use it | ||
/// as [IsRetrievable(false)]. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IsRetrievableAttribute : Attribute | ||
{ | ||
public IsRetrievableAttribute(bool isRetrievable) | ||
{ | ||
IsRetrievable = isRetrievable; | ||
} | ||
|
||
public bool IsRetrievable { get; } | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IsSearchableAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Causes the field to be included in full-text searches. Valid only for | ||
/// string or string collection fields. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IsSearchableAttribute : Attribute | ||
{ | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/Search/Microsoft.Azure.Search/Customizations/Indexes/IsSortableAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for | ||
// license information. | ||
|
||
namespace Microsoft.Azure.Search | ||
{ | ||
using System; | ||
|
||
using Microsoft.Azure.Search.Models; | ||
|
||
/// <summary> | ||
/// Indicates that the field can be used in orderby expressions. Not valid | ||
/// for string collection fields. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property)] | ||
public class IsSortableAttribute : Attribute | ||
{ | ||
} | ||
} |
Oops, something went wrong.