Skip to content

Commit

Permalink
RediSearch Tag map to String[]
Browse files Browse the repository at this point in the history
  • Loading branch information
2881099 committed Oct 23, 2024
1 parent f0be4f4 commit 697f35b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/FreeRedis/Internal/RespHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,9 @@ internal static string DisplayCsharp(this Type type, bool isNameSpace = true)
if (genericParameters.Any() == false)
return sb.Append(type.Name).ToString();

var idxof = type.Name.IndexOf('`');
if (idxof == -1) return sb.Append(type.Name).ToString();

sb.Append(type.Name.Remove(type.Name.IndexOf('`'))).Append("<");
var genericTypeIndex = 0;
foreach (var genericType in genericParameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,26 @@ public void CreateIndex()
void Save(T doc, RedisClient.PipelineHook pipe)
{
var key = $"{_schema.DocumentAttribute.Prefix}{_schema.KeyProperty.GetValue(doc, null)}";
var opts = _schema.Fields.Where((a, b) => b > 0).Select((a, b) => new object[] { a.FieldAttribute.FieldName, a.Property.GetValue(doc, null) }).SelectMany(a => a).ToArray();
var opts = _schema.Fields.Where((a, b) => b > 0).Select((a, b) => new object[]
{
a.FieldAttribute.FieldName,
toRedisValue(a)
}).SelectMany(a => a).ToArray();
var field = _schema.Fields[0].FieldAttribute.FieldName;
var value = _schema.Fields[0].Property.GetValue(doc, null);
if (pipe != null) pipe.HMSet(key, field, value, opts);
else _client.HMSet(key, field, value, opts);

object toRedisValue(DocumentSchemaFieldInfo dsf)
{
var val = dsf.Property.GetValue(doc, null);
if (dsf.FieldType == FieldType.Tag)
{
if (dsf.Property.PropertyType.IsArrayOrList())
val = string.Join((dsf.FieldAttribute as FtTagFieldAttribute).Separator ?? ",", typeof(string[]).FromObject(val) as string[]);
}
return val;
}
}
public void Save(T doc) => Save(doc, null);
public void Save(T[] docs) => Save(docs as IEnumerable<T>);
Expand Down Expand Up @@ -237,6 +252,14 @@ object toFtObject(object param)

return string.Concat("\"", param.ToString().Replace("\\", "\\\\").Replace("\"", "\\\""), "\"");
}
string toFtTagString(string expResultStr)
{
if (expResultStr == null) return "";
if (expResultStr.StartsWith("\"") && expResultStr.EndsWith("\""))
return expResultStr.Substring(1, expResultStr.Length - 2)
.Replace("\\\"", "\"").Replace("\\\\", "\\");
return expResultStr;
}
if (exp == null) return "";

