Skip to content

Commit

Permalink
adding ability to add prefixes to indexes for Searches
Browse files Browse the repository at this point in the history
  • Loading branch information
slorello89 committed Oct 26, 2022
1 parent 557123e commit eecc7a5
Show file tree
Hide file tree
Showing 16 changed files with 590 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/Redis.OM/Aggregation/RedisAggregationSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public RedisAggregationSet(IRedisConnection connection, bool useCursor = false,
}

_chunkSize = chunkSize;
Initialize(new RedisQueryProvider(connection, rootAttribute, _chunkSize, true), null, useCursor);
Initialize(new RedisQueryProvider(connection, rootAttribute, _chunkSize, true, string.Empty), null, useCursor);
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions src/Redis.OM/Common/ExpressionTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,19 @@ public static RedisAggregation BuildAggregationFromExpression(Expression express
/// <param name="expression">The expression.</param>
/// <param name="type">The root type.</param>
/// <param name="mainBooleanExpression">The primary boolean expression to build the filter from.</param>
/// <param name="prefix">The index to use when creating the indexName.</param>
/// <returns>A Redis query.</returns>
/// <exception cref="InvalidOperationException">Thrown if type is missing indexing.</exception>
internal static RedisQuery BuildQueryFromExpression(Expression expression, Type type, Expression? mainBooleanExpression)
internal static RedisQuery BuildQueryFromExpression(Expression expression, Type type, Expression? mainBooleanExpression, string prefix)
{
var attr = type.GetCustomAttribute<DocumentAttribute>();
if (attr == null)
{
throw new InvalidOperationException("Searches can only be performed on objects decorated with a RedisObjectDefinitionAttribute that specifies a particular index");
}

var indexName = string.IsNullOrEmpty(attr.IndexName) ? $"{type.Name.ToLower()}-idx" : attr.IndexName;
var prefixAddendum = string.IsNullOrEmpty(prefix) ? string.Empty : $"-{prefix}";
var indexName = string.IsNullOrEmpty(attr.IndexName) ? $"{type.Name.ToLower()}{prefixAddendum}-idx" : $"{attr.IndexName}{prefixAddendum}";
var query = new RedisQuery(indexName!) { QueryText = "*" };
switch (expression)
{
Expand Down
15 changes: 11 additions & 4 deletions src/Redis.OM/Modeling/RedisIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ internal static class RedisIndex
/// Serialize the Index.
/// </summary>
/// <param name="type">The type to be indexed.</param>
/// <param name="prefix">The Prefix to use for the index. If this is set, the prefixes property in the DocumentAttribute will be ignored.</param>
/// <exception cref="InvalidOperationException">Thrown if type provided is not decorated with a RedisObjectDefinitionAttribute.</exception>
/// <returns>An array of strings (the serialized args for redis).</returns>
internal static string[] SerializeIndex(this Type type)
internal static string[] SerializeIndex(this Type type, string prefix = "")
{
var objAttribute = Attribute.GetCustomAttribute(
type,
Expand All @@ -40,19 +41,25 @@ internal static string[] SerializeIndex(this Type type)
}

var args = new List<string>();
var prefixAddendum = string.IsNullOrEmpty(prefix) ? string.Empty : $"-{prefix}";
if (string.IsNullOrEmpty(objAttribute.IndexName))
{
args.Add($"{type.Name.ToLower()}-idx");
args.Add($"{type.Name.ToLower()}{prefixAddendum}-idx");
}
else
{
args.Add(objAttribute.IndexName!);
args.Add($"{objAttribute.IndexName!}{prefixAddendum}");
}

args.Add("ON");
args.Add(objAttribute.StorageType.ToString());
args.Add("PREFIX");
if (objAttribute.Prefixes.Length > 0)
if (!string.IsNullOrEmpty(prefix))
{
args.Add("1");
args.Add($"{prefix}:");
}
else if (objAttribute.Prefixes.Length > 0)
{
args.Add(objAttribute.Prefixes.Length.ToString());
args.AddRange(objAttribute.Prefixes);
Expand Down
156 changes: 156 additions & 0 deletions src/Redis.OM/RediSearchCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,32 @@ public static bool CreateIndex(this IRedisConnection connection, Type type)
}
}

/// <summary>
/// Creates an index.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to use for creating the index.</param>
/// <param name="prefix">The prefix for the index, this will override the prefix property in the type's DocumentAttribute.</param>
/// <returns>whether the index was created or not.</returns>
public static bool CreateIndex(this IRedisConnection connection, Type type, string prefix)
{
try
{
var serializedParams = type.SerializeIndex(prefix);
connection.Execute("FT.CREATE", serializedParams);
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Index already exists"))
{
return false;
}

throw;
}
}

/// <summary>
/// Creates an index.
/// </summary>
Expand All @@ -91,6 +117,32 @@ public static async Task<bool> CreateIndexAsync(this IRedisConnection connection
}
}

/// <summary>
/// Creates an index.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to use for creating the index.</param>
/// <param name="prefix">The prefix for the index, this will override the prefix property in the type's DocumentAttribute.</param>
/// <returns>whether the index was created or not.</returns>
public static async Task<bool> CreateIndexAsync(this IRedisConnection connection, Type type, string prefix)
{
try
{
var serializedParams = type.SerializeIndex(prefix);
await connection.ExecuteAsync("FT.CREATE", serializedParams);
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Index already exists"))
{
return false;
}

throw;
}
}

/// <summary>
/// Get index information.
/// </summary>
Expand Down Expand Up @@ -168,6 +220,32 @@ public static async Task<bool> DropIndexAsync(this IRedisConnection connection,
}
}

/// <summary>
/// Deletes an index.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to drop the index for.</param>
/// <param name="prefix">The prefix used to create the index.</param>
/// <returns>whether the index was dropped or not.</returns>
public static async Task<bool> DropIndexAsync(this IRedisConnection connection, Type type, string prefix)
{
try
{
var indexName = type.SerializeIndex(prefix).First();
await connection.ExecuteAsync("FT.DROPINDEX", indexName);
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Unknown Index name"))
{
return false;
}

throw;
}
}

/// <summary>
/// Deletes an index.
/// </summary>
Expand All @@ -193,6 +271,32 @@ public static bool DropIndex(this IRedisConnection connection, Type type)
}
}

