Skip to content

Commit 71e291e

Browse files
authored
Merge pull request #637 from gumbarros/dev
Add support for specifying non-clustered index sort direction
2 parents 4db075a + 44c7bb3 commit 71e291e

File tree

13 files changed

+249
-28
lines changed

13 files changed

+249
-28
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ columnOpts.Store.Add(StandardColumn.LogEvent);
110110
columnOpts.LogEvent.DataLength = 2048;
111111
columnOpts.PrimaryKey = columnOpts.TimeStamp;
112112
columnOpts.TimeStamp.NonClusteredIndex = true;
113+
columnOpts.TimeStamp.NonClusteredIndexDirection = SqlIndexDirection.Desc;
113114

114115
var log = new LoggerConfiguration()
115116
.WriteTo.MSSqlServer(
@@ -349,6 +350,7 @@ Each Standard Column in the `ColumnOptions.Store` list and any custom columns yo
349350
* `AllowNull`
350351
* `DataLength`
351352
* `NonClusteredIndex`
353+
* `NonClusteredIndexDirection`
352354

353355
### ColumnName
354356

@@ -412,9 +414,20 @@ Supported SQL column data types that use this property:
412414

413415
Any individual column can be defined as a non-clustered index, including the table primary key. Use this with caution, indexing carries a relatively high write-throughput penalty. One way to mitigate this is to keep non-clustered indexes offline and use batch reindexing on a scheduled basis.
414416

417+
### NonClusteredIndexDirection
418+
419+
Specifies the sort direction (`ASC` or `DESC`) for a non-clustered index on the SQL column.
420+
The default value is `ASC`.
421+
422+
It is especially useful for the timestamp column,
423+
where choosing the correct sort direction can optimize query performance for workloads that typically scan
424+
recent data first.
425+
426+
This only has effect if `NonClusteredIndex` is `true`.
427+
415428
## Standard Columns
416429

417-
By default (and consistent with the SQL DDL to create a table shown earlier) these columns are included in a new `ColumnOptions.Store` list:
430+
By default (and consistent with the SQL DDL to create a table shown earlier), these columns are included in a new `ColumnOptions.Store` list:
418431

419432
- `StandardColumn.Id`
420433
- `StandardColumn.Message`

sample/WorkerServiceDemo/appsettings.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
"removeStandardColumns": [ "MessageTemplate", "Properties" ],
2626
"timeStamp": {
2727
"columnName": "Timestamp",
28-
"convertToUtc": false
28+
"allowNulls": false,
29+
"convertToUtc": false,
30+
"nonClusteredIndex": true,
31+
"nonClusteredIndexDirection": "Desc"
2932
},
3033
"customColumns": [
3134
{
@@ -43,6 +46,12 @@
4346
"propertyName": "NonstructuredProperty.WithNameContainingDots.Name",
4447
"resolveHierarchicalPropertyName": false,
4548
"dataType": "12"
49+
},
50+
{
51+
"columnName": "AdditionalColumn4",
52+
"dataType": "0",
53+
"nonClusteredIndex": true,
54+
"nonClusteredIndexDirection": "Desc"
4655
}
4756
]
4857
},

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/Microsoft.Extensions.Configuration/MicrosoftExtensionsColumnOptionsProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ private static void SetCommonColumnOptions(IConfigurationSection section, SqlCol
3434
SetProperty.IfNotNull<bool>(section["allowNull"], (val) => target.AllowNull = val);
3535
SetProperty.IfNotNull<int>(section["dataLength"], (val) => target.DataLength = val);
3636
SetProperty.IfNotNull<bool>(section["nonClusteredIndex"], (val) => target.NonClusteredIndex = val);
37+
SetProperty.IfEnumNotNull<SqlIndexDirection>(section["nonClusteredIndexDirection"],
38+
(val) => target.NonClusteredIndexDirection = val);
3739
}
3840

3941
private static void AddRemoveStandardColumns(IConfigurationSection config, ColumnOptions columnOptions)

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/SetProperty.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,25 @@ public static void IfNotNull<T>(string value, PropertySetter<T> setter)
2929
var setting = (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
3030
setter(setting);
3131
}
32-
// don't change the property if the conversion fails
32+
// don't change the property if the conversion fails
33+
catch (InvalidCastException) { }
34+
catch (OverflowException) { }
35+
}
36+
37+
/// <summary>
38+
/// This will only set a value (execute the PropertySetter delegate) if the value is non-null.
39+
/// It also converts the provided value to the requested enum type. This allows configuration to only
40+
/// apply property changes when external configuration has actually provided a value.
41+
/// </summary>
42+
public static void IfEnumNotNull<T>(string value, PropertySetter<T> setter) where T : System.Enum
43+
{
44+
if (value == null || setter == null) return;
45+
try
46+
{
47+
var setting = (T)Enum.Parse(typeof(T), value, ignoreCase: true);
48+
setter(setting);
49+
}
50+
// don't change the property if the conversion fails
3351
catch (InvalidCastException) { }
3452
catch (OverflowException) { }
3553
}

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/ColumnConfig.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ public string NonClusteredIndex
8383
set { this[nameof(NonClusteredIndex)] = value; }
8484
}
8585