switch (exp.NodeType)
Expand Down Expand Up @@ -341,6 +364,7 @@ string ParseMemberAccessDateTime()
case "System.String": callParseResult = ParseCallString(); break;
case "System.Math": callParseResult = ParseCallMath(); break;
case "System.DateTime": callParseResult = ParseCallDateTime(); break;
default: callParseResult = ParseCallOther(); break;
}
if (!string.IsNullOrEmpty(callParseResult)) return callParseResult;
break;
Expand Down Expand Up @@ -439,6 +463,44 @@ string ParseCallDateTime()
}
return null;
}
string ParseCallOther()
{
var objExp = callExp.Object;
var objType = objExp?.Type;
if (objType?.FullName == "System.Byte[]") return null;

var argIndex = 0;
if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable))
{
objExp = callExp.Arguments.FirstOrDefault();
objType = objExp?.Type;
argIndex++;

if (objType == typeof(string))
{
switch (callExp.Method.Name)
{
case "First":
case "FirstOrDefault":
return $"substr({parseExp(callExp.Arguments[0])},0,1)";
}
}
}
if (objType == null) objType = callExp.Method.DeclaringType;
if (objType != null || objType.IsArrayOrList())
{
string left = null;
switch (callExp.Method.Name)
{
case "Contains":

left = objExp == null ? null : parseExp(objExp);
var args1 = parseExp(callExp.Arguments[argIndex]);
return $"{left}:{{{toFtTagString(args1)}}}";
}
}
return null;
}
}
if (exp is BinaryExpression expBinary && expBinary != null)
{
Expand Down Expand Up @@ -467,7 +529,7 @@ string ParseCallDateTime()
if (field.FieldType == FieldType.Text)
return $"{(expBinary.NodeType == ExpressionType.NotEqual ? "-" : "")}{parseExp(expBinary.Left)}:{equalRight}";
else if (field.FieldType == FieldType.Tag)
return $"{(expBinary.NodeType == ExpressionType.NotEqual ? "-" : "")}{parseExp(expBinary.Left)}:{{{equalRight}}}";
return $"{(expBinary.NodeType == ExpressionType.NotEqual ? "-" : "")}{parseExp(expBinary.Left)}:{{{toFtTagString(equalRight)}}}";
}
return $"{(expBinary.NodeType == ExpressionType.NotEqual ? "-" : "")}{parseExp(expBinary.Left)}:[{equalRight} {equalRight}]";
case ExpressionType.Add:
Expand Down Expand Up @@ -586,8 +648,21 @@ public List<T> ToList(out long total)
var keyProperty = _repository._schema.KeyProperty;
var result = _searchBuilder.Execute();
total = result.Total;
return result.Documents.Select(doc => {
var item = doc.Body.MapToClass<T>();
var ttype = typeof(T);
return result.Documents.Select(doc =>
{
var item = (T)ttype.CreateInstanceGetDefaultValue();
foreach (var kv in doc.Body)
{
var name = kv.Key.Replace("-", "_");
var prop = ttype.GetPropertyOrFieldIgnoreCase(name);
if (prop == null) continue;
if (kv.Value == null) continue;
if (kv.Value is string valstr && _repository._schema.FieldsMap.TryGetValue(prop.Name, out var field) && field.FieldType == FieldType.Tag)
ttype.SetPropertyOrFieldValue(item, prop.Name, valstr.Split(new[] { (field.FieldAttribute as FtTagFieldAttribute).Separator ?? "," }, StringSplitOptions.None));
else
ttype.SetPropertyOrFieldValue(item, prop.Name, prop.GetPropertyOrFieldType().FromObject(kv.Value));
}
var itemId = doc.Id;
if (!string.IsNullOrEmpty(prefix))
if (itemId.StartsWith(prefix))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Xml.Linq;
using System.Linq;
using Xunit;

namespace FreeRedis.Tests.RedisClientTests.Other
Expand Down Expand Up @@ -51,6 +51,54 @@ class TestDoc
public int Views { get; set; }
}

[FtDocument("index_post100", Prefix = "blog:post:")]
class TagMapArrayIndex
{
[FtKey]
public int Id { get; set; }

[FtTextField("title", Weight = 5.0)]
public string Title { get; set; }

[FtTextField("category")]
public string Category { get; set; }

[FtTextField("content", Weight = 1.0, NoIndex = true)]
public string Content { get; set; }

[FtTagField("tags")]
public string[] Tags { get; set; }

[FtNumericField("views")]
public int Views { get; set; }
}
[Fact]
public void TagMapArray()
{
var repo = cli.FtDocumentRepository<TagMapArrayIndex>();

try
{
repo.DropIndex();
}
catch { }
repo.CreateIndex();

repo.Save(new TagMapArrayIndex { Id = 1, Title = "测试标题1 word", Category = "一级分类", Content = "测试内容1suffix", Tags = ["作者1","作者2"], Views = 101 });
repo.Save(new TagMapArrayIndex { Id = 2, Title = "prefix测试标题2", Category = "二级分类", Content = "测试infix内容2", Tags = ["作者2","作者3"], Views = 201 });
repo.Save(new TagMapArrayIndex { Id = 3, Title = "测试标题3 word", Category = "一级分类", Content = "测试word内容3", Tags = ["作者2","作者5"], Views = 301 });

repo.Delete(1, 2, 3);

repo.Save(new[]{
new TagMapArrayIndex { Id = 1, Title = "测试标题1 word", Category = "一级分类", Content = "测试内容1suffix", Tags = ["作者1","作者2"], Views = 101 },
new TagMapArrayIndex { Id = 2, Title = "prefix测试标题2", Category = "二级分类", Content = "测试infix内容2", Tags = ["作者2","作者3"], Views = 201 },
new TagMapArrayIndex { Id = 3, Title = "测试标题3 word", Category = "一级分类", Content = "测试word内容3", Tags = ["作者2","作者5"], Views = 301 }
});

var list = repo.Search(a => a.Tags.Contains("作者1")).ToList();
}

[Fact]
public void FtDocumentRepository()
{
Expand Down

0 comments on commit 697f35b

Please sign in to comment.