/// <summary>
/// Deletes an index.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to drop the index for.</param>
/// <param name="prefix">The prefix used to create the index.</param>
/// <returns>whether the index was dropped or not.</returns>
public static bool DropIndex(this IRedisConnection connection, Type type, string prefix)
{
try
{
var indexName = type.SerializeIndex(prefix).First();
connection.Execute("FT.DROPINDEX", indexName);
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Unknown Index name"))
{
return false;
}

throw;
}
}

/// <summary>
/// Deletes an index. And drops associated records.
/// </summary>
Expand All @@ -218,6 +322,58 @@ public static bool DropIndexAndAssociatedRecords(this IRedisConnection connectio
}
}

/// <summary>
/// Deletes an index. And drops associated records.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to drop the index for.</param>
/// <param name="prefix">The prefix associated with the index.</param>
/// <returns>whether the index was dropped or not.</returns>
public static bool DropIndexAndAssociatedRecords(this IRedisConnection connection, Type type, string prefix)
{
try
{
var indexName = type.SerializeIndex(prefix).First();
connection.Execute("FT.DROPINDEX", indexName, "DD");
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Unknown Index name"))
{
return false;
}

throw;
}
}

/// <summary>
/// Deletes an index. And drops associated records.
/// </summary>
/// <param name="connection">the connection.</param>
/// <param name="type">the type to drop the index for.</param>
/// <param name="prefix">The prefix associated with the index.</param>
/// <returns>whether the index was dropped or not.</returns>
public static async Task<bool> DropIndexAndAssociatedRecordsAsync(this IRedisConnection connection, Type type, string prefix)
{
try
{
var indexName = type.SerializeIndex(prefix).First();
await connection.ExecuteAsync("FT.DROPINDEX", indexName, "DD");
return true;
}
catch (Exception ex)
{
if (ex.Message.Contains("Unknown Index name"))
{
return false;
}

throw;
}
}

/// <summary>
/// Search redis with the given query.
/// </summary>
Expand Down
54 changes: 46 additions & 8 deletions src/Redis.OM/RedisCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,18 @@ static RedisCommands()
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <returns>the key for the object.</returns>
public static async Task<string> SetAsync(this IRedisConnection connection, object obj)
public static Task<string> SetAsync(this IRedisConnection connection, object obj) => connection.SetAsync(obj, null);

