diff --git a/src/NRedisStack/Graph/DataTypes/Edge.cs b/src/NRedisStack/Graph/DataTypes/Edge.cs index 3e446f22..24e7df5b 100644 --- a/src/NRedisStack/Graph/DataTypes/Edge.cs +++ b/src/NRedisStack/Graph/DataTypes/Edge.cs @@ -26,7 +26,6 @@ public class Edge : GraphEntity /// public long Destination { get; set; } - // TODO: check if this is needed: /// /// Overriden from the base `Equals` implementation. In addition to the expected behavior of checking /// reference equality, we'll also fall back and check to see if the: Source, Destination, and RelationshipType diff --git a/src/NRedisStack/Graph/DataTypes/GraphEntity.cs b/src/NRedisStack/Graph/DataTypes/GraphEntity.cs index 58a5391e..6e3401c9 100644 --- a/src/NRedisStack/Graph/DataTypes/GraphEntity.cs +++ b/src/NRedisStack/Graph/DataTypes/GraphEntity.cs @@ -12,7 +12,6 @@ public abstract class GraphEntity public IDictionary PropertyMap = new Dictionary(); - // TODO: check if this is needed: /// /// Overriden Equals that considers the equality of the entity ID as well as the equality of the /// properties that each entity has. @@ -49,7 +48,7 @@ public override int GetHashCode() hash = hash * 31 + Id.GetHashCode(); - foreach(var prop in PropertyMap) + foreach (var prop in PropertyMap) { hash = hash * 31 + prop.Key.GetHashCode(); hash = hash * 31 + prop.Value.GetHashCode(); @@ -59,23 +58,6 @@ public override int GetHashCode() } } - /// - /// Overriden ToString that emits a string containing the ID and property map of the entity. - /// - /// - public override string ToString() - { - var sb = new StringBuilder(); - - sb.Append("GraphEntity{id="); - sb.Append(Id); - sb.Append(", propertyMap="); - sb.Append(PropertyMap); - sb.Append('}'); - - return sb.ToString(); - } - public string PropertyMapToString() { var sb = new StringBuilder(); diff --git a/src/NRedisStack/Graph/DataTypes/Node.cs b/src/NRedisStack/Graph/DataTypes/Node.cs index 92fda49b..05a30e86 100644 --- a/src/NRedisStack/Graph/DataTypes/Node.cs +++ b/src/NRedisStack/Graph/DataTypes/Node.cs @@ -16,7 +16,6 @@ public Node() Labels = new List(); } - // TODO: check if this is needed: /// /// Overriden member that checks to see if the names of the labels of a node are equal /// (in addition to base `Equals` functionality). diff --git a/src/NRedisStack/Graph/DataTypes/Path.cs b/src/NRedisStack/Graph/DataTypes/Path.cs index 0efa1dde..02af0c8e 100644 --- a/src/NRedisStack/Graph/DataTypes/Path.cs +++ b/src/NRedisStack/Graph/DataTypes/Path.cs @@ -20,7 +20,6 @@ public Path(IList nodes, IList edges) Edges = new ReadOnlyCollection(edges); } - // TODO: check if this is needed: /// /// Overriden `Equals` method that will consider the equality of the Nodes and Edges between two paths. /// @@ -68,7 +67,6 @@ public override int GetHashCode() } } - // TODO: check if this is needed: /// /// Overridden `ToString` method that will emit a string based on the string values of the nodes and edges /// on the path. diff --git a/src/NRedisStack/Graph/GraphCacheList.cs b/src/NRedisStack/Graph/GraphCacheList.cs index 74db8abe..b6e16d92 100644 --- a/src/NRedisStack/Graph/GraphCacheList.cs +++ b/src/NRedisStack/Graph/GraphCacheList.cs @@ -52,15 +52,4 @@ private void GetProcedureInfo() protected virtual ResultSet CallProcedure() => graph.CallProcedure(GraphName, Procedure); } - - internal class ReadOnlyGraphCacheList : GraphCacheList - { - internal ReadOnlyGraphCacheList(string graphName, string procedure, GraphCommands redisGraph) : - base(graphName, procedure, redisGraph) - { - } - - protected override ResultSet CallProcedure() => - graph.CallProcedureReadOnly(GraphName, Procedure); - } } \ No newline at end of file diff --git a/src/NRedisStack/Graph/GraphCommandBuilder.cs b/src/NRedisStack/Graph/GraphCommandBuilder.cs index ed56b4dd..a2d259ff 100644 --- a/src/NRedisStack/Graph/GraphCommandBuilder.cs +++ b/src/NRedisStack/Graph/GraphCommandBuilder.cs @@ -1,6 +1,5 @@ using NRedisStack.Literals; using NRedisStack.RedisStackCommands; -using static NRedisStack.Graph.RedisGraphUtilities; namespace NRedisStack { diff --git a/src/NRedisStack/Graph/GraphCommands.cs b/src/NRedisStack/Graph/GraphCommands.cs index 3779b961..a1363f6b 100644 --- a/src/NRedisStack/Graph/GraphCommands.cs +++ b/src/NRedisStack/Graph/GraphCommands.cs @@ -97,7 +97,7 @@ public async Task RO_QueryAsync(string graphName, string query, long? internal static readonly Dictionary> EmptyKwargsDictionary = new Dictionary>(); - // TODO: Check if needed + // TODO: Check if this is needed: /// public ResultSet CallProcedure(string graphName, string procedure) => CallProcedure(graphName, procedure, Enumerable.Empty(), EmptyKwargsDictionary); @@ -147,32 +147,6 @@ public async Task DeleteAsync(string graphName) return processedResult; } - // TODO: Check if this (CallProcedure) is needed - /// - public ResultSet CallProcedureReadOnly(string graphName, string procedure) => - CallProcedureReadOnly(graphName, procedure, Enumerable.Empty(), EmptyKwargsDictionary); - - /// - public ResultSet CallProcedureReadOnly(string graphName, string procedure, IEnumerable args) => - CallProcedureReadOnly(graphName, procedure, args, EmptyKwargsDictionary); - - /// - public ResultSet CallProcedureReadOnly(string graphName, string procedure, IEnumerable args, Dictionary> kwargs) - { - args = args.Select(a => QuoteString(a)); - - var queryBody = new StringBuilder(); - - queryBody.Append($"CALL {procedure}({string.Join(",", args)})"); - - if (kwargs.TryGetValue("y", out var kwargsList)) - { - queryBody.Append(string.Join(",", kwargsList)); - } - - return RO_Query(graphName, queryBody.ToString()); - } - /// public IReadOnlyList Explain(string graphName, string query) { diff --git a/src/NRedisStack/Graph/Header.cs b/src/NRedisStack/Graph/Header.cs index ea9a6259..3101986c 100644 --- a/src/NRedisStack/Graph/Header.cs +++ b/src/NRedisStack/Graph/Header.cs @@ -20,7 +20,7 @@ public enum ResultSetColumnTypes /// /// Collection of the schema types present in the header. /// - // [Obsolete("SchemaType is no longer supported after RedisGraph 2.1 and will always return COLUMN_SCALAR")] // TODO: it's correct? + [Obsolete("SchemaType is no longer supported after RedisGraph 2.1 and will always return COLUMN_SCALAR")] public List SchemaTypes { get; } /// @@ -41,7 +41,6 @@ internal Header(RedisResult result) } } - // TODO: check if this is needed: public override bool Equals(object? obj) { if (obj == null) return this == null; diff --git a/src/NRedisStack/Graph/IGraphCommands.cs b/src/NRedisStack/Graph/IGraphCommands.cs index cf374eb8..e2470b64 100644 --- a/src/NRedisStack/Graph/IGraphCommands.cs +++ b/src/NRedisStack/Graph/IGraphCommands.cs @@ -133,34 +133,6 @@ public interface IGraphCommands /// Task DeleteAsync(string graphName); - // TODO: Check if this (CallProcedure) is needed - /// - /// Call a saved procedure against a read-only node. - /// - /// The graph containing the saved procedure. - /// The procedure name. - /// A result set. - ResultSet CallProcedureReadOnly(string graphName, string procedure); - - /// - /// Call a saved procedure with parameters against a read-only node. - /// - /// The graph containing the saved procedure. - /// The procedure name. - /// A collection of positional arguments. - /// A result set. - ResultSet CallProcedureReadOnly(string graphName, string procedure, IEnumerable args); - - /// - /// Call a saved procedure with parameters against a read-only node. - /// - /// The graph containing the saved procedure. - /// The procedure name. - /// A collection of positional arguments. - /// A collection of keyword arguments. - /// A result set. - ResultSet CallProcedureReadOnly(string graphName, string procedure, IEnumerable args, Dictionary> kwargs); - /// /// Constructs a query execution plan but does not run it. Inspect this execution plan to better understand how your /// query will get executed. diff --git a/src/NRedisStack/Graph/Point.cs b/src/NRedisStack/Graph/Point.cs index a1f9a226..4bb562fb 100644 --- a/src/NRedisStack/Graph/Point.cs +++ b/src/NRedisStack/Graph/Point.cs @@ -23,7 +23,6 @@ public Point(List values) this.longitude = values[1]; } - // TODO: check if this is needed: public override bool Equals(object? obj) { if (obj == null) return this == null; diff --git a/src/NRedisStack/Graph/Record.cs b/src/NRedisStack/Graph/Record.cs index 84a9346c..be2346ec 100644 --- a/src/NRedisStack/Graph/Record.cs +++ b/src/NRedisStack/Graph/Record.cs @@ -58,7 +58,6 @@ internal Record(List header, List values) /// public int Size => Header.Count; - // TODO: check if this is needed: public override bool Equals(object? obj) { if (obj == null) return this == null; diff --git a/src/NRedisStack/Graph/RedisGraphUtilities.cs b/src/NRedisStack/Graph/RedisGraphUtilities.cs index 5f10cd74..a13f1049 100644 --- a/src/NRedisStack/Graph/RedisGraphUtilities.cs +++ b/src/NRedisStack/Graph/RedisGraphUtilities.cs @@ -22,21 +22,6 @@ internal static string PrepareQuery(string query, IDictionary pa return preparedQuery.ToString(); } - public static string ValueToStringNoQuotes(object value) - { - if (value == null) - { - return "null"; - } - - if (value is IConvertible floatValue) - { - return ConvertibleToString(floatValue); - } - - return value.ToString(); - } - public static string ValueToString(object value) { if (value == null) diff --git a/src/NRedisStack/Json/JsonCommandBuilder.cs b/src/NRedisStack/Json/JsonCommandBuilder.cs index 3f06ede9..4924a346 100644 --- a/src/NRedisStack/Json/JsonCommandBuilder.cs +++ b/src/NRedisStack/Json/JsonCommandBuilder.cs @@ -113,7 +113,7 @@ public static SerializedCommand ArrPop(RedisKey key, string? path = null, long? throw new ArgumentException("index cannot be defined without path"); var args = AssembleNonNullArguments(key, path, index); - return new SerializedCommand(JSON.ARRPOP, args)!; // TODO: understand the meaning of the '!' here + return new SerializedCommand(JSON.ARRPOP, args)!; } public static SerializedCommand ArrTrim(RedisKey key, string path, long start, long stop) => diff --git a/src/NRedisStack/Json/JsonCommands.cs b/src/NRedisStack/Json/JsonCommands.cs index 8c2a2116..a1ec708f 100644 --- a/src/NRedisStack/Json/JsonCommands.cs +++ b/src/NRedisStack/Json/JsonCommands.cs @@ -250,7 +250,6 @@ public async Task SetAsync(RedisKey key, RedisValue path, RedisValue json, return (await _db.ExecuteAsync(JsonCommandBuilder.Set(key, path, json, when))).OKtoBoolean(); } - /// // TODO: check way asnyc methods dont have documenation public async Task SetFromFileAsync(RedisKey key, RedisValue path, string filePath, When when = When.Always) { if (!File.Exists(filePath)) @@ -262,7 +261,6 @@ public async Task SetFromFileAsync(RedisKey key, RedisValue path, string f return await SetAsync(key, path, fileContent, when); } - /// public async Task SetFromDirectoryAsync(RedisValue path, string filesPath, When when = When.Always) { int inserted = 0; @@ -285,7 +283,6 @@ public async Task SetFromDirectoryAsync(RedisValue path, string filesPath, return inserted; } - public async Task StrAppendAsync(RedisKey key, string value, string? path = null) { return (await _db.ExecuteAsync(JsonCommandBuilder.StrAppend(key, value, path))).ToNullableLongArray(); diff --git a/src/NRedisStack/Search/AggregationRequest.cs b/src/NRedisStack/Search/AggregationRequest.cs index d8b56c53..e6d5f705 100644 --- a/src/NRedisStack/Search/AggregationRequest.cs +++ b/src/NRedisStack/Search/AggregationRequest.cs @@ -52,10 +52,9 @@ public AggregationRequest Limit(int offset, int count) return this; } - public AggregationRequest Limit(int count) - { - return Limit(0, count); - } + public AggregationRequest Limit(int count) => Limit(0, count); + + public AggregationRequest SortBy(string property) => SortBy(SortedField.Asc(property)); public AggregationRequest SortBy(params SortedField[] Fields) { diff --git a/src/NRedisStack/Search/AggregationResult.cs b/src/NRedisStack/Search/AggregationResult.cs index 03545895..bd3af50a 100644 --- a/src/NRedisStack/Search/AggregationResult.cs +++ b/src/NRedisStack/Search/AggregationResult.cs @@ -26,8 +26,9 @@ internal AggregationResult(RedisResult result, long cursorId = -1) { var key = (string)raw[j++]; var val = raw[j++]; - if (val.Type != ResultType.MultiBulk) - cur.Add(key, (RedisValue)val); + if (val.Type == ResultType.MultiBulk) + continue; // TODO: handle multi-bulk (maybe change to object?) + cur.Add(key, (RedisValue)val); } _results[i - 1] = cur; } diff --git a/src/NRedisStack/Search/DataTypes/InfoResult.cs b/src/NRedisStack/Search/DataTypes/InfoResult.cs index 1040f440..81a8350d 100644 --- a/src/NRedisStack/Search/DataTypes/InfoResult.cs +++ b/src/NRedisStack/Search/DataTypes/InfoResult.cs @@ -9,10 +9,8 @@ public class InfoResult public string IndexName => GetString("index_name"); public Dictionary IndexOption => GetRedisResultDictionary("index_options"); - public Dictionary IndexDefinition => GetRedisResultsDictionary("index_definition"); - // public Dictionary Attributes => GetRedisResultsDictionary("attributes"); // TODO: check if this is correct - public Dictionary[] Attributes => GetRedisResultDictionaryArray("attributes"); // TODO: check if this is correct + public Dictionary[] Attributes => GetRedisResultDictionaryArray("attributes"); public long NumDocs => GetLong("num_docs"); @@ -122,25 +120,6 @@ private Dictionary GetRedisResultDictionary(string key) } } - private Dictionary GetRedisResultsDictionary(string key) - { - if (_all.TryGetValue(key, out var value)) - { - var result = new Dictionary(); - - foreach (RedisResult[] fv in (RedisResult[])value) - { - result.Add((string)fv[0], fv); - } - - return result; - } - else - { - return default; - } - } - private Dictionary[] GetRedisResultDictionaryArray(string key) { if (_all.TryGetValue(key, out var value)) @@ -165,25 +144,5 @@ private Dictionary[] GetRedisResultDictionaryArray(string k return default; } } - // private Dictionary[] GetRedisResultsDictionaryTry(string key) - // { - // if (_all.TryGetValue(key, out var value)) - // { - // var result = new List>(); - - // int i = 0; - // foreach (RedisResult[] fv in (RedisResult[])value) - // { - // var res = GetRedisResultDictionary((string)fv[i++]); - // result.Add(res); - // } - - // return result.ToArray(); - // } - // else - // { - // return default; - // } - // } } } \ No newline at end of file diff --git a/src/NRedisStack/Search/DataTypes/SearchInformation.cs b/src/NRedisStack/Search/DataTypes/SearchInformation.cs deleted file mode 100644 index 26c6cb63..00000000 --- a/src/NRedisStack/Search/DataTypes/SearchInformation.cs +++ /dev/null @@ -1,34 +0,0 @@ -// namespace NRedisStack.Search.DataTypes -// { -// /// -// /// This class represents the response for SEARCH.INFO command. -// /// This object has Read-only properties and cannot be generated outside a SEARCH.INFO response. -// /// -// public class SearchInformation -// { -// // TODO: work on it with someone from Search team -// // public string IndexName { get; private set; } -// // public string[] IndexOptions { get; private set; } -// // public long IndexDefinition { get; private set; } -// // public long UnmergedNodes { get; private set; } -// // public double MergedWeight { get; private set; } -// // public double UnmergedWeight { get; private set; } - -// // public long TotalCompressions { get; private set; } - - -// // internal SearchInformation(long compression, long capacity, long mergedNodes, -// // long unmergedNodes, double mergedWeight, -// // double unmergedWeight, long totalCompressions) - -// // { -// // Compression = compression; -// // Capacity = capacity; -// // MergedNodes = mergedNodes; -// // UnmergedNodes = unmergedNodes; -// // MergedWeight = mergedWeight; -// // UnmergedWeight = unmergedWeight; -// // TotalCompressions = totalCompressions; -// // } -// } -// } \ No newline at end of file diff --git a/src/NRedisStack/Search/Document.cs b/src/NRedisStack/Search/Document.cs index 96880700..a118adbf 100644 --- a/src/NRedisStack/Search/Document.cs +++ b/src/NRedisStack/Search/Document.cs @@ -63,21 +63,6 @@ public RedisValue this[string key] internal set { _properties[key] = value; } } - public bool HasProperty(string key) => _properties.ContainsKey(key); - - internal static Document Parse(string docId, RedisResult result) - { - if (result == null || result.IsNull) return null; - var arr = (RedisResult[])result; - var doc = new Document(docId); - - for(int i = 0; i < arr.Length; ) - { - doc[(string)arr[i++]] = (RedisValue)arr[i++]; - } - return doc; - } - public Document Set(string field, RedisValue value) { this[field] = value; diff --git a/src/NRedisStack/Search/Extensions/IndexDataTypeExtensions.cs b/src/NRedisStack/Search/Extensions/IndexDataTypeExtensions.cs deleted file mode 100644 index 13d59c3c..00000000 --- a/src/NRedisStack/Search/Extensions/IndexDataTypeExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using NRedisStack.Literals.Enums; - -namespace NRedisStack.Extensions -{ - internal static class IndexIndexDataType - { - public static string AsArg(this IndexDataType dataType) => dataType switch - { - IndexDataType.Hash => "HASH", - IndexDataType.Json => "JSON", - _ => throw new ArgumentOutOfRangeException(nameof(dataType), "Invalid Index DataType"), - }; - - public static IndexDataType AsDataType(string dataType) => dataType switch - { - "HASH" => IndexDataType.Hash, - "JSON" => IndexDataType.Json, - _ => throw new ArgumentOutOfRangeException(nameof(dataType), $"Invalid Index DataType '{dataType}'"), - }; - } -} \ No newline at end of file diff --git a/src/NRedisStack/Search/FT.CREATE/FTCreateParams.cs b/src/NRedisStack/Search/FTCreateParams.cs similarity index 99% rename from src/NRedisStack/Search/FT.CREATE/FTCreateParams.cs rename to src/NRedisStack/Search/FTCreateParams.cs index 755a86f1..96c56236 100644 --- a/src/NRedisStack/Search/FT.CREATE/FTCreateParams.cs +++ b/src/NRedisStack/Search/FTCreateParams.cs @@ -225,7 +225,7 @@ public void AddParams(List args) if (dataType != default(IndexDataType)) { args.Add("ON"); - args.Add(dataType.AsArg()); + args.Add(dataType.ToString()); } if (prefixes != null) diff --git a/src/NRedisStack/Search/FieldName.cs b/src/NRedisStack/Search/FieldName.cs index e277a30f..2ce9f850 100644 --- a/src/NRedisStack/Search/FieldName.cs +++ b/src/NRedisStack/Search/FieldName.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Text; - namespace NRedisStack.Search { public class FieldName @@ -39,15 +36,5 @@ public FieldName As(string attribute) this.alias = attribute; return this; } - - public static FieldName[] convert(params string[] names) - { - if (names == null) return null; - FieldName[] fields = new FieldName[names.Length]; - for (int i = 0; i < names.Length; i++) - fields[i] = FieldName.Of(names[i]); - - return fields; - } } } \ No newline at end of file diff --git a/src/NRedisStack/Search/Group.cs b/src/NRedisStack/Search/Group.cs index 36bca902..57a894b3 100644 --- a/src/NRedisStack/Search/Group.cs +++ b/src/NRedisStack/Search/Group.cs @@ -7,7 +7,7 @@ public class Group private readonly IList _reducers = new List(); private readonly IList _fields; - private Limit _limit = new Limit(0, 0); + private Limit _limit = Aggregation.Limit.NO_LIMIT; public Group(params string[] fields) => _fields = fields; public Group(IList fields) => _fields = fields; @@ -43,12 +43,5 @@ internal void SerializeRedisArgs(List args) } _limit.SerializeRedisArgs(args); } - - public List getArgs() - { - List args = new List(); - SerializeRedisArgs(args); - return args; - } } } \ No newline at end of file diff --git a/src/NRedisStack/Search/Literals/Enums/IndexDataType.cs b/src/NRedisStack/Search/Literals/Enums/IndexDataType.cs index e4b9ae34..f699d843 100644 --- a/src/NRedisStack/Search/Literals/Enums/IndexDataType.cs +++ b/src/NRedisStack/Search/Literals/Enums/IndexDataType.cs @@ -2,7 +2,8 @@ namespace NRedisStack.Literals.Enums { public enum IndexDataType { - Json, - Hash, + HASH, + JSON, + } } \ No newline at end of file diff --git a/src/NRedisStack/Search/Query.cs b/src/NRedisStack/Search/Query.cs index e0bcf33b..5ecdc504 100644 --- a/src/NRedisStack/Search/Query.cs +++ b/src/NRedisStack/Search/Query.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; -using System.Globalization; +using System.Globalization; using NRedisStack.Literals; -using NRedisStack.Search; using StackExchange.Redis; namespace NRedisStack.Search @@ -170,7 +168,7 @@ public HighlightTags(string open, string close) /// /// Set the query payload to be evaluated by the scoring function /// - public byte[] Payload { get; set; } + public string Payload { get; set; } // TODO: should this be a byte[]? // TODO: Check if I need to add here WITHSORTKEYS @@ -191,7 +189,7 @@ public HighlightTags(string open, string close) /// Set the query scoring. see https://oss.redislabs.com/redisearch/Scoring.html for documentation /// public string Scorer { get; set; } - public bool ExplainScore { get; set; } // TODO: Check if this is needed because Jedis doesn't have it + // public bool ExplainScore { get; set; } // TODO: Check if this is needed because Jedis doesn't have it private Dictionary _params = null; private int _dialect = 0; @@ -230,6 +228,10 @@ internal void SerializeRedisArgs(List args) if (WithScores) { args.Add("WITHSCORES"); + // if (ExplainScore) + // { + // args.Add("EXPLAINSCORE"); // TODO: Check Why Jedis doesn't have it + // } } if (WithPayloads) { @@ -245,11 +247,6 @@ internal void SerializeRedisArgs(List args) { args.Add("SCORER"); args.Add(Scorer); - - if (ExplainScore) - { - args.Add("EXPLAINSCORE"); // TODO: Check Why Jedis doesn't have it - } } if (_fields?.Length > 0) @@ -265,7 +262,7 @@ internal void SerializeRedisArgs(List args) args.Add(SortBy); args.Add((SortAscending ? "ASC" : "DESC")); } - if (Payload != null) + if (Payload != null) { args.Add("PAYLOAD"); args.Add(Payload); @@ -346,24 +343,20 @@ internal void SerializeRedisArgs(List args) } } - if (_keys?.Length > 0) - { - args.Add("INKEYS"); - args.Add(_keys.Length); - args.AddRange(_keys); - } if (_returnFields?.Length > 0) { args.Add("RETURN"); args.Add(_returnFields.Length); args.AddRange(_returnFields); } + else if (_returnFieldsNames?.Length > 0) { args.Add("RETURN"); int returnCountIndex = args.Count; int returnCount = 0; - foreach (FieldName fn in _returnFieldsNames) { + foreach (FieldName fn in _returnFieldsNames) + { returnCount += fn.AddCommandArguments(args); } @@ -440,7 +433,7 @@ public Query AddFilter(Filter f) /// /// the payload /// the query itself - public Query SetPayload(byte[] payload) + public Query SetPayload(string payload) { Payload = payload; return this; @@ -491,7 +484,7 @@ public Query SetWithScores(bool value = true) /// Set the query to return object payloads, if any were given /// /// the query itself - public Query SetWithPayload() + public Query SetWithPayloads() { WithPayloads = true; return this; @@ -518,6 +511,20 @@ public Query SetScorer(string scorer) Scorer = scorer; return this; } + + // TODO: check if this is needed (Jedis doesn't have it) + // /// + // /// returns a textual description of how the scores were calculated. + // /// Using this options requires the WITHSCORES option. + // /// + // /// + // /// + // public Query SetExplainScore(bool explainScore = true) + // { + // ExplainScore = explainScore; + // return this; + // } + /// /// Limit the query to results that are limited to a specific set of fields /// @@ -678,4 +685,4 @@ public Query SetExpander(String field) return this; } } -} +} \ No newline at end of file diff --git a/src/NRedisStack/Search/Reducer.cs b/src/NRedisStack/Search/Reducer.cs index 212de35c..0a870024 100644 --- a/src/NRedisStack/Search/Reducer.cs +++ b/src/NRedisStack/Search/Reducer.cs @@ -1,12 +1,7 @@ -using System.Collections.Generic; - -namespace NRedisStack.Search.Aggregation +namespace NRedisStack.Search.Aggregation { public abstract class Reducer { - - public override string ToString() => Name; - // internal Reducer(string field) => _field = field; /// @@ -32,38 +27,13 @@ protected virtual void AddOwnArgs(List args) if (_field != null) args.Add(_field); } - /** - * @return The name of the reducer - */ - // public abstract string getName(); - - // public string getAlias() - // { - // return Alias; - // } - - // public Reducer setAlias(string alias) - // { - // this.Alias = alias; - // return this; - // } - - // public final Reducer as(string alias) { - // return setAlias(alias); - // } - public Reducer As(string alias) { Alias = alias; return this; } - public Reducer SetAliasAsField() - { - if (string.IsNullOrEmpty(_field)) throw new InvalidOperationException("Cannot set to field name since no field exists"); - return As(_field); - } - internal void SerializeRedisArgs(List args) + internal void SerializeRedisArgs(List args) { int count = GetOwnArgsCount(); args.Add(count); @@ -73,13 +43,5 @@ internal void SerializeRedisArgs(List args) if (count != (after - before)) throw new InvalidOperationException($"Reducer '{ToString()}' incorrectly reported the arg-count as {count}, but added {after - before}"); } - - public List GetArgs() - { - List args = new List(); - SerializeRedisArgs(args); - return args; } -} - } \ No newline at end of file diff --git a/src/NRedisStack/Search/Reducers.cs b/src/NRedisStack/Search/Reducers.cs index 6e8254dd..c5e23f06 100644 --- a/src/NRedisStack/Search/Reducers.cs +++ b/src/NRedisStack/Search/Reducers.cs @@ -54,7 +54,7 @@ protected override void AddOwnArgs(List args) } public override string Name => "QUANTILE"; } - public static Reducer FirstValue(string field, SortedField sortBy) => new FirstValueReducer(field, sortBy); + public static Reducer FirstValue(string field, SortedField? sortBy) => new FirstValueReducer(field, sortBy); private sealed class FirstValueReducer : Reducer { private readonly SortedField? _sortBy; @@ -78,7 +78,7 @@ protected override void AddOwnArgs(List args) } } } - public static Reducer FirstValue(string field) => new FirstValueReducer(field, null); + public static Reducer FirstValue(string field) => FirstValue(field, null); public static Reducer ToList(string field) => new SingleFieldReducer("TOLIST", field); diff --git a/src/NRedisStack/Search/SearchCommands.cs b/src/NRedisStack/Search/SearchCommands.cs index c81b29df..26b2a525 100644 --- a/src/NRedisStack/Search/SearchCommands.cs +++ b/src/NRedisStack/Search/SearchCommands.cs @@ -254,14 +254,14 @@ public async Task InfoAsync(RedisValue index) => public SearchResult Search(string indexName, Query q) { var resp = _db.Execute(SearchCommandBuilder.Search(indexName, q)).ToArray(); - return new SearchResult(resp, !q.NoContent, q.WithScores, q.WithPayloads, q.ExplainScore); + return new SearchResult(resp, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/); } /// public async Task SearchAsync(string indexName, Query q) { var resp = (await _db.ExecuteAsync(SearchCommandBuilder.Search(indexName, q))).ToArray(); - return new SearchResult(resp, !q.NoContent, q.WithScores, q.WithPayloads, q.ExplainScore); + return new SearchResult(resp, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/); } /// diff --git a/src/NRedisStack/Search/SearchResult.cs b/src/NRedisStack/Search/SearchResult.cs index a8ea49d3..4f1476d3 100644 --- a/src/NRedisStack/Search/SearchResult.cs +++ b/src/NRedisStack/Search/SearchResult.cs @@ -14,7 +14,7 @@ public class SearchResult public long TotalResults { get; } public List Documents { get; } - internal SearchResult(RedisResult[] resp, bool hasContent, bool hasScores, bool hasPayloads, bool shouldExplainScore) + internal SearchResult(RedisResult[] resp, bool hasContent, bool hasScores, bool hasPayloads/*, bool shouldExplainScore*/) { // Calculate the step distance to walk over the results. // The order of results is id, score (if withScore), payLoad (if hasPayloads), fields @@ -53,17 +53,17 @@ internal SearchResult(RedisResult[] resp, bool hasContent, bool hasScores, bool string[] scoreExplained = null; if (hasScores) { - if (shouldExplainScore) - { - var scoreResult = (RedisResult[])resp[i + scoreOffset]; - score = (double) scoreResult[0]; - var redisResultsScoreExplained = (RedisResult[]) scoreResult[1]; - scoreExplained = FlatRedisResultArray(redisResultsScoreExplained).ToArray(); - } - else - { + // if (shouldExplainScore) + // { + // var scoreResult = (RedisResult[])resp[i + scoreOffset]; + // score = (double) scoreResult[0]; + // var redisResultsScoreExplained = (RedisResult[]) scoreResult[1]; + // scoreExplained = FlatRedisResultArray(redisResultsScoreExplained).ToArray(); + // } + //else + //{ score = (double)resp[i + scoreOffset]; - } + //} } if (hasPayloads) { @@ -78,21 +78,5 @@ internal SearchResult(RedisResult[] resp, bool hasContent, bool hasScores, bool docs.Add(Document.Load(id, score, payload, fields, scoreExplained)); } } - - static IEnumerable FlatRedisResultArray(RedisResult[] collection) - { - foreach (var o in collection) - { - if (o.Type == ResultType.MultiBulk) - { - foreach (string t in FlatRedisResultArray((RedisResult[])o)) - yield return t; - } - else - { - yield return o.ToString(); - } - } - } } } \ No newline at end of file diff --git a/src/NRedisStack/Search/SortedField.cs b/src/NRedisStack/Search/SortedField.cs index 6aa3022a..dc9c1e5c 100644 --- a/src/NRedisStack/Search/SortedField.cs +++ b/src/NRedisStack/Search/SortedField.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace NRedisStack.Search.Aggregation +namespace NRedisStack.Search.Aggregation { public class SortedField { @@ -13,7 +11,7 @@ public enum SortOrder public string FieldName { get; } public SortOrder Order { get; } - public SortedField(String fieldName, SortOrder order) + public SortedField(String fieldName, SortOrder order = SortOrder.ASC) { this.FieldName = fieldName; this.Order = order; diff --git a/src/NRedisStack/TimeSeries/Extensions/AggregationExtensions.cs b/src/NRedisStack/TimeSeries/Extensions/AggregationExtensions.cs index e718514d..2512ffb9 100644 --- a/src/NRedisStack/TimeSeries/Extensions/AggregationExtensions.cs +++ b/src/NRedisStack/TimeSeries/Extensions/AggregationExtensions.cs @@ -1,5 +1,4 @@ -using System; -using NRedisStack.Literals.Enums; +using NRedisStack.Literals.Enums; namespace NRedisStack.Extensions { @@ -25,7 +24,7 @@ internal static class AggregationExtensions public static TsAggregation AsAggregation(string aggregation) => aggregation switch { - "avg" => TsAggregation.Avg, + /*"avg" => TsAggregation.Avg, "sum" => TsAggregation.Sum, "min" => TsAggregation.Min, "max" => TsAggregation.Max, @@ -37,7 +36,7 @@ internal static class AggregationExtensions "std.s" => TsAggregation.StdS, "var.p" => TsAggregation.VarP, "var.s" => TsAggregation.VarS, - "twa" => TsAggregation.Twa, + "twa" => TsAggregation.Twa,*/ "AVG" => TsAggregation.Avg, "SUM" => TsAggregation.Sum, "MIN" => TsAggregation.Min, diff --git a/src/NRedisStack/TimeSeries/Extensions/BucketTimestampsExtensions.cs b/src/NRedisStack/TimeSeries/Extensions/BucketTimestampsExtensions.cs index cb945039..b102e53d 100644 --- a/src/NRedisStack/TimeSeries/Extensions/BucketTimestampsExtensions.cs +++ b/src/NRedisStack/TimeSeries/Extensions/BucketTimestampsExtensions.cs @@ -12,13 +12,5 @@ internal static class TsBucketTimestampsExtensions TsBucketTimestamps.high => "+", _ => throw new ArgumentOutOfRangeException(nameof(bt), "Invalid TsBucketTimestamps type"), }; - - public static TsBucketTimestamps Asbt(string bt) => bt switch - { - "-" => TsBucketTimestamps.low, - "~" => TsBucketTimestamps.mid, - "+" => TsBucketTimestamps.high, - _ => throw new ArgumentOutOfRangeException(nameof(bt), $"Invalid TsBucketTimestamps type '{bt}'"), - }; } } \ No newline at end of file diff --git a/src/NRedisStack/TimeSeries/Extensions/ReduceExtensions.cs b/src/NRedisStack/TimeSeries/Extensions/ReduceExtensions.cs index 005920c0..2485e2a6 100644 --- a/src/NRedisStack/TimeSeries/Extensions/ReduceExtensions.cs +++ b/src/NRedisStack/TimeSeries/Extensions/ReduceExtensions.cs @@ -12,13 +12,5 @@ internal static class ReduceExtensions TsReduce.Max => "MAX", _ => throw new ArgumentOutOfRangeException(nameof(reduce), "Invalid Reduce type"), }; - - public static TsReduce AsReduce(string reduce) => reduce switch - { - "SUM" => TsReduce.Sum, - "MIN" => TsReduce.Min, - "MAX" => TsReduce.Max, - _ => throw new ArgumentOutOfRangeException(nameof(reduce), $"Invalid Reduce type '{reduce}'"), - }; } } diff --git a/src/NRedisStack/TopK/TopKCommandBuilder.cs b/src/NRedisStack/TopK/TopKCommandBuilder.cs index 8360899c..7e299054 100644 --- a/src/NRedisStack/TopK/TopKCommandBuilder.cs +++ b/src/NRedisStack/TopK/TopKCommandBuilder.cs @@ -49,11 +49,6 @@ public static SerializedCommand List(RedisKey key, bool withcount = false) : new SerializedCommand(TOPK.LIST, key); } - public static SerializedCommand Query(RedisKey key, RedisValue item) - { - return new SerializedCommand(TOPK.QUERY, key, item); - } - public static SerializedCommand Query(RedisKey key, params RedisValue[] items) { if (items.Length < 1) diff --git a/tests/NRedisStack.Tests/Bloom/BloomTests.cs b/tests/NRedisStack.Tests/Bloom/BloomTests.cs index eb878e1b..a57a5314 100644 --- a/tests/NRedisStack.Tests/Bloom/BloomTests.cs +++ b/tests/NRedisStack.Tests/Bloom/BloomTests.cs @@ -385,10 +385,21 @@ public void TestModulePrefixs1() var conn = ConnectionMultiplexer.Connect("localhost"); IDatabase db = conn.GetDatabase(); - var bf = db.FT(); + var bf = db.FT(); // ... conn.Dispose(); } + } + [Fact] + public void TestInsertArgsError() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var bf = db.BF(); + + RedisValue[] items = new RedisValue[] { "item1", "item2", "item3" }; + // supose to throw exception: + Assert.Throws(() => bf.Insert("key3", items, 100, 0.01, 2, nocreate: true, nonscaling: true)); } } \ No newline at end of file diff --git a/tests/NRedisStack.Tests/CuckooFilter/CuckooTests.cs b/tests/NRedisStack.Tests/CuckooFilter/CuckooTests.cs index e32be109..f0bf13d0 100644 --- a/tests/NRedisStack.Tests/CuckooFilter/CuckooTests.cs +++ b/tests/NRedisStack.Tests/CuckooFilter/CuckooTests.cs @@ -22,7 +22,7 @@ public void TestReserveBasic() IDatabase db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); var cf = db.CF(); - Assert.True(cf.Reserve(key, 100L)); + Assert.True(cf.Reserve(key, 100L, maxIterations: 20, expansion: 1)); Assert.Throws(() => cf.Reserve(key, 100L)); Assert.True((cf.Add(key, "item1"))); @@ -36,7 +36,7 @@ public async Task TestReserveBasicAsync() IDatabase db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); var cf = db.CF(); - Assert.True(await cf.ReserveAsync(key, 100L)); + Assert.True(await cf.ReserveAsync(key, 100L, maxIterations: 20, expansion: 1)); Assert.ThrowsAsync(async () => await cf.ReserveAsync(key, 100L)); Assert.True(await (cf.AddAsync(key, "item1"))); @@ -271,7 +271,9 @@ public void TestInsertNX() RedisValue[] items = new RedisValue[] { "item1", "item2", "item3" }; - var result = cf.InsertNX(key, items); + Assert.Throws(() => cf.InsertNX(key, items, 1024, true)); + var result = cf.InsertNX(key, items, 1024); + cf.InsertNX(key, items, 10245, true); var trues = new bool[] { true, true, true }; Assert.Equal(result, trues); @@ -283,6 +285,9 @@ public void TestInsertNX() result = cf.InsertNX(key, items); Assert.Equal(result, new bool[] { false, false, false }); + + // test empty items: + Assert.Throws(() => cf.InsertNX(key, new RedisValue[]{})); } [Fact] @@ -294,7 +299,9 @@ public async Task TestInsertNXAsync() RedisValue[] items = new RedisValue[] { "item1", "item2", "item3" }; - var result = await cf.InsertNXAsync(key, items); + Assert.ThrowsAsync(async () => await cf.InsertNXAsync(key, items, 1024, true)); + var result = await cf.InsertNXAsync(key, items, 1024); + await cf.InsertNXAsync(key, items, 10245, true); var trues = new bool[] { true, true, true }; Assert.Equal(result, trues); @@ -306,6 +313,9 @@ public async Task TestInsertNXAsync() result = await cf.InsertNXAsync(key, items); Assert.Equal(result, new bool[] { false, false, false }); + + // test empty items: + Assert.ThrowsAsync(async () => await cf.InsertNXAsync(key, new RedisValue[]{})); } [Fact] diff --git a/tests/NRedisStack.Tests/Graph/GraphTests.cs b/tests/NRedisStack.Tests/Graph/GraphTests.cs index 211d6702..b1ff64c1 100644 --- a/tests/NRedisStack.Tests/Graph/GraphTests.cs +++ b/tests/NRedisStack.Tests/Graph/GraphTests.cs @@ -782,7 +782,20 @@ private void AssertTestGeoPoint(IGraphCommands graph) Node node = record.Current.GetValue(0); var property = node.PropertyMap["location"]; - Assert.Equal((object)(new Point(30.27822306, -97.75134723)), property); + var point = new Point(30.27822306, -97.75134723); + Assert.Equal((object)(point), property); + } + + [Fact] + public void TestPoint() + { + var point = new Point(30.27822306, -97.75134723); + + var pointString = point.ToString(); + Assert.Equal("Point{latitude=30.27822306, longitude=-97.75134723}", pointString); + var pointHash = point.GetHashCode(); + Assert.Equal(847819990, pointHash); + Assert.Throws(() => new Point(new List { 1, 2, 3 })); } [Fact] @@ -1934,26 +1947,56 @@ public async Task TestModulePrefixs1Async() } [Fact] - public void TestEquals() + public void TestEqualsAndToString() { IDatabase db = redisFixture.Redis.GetDatabase(); db.Execute("FLUSHALL"); + var graph = db.GRAPH(); + ResultSet resultSet1 = graph.Query("db", "RETURN 10^100000"); + ResultSet resultSet2 = graph.Query("db", "RETURN 10^1000"); + var iterator1 = resultSet1.GetEnumerator(); + Assert.True(iterator1.MoveNext()); + var record1 = iterator1.Current; + var iterator2 = resultSet2.GetEnumerator(); + Assert.True(iterator2.MoveNext()); + var record2 = iterator2.Current; + + Assert.True(resultSet1.Header.Equals(resultSet1.Header)); + Assert.False(resultSet1.Header.Equals(resultSet2.Header)); + Assert.False(resultSet1.Header.Equals(new object())); + Assert.False(resultSet1.Header.Equals(null)); + + Assert.True(record1.Equals(record1)); + Assert.False(record1.Equals(record2)); + Assert.False(record1.Equals(new object())); + Assert.False(record1.Equals(null)); + var edge1 = new Edge(); var edge1Copy = new Edge(); var edge2 = new Edge(); var node1 = new Node(); var node1Copy = new Node(); var node2 = new Node(); + edge1.Id = 1; edge1Copy.Id = 1; edge2.Id = 2; node1.Id = 1; node1Copy.Id = 1; node2.Id = 2; + Assert.False(edge1.Equals(edge2)); Assert.False(node1.Equals(node2)); Assert.True(edge1.Equals(edge1Copy)); + Assert.True(edge1.Equals(edge1)); Assert.True(node1.Equals(node1Copy)); + Assert.True(node1.Equals(node1)); + Assert.False(node1.Equals(edge1)); + Assert.False(edge1.Equals(node1)); + Assert.False(node1.Equals(null)); + Assert.False(edge1.Equals(null)); + + var path = new NRedisStack.Graph.DataTypes.Path(new List() { node1, node2 }, new List() { edge1, edge2 }); @@ -1962,7 +2005,33 @@ public void TestEquals() var path2 = new NRedisStack.Graph.DataTypes.Path(new List() { node1, node2 }, new List() { edge1 }); Assert.True(path.Equals(pathCopy)); + Assert.True(path.Equals(path)); Assert.False(path.Equals(path2)); + Assert.False(path.Equals(node1)); + + Assert.True(record1.ToString() == "Record{values=Infinity}" || record1.ToString() == "Record{values=∞}"); + Assert.NotEqual(record2.GetHashCode(), record1.GetHashCode()); + + var node1String = node1.ToString(); + var edge1String = edge1.ToString(); + var pathString = path.ToString(); + var expectedNode1String = "Node{labels=[], id=1, propertyMap={}}"; + var expectedEdge1String = "Edge{relationshipType='', source=0, destination=0, id=1, propertyMap={}}"; + var expectedPathString = "Path{nodes=System.Collections.ObjectModel.ReadOnlyCollection`1[NRedisStack.Graph.DataTypes.Node], edges=System.Collections.ObjectModel.ReadOnlyCollection`1[NRedisStack.Graph.DataTypes.Edge]}"; + Assert.Equal(expectedNode1String, node1String); + Assert.Equal(expectedEdge1String, edge1String); + Assert.Equal(expectedPathString, pathString); + } + + [Fact] + public void TestPrepareQuery() + { + var graph = redisFixture.Redis.GetDatabase().GRAPH(); + var res1 = graph.Query("graph", "RETURN 1", new Dictionary { { "a", (char)'c' } }); + var res2 = graph.Query("graph", "RETURN 1", new Dictionary { { "a", null } }); + var res3 = graph.Query("graph", "RETURN 1", new Dictionary { { "a", new string[]{"foo", "bar"} } }); + var res4 = graph.Query("graph", "RETURN 1", new Dictionary { { "a", new List{"foo2", "bar2"} } }); + // TODO: complete this test } #endregion diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 5be01a74..fcdf95f8 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -24,23 +24,22 @@ public void Dispose() private void AddDocument(IDatabase db, Document doc) { - string key = doc.Id; - var properties = doc.GetProperties(); - // HashEntry[] hash = new HashEntry[properties.Count()]; - // for(int i = 0; i < properties.Count(); i++) - // { - // var property = properties.ElementAt(i); - // hash[i] = new HashEntry(property.Key, property.Value); - // } - // db.HashSet(key, hash); - var nameValue = new List() { key }; - foreach (var item in properties) - { - nameValue.Add(item.Key); - nameValue.Add(item.Value); - } - db.Execute("HSET", nameValue); - + string key = doc.Id; + var properties = doc.GetProperties(); + // HashEntry[] hash = new HashEntry[properties.Count()]; + // for(int i = 0; i < properties.Count(); i++) + // { + // var property = properties.ElementAt(i); + // hash[i] = new HashEntry(property.Key, property.Value); + // } + // db.HashSet(key, hash); + var nameValue = new List() { key }; + foreach (var item in properties) + { + nameValue.Add(item.Key); + nameValue.Add(item.Value); + } + db.Execute("HSET", nameValue); } private void AddDocument(IDatabase db, string key, Dictionary objDictionary) @@ -118,7 +117,7 @@ public void TestAggregationRequestTimeout() sc.AddTextField("name", 1.0, sortable: true); sc.AddNumericField("count", sortable: true); ft.Create(index, FTCreateParams.CreateParams(), sc); - AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); + AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10).SetScore(1.0)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); @@ -140,7 +139,7 @@ public async Task TestAggregationRequestTimeoutAsync() sc.AddTextField("name", 1.0, sortable: true); sc.AddNumericField("count", sortable: true); ft.Create(index, FTCreateParams.CreateParams(), sc); - AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); + AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10).SetScore(1.0)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); @@ -666,6 +665,8 @@ public void AlterAdd() var info = ft.Info(index); Assert.Equal(index, info.IndexName); + Assert.Equal(0, info.IndexOption.Count); + // Assert.Equal(,info.IndexDefinition); Assert.Equal("title", (info.Attributes[0]["identifier"]).ToString()); Assert.Equal("TAG", (info.Attributes[1]["type"]).ToString()); Assert.Equal("name", (info.Attributes[2]["attribute"]).ToString()); @@ -965,6 +966,114 @@ public async Task TestCursorAsync() catch (RedisException) { } } + [Fact] + public void TestAggregationGroupBy() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + // Creating the index definition and schema + ft.Create("idx", new FTCreateParams(), new Schema().AddNumericField("random_num") + .AddTextField("title") + .AddTextField("body") + .AddTextField("parent")); + + // Indexing a document + AddDocument(db, "search", new Dictionary(){ + { "title", "RediSearch" }, + { "body", "Redisearch impements a search engine on top of redis" }, + { "parent", "redis" }, + { "random_num", 10 }}); + + AddDocument(db, "ai", new Dictionary + { + { "title", "RedisAI" }, + { "body", "RedisAI executes Deep Learning/Machine Learning models and managing their data." }, + { "parent", "redis" }, + { "random_num", 3 }}); + + AddDocument(db, "json", new Dictionary + { + { "title", "RedisJson" }, + { "body", "RedisJSON implements ECMA-404 The JSON Data Interchange Standard as a native data type." }, + { "parent", "redis" }, + { "random_num", 8 }}); + + var req = new AggregationRequest("redis").GroupBy("@parent", Reducers.Count()); + var res = ft.Aggregate("idx", req).GetRow(0); + Assert.True(res.ContainsKey("parent")); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res["__generated_aliascount"], "3"); + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.CountDistinct("@title")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliascount_distincttitle"), 3); + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.CountDistinctish("@title")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliascount_distinctishtitle"), 3); + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.Sum("@random_num")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliassumrandom_num"), 21); // 10+8+3 + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.Min("@random_num")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliasminrandom_num"), 3); // min(10,8,3) + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.Max("@random_num")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliasmaxrandom_num"), 10); // max(10,8,3) + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.Avg("@random_num")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliasavgrandom_num"), 7); // (10+3+8)/3 + + req = new AggregationRequest("redis").GroupBy("@parent", Reducers.StdDev("@random_num")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetDouble("__generated_aliasstddevrandom_num"), 3.60555127546); + + req = new AggregationRequest("redis").GroupBy( + "@parent", Reducers.Quantile("@random_num", 0.5)); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res.GetLong("__generated_aliasquantilerandom_num,0.5"), 8); // median of 3,8,10 + + req = new AggregationRequest("redis").GroupBy( + "@parent", Reducers.ToList("@title")); + var rawRes = ft.Aggregate("idx", req); + res = rawRes.GetRow(0); + Assert.Equal(res["parent"], "redis"); + // TODO: complete this assert after handling multi bulk reply + //Assert.Equal((RedisValue[])res["__generated_aliastolisttitle"], { "RediSearch", "RedisAI", "RedisJson"}); + + req = new AggregationRequest("redis").GroupBy( + "@parent", Reducers.FirstValue("@title").As("first")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + Assert.Equal(res["first"], "RediSearch"); + + req = new AggregationRequest("redis").GroupBy( + "@parent", Reducers.RandomSample("@title", 2).As("random")); + res = ft.Aggregate("idx", req).GetRow(0); + Assert.Equal(res["parent"], "redis"); + // TODO: complete this assert after handling multi bulk reply + // Assert.Equal(res[2], "random"); + // Assert.Equal(len(res[3]), 2); + // Assert.Equal(res[3][0] in ["RediSearch", "RedisAI", "RedisJson"]); + // req = new AggregationRequest("redis").GroupBy("@parent", redu + + } + + [Fact] public void TestDictionary() { @@ -1346,7 +1455,7 @@ public void TestFTCreateParamsCommandBuilder() .AddTextField("title", 1.0) .AddTagField("category", separator: ";"); - var ftCreateParams = FTCreateParams.CreateParams().On(IndexDataType.Json) + var ftCreateParams = FTCreateParams.CreateParams().On(IndexDataType.JSON) .AddPrefix("doc:") .Filter("@category:{red}") .Language("English") @@ -1357,14 +1466,14 @@ public void TestFTCreateParamsCommandBuilder() .MaxTextFields() .NoOffsets() .Temporary(10) - .NoHL() + .NoHighlights() .NoFields() .NoFreqs() .Stopwords(new[] { "foo", "bar" }) .SkipInitialScan(); var builedCommand = SearchCommandBuilder.Create(index, ftCreateParams, sc); - var expectedArgs = new object[] { "TEST_INDEX", "PREFIX", 1, + var expectedArgs = new object[] { "TEST_INDEX", "ON", "JSON", "PREFIX", 1, "doc:", "FILTER", "@category:{red}", "LANGUAGE", "English", "LANGUAGE_FIELD", "play", "SCORE", 1, "SCORE_FIELD", "chapter", "PAYLOAD_FIELD", "txt", @@ -1432,7 +1541,7 @@ public void TestFilters() // Test numerical filter var q1 = new Query("foo").AddFilter(new Query.NumericFilter("num", 0, 2)); - var q2 = new Query("foo").AddFilter(new Query.NumericFilter("num",2,true, double.MaxValue, false)); + var q2 = new Query("foo").AddFilter(new Query.NumericFilter("num", 2, true, double.MaxValue, false)); q1.NoContent = q2.NoContent = true; var res1 = ft.Search("idx", q1); var res2 = ft.Search("idx", q2); @@ -1449,9 +1558,9 @@ public void TestFilters() res1 = ft.Search("idx", q1); res2 = ft.Search("idx", q2); - Assert.Equal( 1 , res1.TotalResults); - Assert.Equal( 2 , res2.TotalResults); - Assert.Equal( "doc1" , res1.Documents[0].Id); + Assert.Equal(1, res1.TotalResults); + Assert.Equal(2, res2.TotalResults); + Assert.Equal("doc1", res1.Documents[0].Id); } [Fact] @@ -1482,7 +1591,7 @@ public async Task TestFiltersAsync() // Test numerical filter var q1 = new Query("foo").AddFilter(new Query.NumericFilter("num", 0, 2)); - var q2 = new Query("foo").AddFilter(new Query.NumericFilter("num",2,true, double.MaxValue, false)); + var q2 = new Query("foo").AddFilter(new Query.NumericFilter("num", 2, true, double.MaxValue, false)); q1.NoContent = q2.NoContent = true; var res1 = await ft.SearchAsync("idx", q1); var res2 = await ft.SearchAsync("idx", q2); @@ -1499,9 +1608,156 @@ public async Task TestFiltersAsync() res1 = await ft.SearchAsync("idx", q1); res2 = await ft.SearchAsync("idx", q2); - Assert.Equal( 1 , res1.TotalResults); - Assert.Equal( 2 , res2.TotalResults); - Assert.Equal( "doc1" , res1.Documents[0].Id); + Assert.Equal(1, res1.TotalResults); + Assert.Equal(2, res2.TotalResults); + Assert.Equal("doc1", res1.Documents[0].Id); + } + + [Fact] + public void TestQueryCommandBuilder() + { + var testQuery = new Query("foo").HighlightFields(new Query.HighlightTags("", ""), "txt") + .SetVerbatim() + .SetNoStopwords() + .SetWithScores() + .SetPayload("txt") + .SetLanguage("English") + .SetScorer("TFIDF") + //.SetExplainScore() + .SetWithPayloads() + .SetSortBy("txt", true) + .Limit(0, 11) + .SummarizeFields(20, 3, ";", "txt") + .LimitKeys("key1", "key2") + .LimitFields("txt") + .ReturnFields("txt") + .AddParam("name", "value") + .Dialect(1) + .Slop(0) + .Timeout(1000) + .SetInOrder() + .SetExpander("myexpander"); + var buildCommand = SearchCommandBuilder.Search("idx", testQuery); + var expectedArgs = new List {"idx", + "foo", + "VERBATIM", + "NOSTOPWORDS", + "WITHSCORES", + "WITHPAYLOADS", + "LANGUAGE", + "English", + "SCORER", + "TFIDF", + "INFIELDS", + "1", + "txt", + "SORTBY", + "txt", + "ASC", + "PAYLOAD", + "txt", + "LIMIT", + "0", + "11", + "HIGHLIGHT", + "FIELDS", + "1", + "txt", + "TAGS", + "", + "", + "SUMMARIZE", + "FIELDS", + "1", + "txt", + "FRAGS", + "3", + "LEN", + "20", + "SEPARATOR", + ";", + "INKEYS", + "2", + "key1", + "key2", + "RETURN", + "1", + "txt", + "PARAMS", + "2", + "name", + "value", + "DIALECT", + "1", + "SLOP", + "0", + "TIMEOUT", + "1000", + "INORDER", + "EXPANDER", + "myexpander"}; + + for (int i = 0; i < buildCommand.Args.Count(); i++) + { + Assert.Equal(expectedArgs[i].ToString(), buildCommand.Args[i].ToString()); + } + Assert.Equal("FT.SEARCH", buildCommand.Command); + // test that the command not throw an exception: + var db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + ft.Create("idx", new FTCreateParams(), new Schema().AddTextField("txt")); + var res = ft.Search("idx", testQuery); + Assert.Equal(0, res.Documents.Count()); + } + + [Fact] + public void TestQueryCommandBuilderReturnField() + { + var testQuery = new Query("foo").HighlightFields("txt") + .ReturnFields(new FieldName("txt")) + .SetNoContent(); + + + var buildCommand = SearchCommandBuilder.Search("idx", testQuery); + var expectedArgs = new List {"idx", + "foo", + "NOCONTENT", + "HIGHLIGHT", + "FIELDS", + "1", + "txt", + "RETURN", + "1", + "txt"}; + + for (int i = 0; i < buildCommand.Args.Count(); i++) + { + Assert.Equal(expectedArgs[i].ToString(), buildCommand.Args[i].ToString()); + } + Assert.Equal("FT.SEARCH", buildCommand.Command); + + // test that the command not throw an exception: + var db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + ft.Create("idx", new FTCreateParams(), new Schema().AddTextField("txt")); + var res = ft.Search("idx", testQuery); + Assert.Equal(0, res.Documents.Count()); + } + + [Fact] + public void TestQueryCommandBuilderScore() + { + // TODO: write better test for scores and payloads + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + db.Execute("JSON.SET", "doc:1", "$", "[{\"arr\": [1, 2, 3]}, {\"val\": \"hello\"}, {\"val\": \"world\"}]"); + db.Execute("FT.CREATE", "idx", "ON", "JSON", "PREFIX", "1", "doc:", "SCHEMA", "$..arr", "AS", "arr", "NUMERIC", "$..val", "AS", "val", "TEXT"); + var res = ft.Search("idx", new Query("*").ReturnFields("arr", "val").SetWithScores().SetPayload("arr")); + Assert.Equal(1, res.TotalResults); } [Fact] @@ -1512,11 +1768,11 @@ public void TestFieldsCommandBuilder() var ft = db.FT(); // Create the index with the same fields as in the original test var sc = new Schema() - .AddTextField("txt", 1.0, true, true, true, "dm:en", true, true) - .AddNumericField("num", true, true) - .AddGeoField("loc", true, true) - .AddTagField("tag",true,true, true, ";", true, true) - .AddVectorField("vec", VectorField.VectorAlgo.FLAT, null); + .AddTextField(FieldName.Of("txt"), 1.0, true, true, true, "dm:en", true, true) + .AddNumericField(FieldName.Of("num"), true, true) + .AddGeoField(FieldName.Of("loc"), true, true) + .AddTagField(FieldName.Of("tag"), true, true, true, ";", true, true) + .AddVectorField("vec", VectorField.VectorAlgo.FLAT, new Dictionary { { "dim", 10 } }); var buildCommand = SearchCommandBuilder.Create("idx", new FTCreateParams(), sc); var expectedArgs = new List { "idx", @@ -1549,35 +1805,96 @@ public void TestFieldsCommandBuilder() "CASESENSITIVE", "vec", "VECTOR", - "FLAT" + "FLAT", + "1", + "dim", + "10" }; Assert.Equal("FT.CREATE", buildCommand.Command); - for(int i = 0; i < expectedArgs.Count; i++) + for (int i = 0; i < expectedArgs.Count; i++) { - Assert.Equal(expectedArgs[i], buildCommand.Args[i]); + Assert.Equal(expectedArgs[i], buildCommand.Args[i].ToString()); } } - [Fact] - public void TestModulePrefixs1() + [Fact] + public void TestLimit() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + ft.Create("idx", new FTCreateParams(), new Schema().AddTextField("t1").AddTextField("t2")); + Document doc1 = new Document("doc1", new Dictionary {{"t1", "a"}, {"t2", "b"}}); + Document doc2 = new Document("doc2", new Dictionary {{"t1", "b"}, {"t2", "a"}}); + AddDocument(db, doc1); + AddDocument(db, doc2); + + var req = new AggregationRequest("*").SortBy("@t1").Limit(1, 1); + var res = ft.Aggregate("idx", req); + + Assert.Equal( res.GetResults().Count, 1); + Assert.Equal( res.GetResults()[0]["t1"].ToString(), "b"); + } + + [Fact] + public async Task TestLimitAsync() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + + ft.Create("idx", new FTCreateParams(), new Schema().AddTextField("t1").AddTextField("t2")); + Document doc1 = new Document("doc1", new Dictionary {{"t1", "a"}, {"t2", "b"}}); + Document doc2 = new Document("doc2", new Dictionary {{"t1", "b"}, {"t2", "a"}}); + AddDocument(db, doc1); + AddDocument(db, doc2); + + var req = new AggregationRequest("*").SortBy("@t1").Limit(1, 1); + var res = await ft.AggregateAsync("idx", req); + + Assert.Equal( res.GetResults().Count, 1); + Assert.Equal( res.GetResults()[0]["t1"].ToString(), "b"); + } + + [Fact] + public void Test_List() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + Assert.Equal(ft._List(), new RedisResult[] { }); + } + + [Fact] + public async Task Test_ListAsync() + { + IDatabase db = redisFixture.Redis.GetDatabase(); + db.Execute("FLUSHALL"); + var ft = db.FT(); + Assert.Equal(await ft._ListAsync(), new RedisResult[] { }); + } + + [Fact] + public void TestModulePrefixs1() + { { - { - var conn = ConnectionMultiplexer.Connect("localhost"); - IDatabase db = conn.GetDatabase(); + var conn = ConnectionMultiplexer.Connect("localhost"); + IDatabase db = conn.GetDatabase(); - var ft = db.FT(); - // ... - conn.Dispose(); - } + var ft = db.FT(); + // ... + conn.Dispose(); + } - { - var conn = ConnectionMultiplexer.Connect("localhost"); - IDatabase db = conn.GetDatabase(); + { + var conn = ConnectionMultiplexer.Connect("localhost"); + IDatabase db = conn.GetDatabase(); - var ft = db.FT(); - // ... - conn.Dispose(); - } + var ft = db.FT(); + // ... + conn.Dispose(); } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRange.cs b/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRange.cs index eb3599b3..f21370ac 100644 --- a/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRange.cs +++ b/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRange.cs @@ -200,7 +200,7 @@ public void TestBucketTimestamp() ts.Add("t1", 73, 5); ts.Add("t1", 75, 3); - var range = ts.Range("t1", 0, 100, + var rangeHigh = ts.Range("t1", 0, 100, align: 0, aggregation: TsAggregation.Max, timeBucket: 10); @@ -209,19 +209,40 @@ public void TestBucketTimestamp() expected.Add(new TimeSeriesTuple(10, 4.0)); expected.Add(new TimeSeriesTuple(50, 3.0)); expected.Add(new TimeSeriesTuple(70, 5.0)); - Assert.Equal(range, expected); + Assert.Equal(rangeHigh, expected); - range = ts.Range("t1", 0, 100, + rangeHigh = ts.Range("t1", 0, 100, align: 0, aggregation: TsAggregation.Max, timeBucket: 10, bt: TsBucketTimestamps.high); - expected.Clear(); expected.Add(new TimeSeriesTuple(20, 4.0)); expected.Add(new TimeSeriesTuple(60, 3.0)); expected.Add(new TimeSeriesTuple(80, 5.0)); - Assert.Equal(range, expected); + Assert.Equal(rangeHigh, expected); + + var rangeLow = ts.Range("t1", 0, 100, + align: 0, + aggregation: TsAggregation.Max, + timeBucket: 10, + bt: TsBucketTimestamps.low); + expected.Clear(); + expected.Add(new TimeSeriesTuple(10, 4.0)); + expected.Add(new TimeSeriesTuple(50, 3.0)); + expected.Add(new TimeSeriesTuple(70, 5.0)); + Assert.Equal(rangeLow, expected); + + var rangeMid = ts.Range("t1", 0, 100, + align: 0, + aggregation: TsAggregation.Max, + timeBucket: 10, + bt: TsBucketTimestamps.mid); + expected.Clear(); + expected.Add(new TimeSeriesTuple(15, 4.0)); + expected.Add(new TimeSeriesTuple(55, 3.0)); + expected.Add(new TimeSeriesTuple(75, 5.0)); + Assert.Equal(rangeMid, expected); } [Fact] diff --git a/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRangeAsync.cs b/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRangeAsync.cs index 5de3d01a..381aac78 100644 --- a/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRangeAsync.cs +++ b/tests/NRedisStack.Tests/TimeSeries/TestAPI/TestRangeAsync.cs @@ -200,7 +200,7 @@ public async Task TestBucketTimestampAsync() ts.Add("t1", 73, 5); ts.Add("t1", 75, 3); - var range = await ts.RangeAsync("t1", 0, 100, + var rangeHigh = await ts.RangeAsync("t1", 0, 100, align: 0, aggregation: TsAggregation.Max, timeBucket: 10); @@ -209,9 +209,9 @@ public async Task TestBucketTimestampAsync() expected.Add(new TimeSeriesTuple(10, 4.0)); expected.Add(new TimeSeriesTuple(50, 3.0)); expected.Add(new TimeSeriesTuple(70, 5.0)); - Assert.Equal(range, expected); + Assert.Equal(rangeHigh, expected); - range = await ts.RangeAsync("t1", 0, 100, + rangeHigh = await ts.RangeAsync("t1", 0, 100, align: 0, aggregation: TsAggregation.Max, timeBucket: 10, @@ -221,7 +221,30 @@ public async Task TestBucketTimestampAsync() expected.Add(new TimeSeriesTuple(20, 4.0)); expected.Add(new TimeSeriesTuple(60, 3.0)); expected.Add(new TimeSeriesTuple(80, 5.0)); - Assert.Equal(range, expected); + Assert.Equal(rangeHigh, expected); + + var rangeLow = await ts.RangeAsync("t1", 0, 100, + align: 0, + aggregation: TsAggregation.Max, + timeBucket: 10, + bt: TsBucketTimestamps.low); + expected.Clear(); + expected.Add(new TimeSeriesTuple(10, 4.0)); + expected.Add(new TimeSeriesTuple(50, 3.0)); + expected.Add(new TimeSeriesTuple(70, 5.0)); + Assert.Equal(rangeLow, expected); + + var rangeMid = await ts.RangeAsync("t1", 0, 100, + align: 0, + aggregation: TsAggregation.Max, + timeBucket: 10, + bt: TsBucketTimestamps.mid); + expected.Clear(); + expected.Add(new TimeSeriesTuple(15, 4.0)); + expected.Add(new TimeSeriesTuple(55, 3.0)); + expected.Add(new TimeSeriesTuple(75, 5.0)); + Assert.Equal(rangeMid, expected); + } [Fact] diff --git a/tests/NRedisStack.Tests/TopK/TopKTests.cs b/tests/NRedisStack.Tests/TopK/TopKTests.cs index 653e4175..478732ee 100644 --- a/tests/NRedisStack.Tests/TopK/TopKTests.cs +++ b/tests/NRedisStack.Tests/TopK/TopKTests.cs @@ -29,6 +29,7 @@ public void CreateTopKFilter() Assert.True(res[0].IsNull && res[1].IsNull); Assert.Equal(topk.Query("aaa", "bb", "gg", "cc"), new bool[] { true, false, true }); + Assert.False(topk.Query("aaa", "notExists")); Assert.Equal(topk.Count("aaa", "bb", "gg", "cc"), new long[] { 1, 0, 1 }); @@ -65,6 +66,7 @@ public async Task CreateTopKFilterAsync() Assert.True(res[0].IsNull && res[1].IsNull); Assert.Equal(await topk.QueryAsync("aaa", "bb", "gg", "cc"), new bool[] { true, false, true }); + Assert.False(await topk.QueryAsync("aaa", "notExists")); Assert.Equal(await topk.CountAsync("aaa", "bb", "gg", "cc"), new long[] { 1, 0, 1 });