86+
[ConfigurationProperty("NonClusteredIndexDirection")]
87+
public string NonClusteredIndexDirection
88+
{
89+
get { return (string)this[nameof(NonClusteredIndexDirection)]; }
90+
set { this[nameof(NonClusteredIndexDirection)] = value; }
91+
}
92+
8693
internal SqlColumn AsSqlColumn()
8794
{
8895
var sqlColumn = new SqlColumn();
@@ -106,6 +113,9 @@ internal SqlColumn AsSqlColumn()
106113

107114
SetProperty.IfProvided<bool>(this, nameof(NonClusteredIndex), (val) => sqlColumn.NonClusteredIndex = val);
108115

116+
SetProperty.IfEnumProvided<SqlIndexDirection>(this, nameof(NonClusteredIndexDirection),
117+
(val) => sqlColumn.NonClusteredIndexDirection = val);
118+
109119
return sqlColumn;
110120
}
111121
}

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SetPropertyValueOrigin.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ public static void IfProvided<T>(ConfigurationElement element, string propertyNa
2222
IfNotNull((string)property.Value, setter);
2323
}
2424

25+
/// <summary>
26+
/// Test the underlying enum property collection's value-origin flag for a non-default string value. Empty strings allowed.
27+
/// </summary>
28+
public static void IfEnumProvided<T>(ConfigurationElement element, string propertyName, PropertySetter<T> setter)
29+
where T : System.Enum
30+
{
31+
if (element == null)
32+
{
33+
return;
34+
}
35+
36+
var property = element.ElementInformation.Properties[propertyName];
37+
if (property.ValueOrigin == PropertyValueOrigin.Default) return;
38+
IfEnumNotNull((string)property.Value, setter);
39+
}
40+
2541
/// <summary>
2642
/// Test the underlying property collection's value-origin flag for a non-default, non-null, non-empty string value.
2743
/// </summary>

src/Serilog.Sinks.MSSqlServer/Configuration/Implementations/System.Configuration/SystemConfigurationColumnOptionsProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ private static void SetCommonColumnOptions(ColumnConfig source, SqlColumn target
2626
SetProperty.IfProvided<bool>(source, nameof(target.AllowNull), value => target.AllowNull = value);
2727
SetProperty.IfProvided<int>(source, nameof(target.DataLength), value => target.DataLength = value);
2828
SetProperty.IfProvided<bool>(source, nameof(target.NonClusteredIndex), value => target.NonClusteredIndex = value);
29+
SetProperty.IfEnumProvided<SqlIndexDirection>(source, nameof(target.NonClusteredIndexDirection), value => target.NonClusteredIndexDirection = value);
2930
}
3031

3132
private static void ReadPropertiesColumnOptions(MSSqlServerConfigurationSection config, ColumnOptions columnOptions)

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/Platform/SqlCreateTableWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public string GetSql()
5454

5555
// collect non-PK indexes for separate output after the table DDL
5656
if (common != null && common.NonClusteredIndex && common != _columnOptions.PrimaryKey)
57-
ix.AppendLine(Invariant($"CREATE NONCLUSTERED INDEX [IX{indexCount++}_{_tableName}] ON [{_schemaName}].[{_tableName}] ([{common.ColumnName}]);"));
57+
ix.AppendLine(Invariant($"CREATE NONCLUSTERED INDEX [IX{indexCount++}_{_tableName}] ON [{_schemaName}].[{_tableName}] ([{common.ColumnName}] {common.NonClusteredIndexDirection});"));
5858
}
5959
}
6060

src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/SqlColumn.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ public SqlDbType DataType
100100
/// </summary>
101101
public bool NonClusteredIndex { get; set; }
102102

103+
/// <summary>
104+
/// Specifies the sort direction for a non-clustered index on the SQL column.
105+
/// The default value is <see cref="SqlIndexDirection.Asc"/>. This property is only used when auto-creating a log table.
106+
/// </summary>
107+
public SqlIndexDirection NonClusteredIndexDirection { get; set; }
108+
103109
/// <summary>
104110
/// The name of the Serilog property to use as the value when filling the DataTable.
105111
/// If not specified, the ColumnName and PropertyName are the same.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace Serilog.Sinks.MSSqlServer
2+
{
3+
/// <summary>
4+
/// Defines the sort order for an SQL index.
5+
/// </summary>
6+
public enum SqlIndexDirection
7+
{
8+
/// <summary>
9+
/// Represents the ascending direction for SQL indexing.
10+
/// </summary>
11+
Asc,
12+
13+
/// <summary>
14+
/// Represents the descending direction for SQL indexing.
15+
/// </summary>
16+
Desc
17+
}
18+
}

0 commit comments

Comments
 (0)