/// <summary>
/// Serializes an object to either hash or json (depending on how it's decorated), and saves it in redis.
/// </summary>
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <param name="prefix">The prefix to use when generating the keyname for the object inserted.</param>
/// <returns>the key for the object.</returns>
public static async Task<string> SetAsync(this IRedisConnection connection, object obj, string? prefix)
{
var id = obj.SetId();
var id = obj.SetId(prefix);
var type = obj.GetType();
var attr = Attribute.GetCustomAttribute(type, typeof(DocumentAttribute)) as DocumentAttribute;
if (attr == null || attr.StorageType == StorageType.Hash)
Expand Down Expand Up @@ -64,9 +73,19 @@ public static async Task<string> SetAsync(this IRedisConnection connection, obje
/// <param name="obj">the object to save.</param>
/// <param name="timeSpan">the expiry date of the key (TTL).</param>
/// <returns>the key for the object.</returns>
public static async Task<string> SetAsync(this IRedisConnection connection, object obj, TimeSpan timeSpan)
public static Task<string> SetAsync(this IRedisConnection connection, object obj, TimeSpan timeSpan) => connection.SetAsync(obj, timeSpan, null);

/// <summary>
/// Serializes an object to either hash or json (depending on how it's decorated), and saves it in redis.
/// </summary>
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <param name="timeSpan">the expiry date of the key (TTL).</param>
/// /// <param name="prefix">The prefix to use when generating the keyname for the object inserted.</param>
/// <returns>the key for the object.</returns>
public static async Task<string> SetAsync(this IRedisConnection connection, object obj, TimeSpan timeSpan, string? prefix)
{
var id = obj.SetId();
var id = obj.SetId(prefix);
var type = obj.GetType();
var attr = Attribute.GetCustomAttribute(type, typeof(DocumentAttribute)) as DocumentAttribute;
if (attr == null || attr.StorageType == StorageType.Hash)
Expand Down Expand Up @@ -292,9 +311,18 @@ public static bool JsonSet(this IRedisConnection connection, string key, string
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <returns>the key for the object.</returns>
public static string Set(this IRedisConnection connection, object obj)
public static string Set(this IRedisConnection connection, object obj) => connection.Set(obj, null);

/// <summary>
/// Serializes an object to either hash or json (depending on how it's decorated), and saves it in redis.
/// </summary>
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <param name="prefix">The overriding prefix to use for the keyname.</param>
/// <returns>the key for the object.</returns>
public static string Set(this IRedisConnection connection, object obj, string? prefix)
{
var id = obj.SetId();
var id = obj.SetId(prefix);
var type = obj.GetType();
if (Attribute.GetCustomAttribute(type, typeof(DocumentAttribute)) is not DocumentAttribute attr || attr.StorageType == StorageType.Hash)
{
Expand Down Expand Up @@ -322,9 +350,19 @@ public static string Set(this IRedisConnection connection, object obj)
/// <param name="obj">the object to save.</param>
/// <param name="timeSpan">the the timespan to set for your (TTL).</param>
/// <returns>the key for the object.</returns>
public static string Set(this IRedisConnection connection, object obj, TimeSpan timeSpan)
public static string Set(this IRedisConnection connection, object obj, TimeSpan timeSpan) => connection.Set(obj, timeSpan, null);

/// <summary>
/// Serializes an object to either hash or json (depending on how it's decorated), and saves it in redis.
/// </summary>
/// <param name="connection">connection to redis.</param>
/// <param name="obj">the object to save.</param>
/// <param name="timeSpan">the the timespan to set for your (TTL).</param>
/// <param name="prefix">The overriden prefix to use for the keyname.</param>
/// <returns>the key for the object.</returns>
public static string Set(this IRedisConnection connection, object obj, TimeSpan timeSpan, string? prefix)
{
var id = obj.SetId();
var id = obj.SetId(prefix);
var type = obj.GetType();
if (Attribute.GetCustomAttribute(type, typeof(DocumentAttribute)) is not DocumentAttribute attr || attr.StorageType == StorageType.Hash)
{
Expand Down
13 changes: 12 additions & 1 deletion src/Redis.OM/RedisConnectionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ public IRedisCollection<T> RedisCollection<T>(int chunkSize = 100)
/// <param name="chunkSize">Size of chunks to use during pagination, larger chunks = larger payloads returned but fewer round trips.</param>
/// <returns>A RedisCollection.</returns>
public IRedisCollection<T> RedisCollection<T>(bool saveState, int chunkSize = 100)
where T : notnull => new RedisCollection<T>(Connection, saveState, chunkSize);
where T : notnull => new RedisCollection<T>(Connection, saveState, chunkSize, string.Empty);

/// <summary>
/// Gets a redis collection.
/// </summary>
/// <typeparam name="T">The type the collection will be retrieving.</typeparam>
/// <param name="saveState">Whether or not the RedisCollection should maintain the state of documents it enumerates.</param>
/// <param name="chunkSize">Size of chunks to use during pagination, larger chunks = larger payloads returned but fewer round trips.</param>
/// <param name="prefix">The Prefix to use when creating index, as well as for inserting keys and querying them.</param>
/// <returns>A RedisCollection.</returns>
public IRedisCollection<T> RedisCollection<T>(bool saveState, int chunkSize, string prefix)
where T : notnull => new RedisCollection<T>(Connection, saveState, chunkSize, prefix);
}
}
Loading

0 comments on commit eecc7a5

Please sign in to comment.