Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for extended search commands #151

Merged
merged 30 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
510c931
start working on Adding Support for FT.SPELLCHECK
shacharPash Jun 18, 2023
b630973
tests
shacharPash Jun 18, 2023
f4966c8
Add tests
shacharPash Jun 19, 2023
c6415a9
Add Bounds tests
shacharPash Jun 19, 2023
43ae14d
Chagne to RedisServerException
shacharPash Jun 19, 2023
d4d87b9
fixes
shacharPash Jun 19, 2023
561cf35
add comands to interface
shacharPash Jun 19, 2023
f5df688
using SearchArgs
shacharPash Jun 19, 2023
af6f1de
Commands implement
shacharPash Jun 19, 2023
059e1fa
delete unused literals
shacharPash Jun 19, 2023
c178b60
TestAddAndGetSuggestion
shacharPash Jun 19, 2023
ee6ec25
Add SugGetWithScoresAsync
shacharPash Jun 20, 2023
fbcf756
assync tests
shacharPash Jun 20, 2023
34ddd77
Support FT.PROFILE
shacharPash Jun 25, 2023
e2d8c50
fix parser
shacharPash Jun 25, 2023
64ee31f
fix command builder
shacharPash Jun 25, 2023
258a618
test + fixes
shacharPash Jun 25, 2023
8c43229
add tests
shacharPash Jun 26, 2023
4f27a11
add TestProfileCommandBuilder
shacharPash Jun 26, 2023
36644ef
Merge branch 'master' into ftSearchCommands
shacharPash Jun 27, 2023
3fe326d
space
shacharPash Jun 27, 2023
498560e
Try make Tran&pipe instances not interface type
shacharPash Jun 29, 2023
fbe99bf
Add ft.create without FTCreateParams
shacharPash Jun 29, 2023
e38814b
tests
shacharPash Jun 29, 2023
2031536
Merge branch 'master' into ftSearchCommands
shacharPash Jul 2, 2023
ea22d3c
fix } missing
shacharPash Jul 2, 2023
20c79b3
Delete 'I' from I<module>Commands intances in the examples
shacharPash Jul 3, 2023
6c96b6e
change to Uppercase
shacharPash Jul 5, 2023
8d0cd45
fixex after vlad's review
shacharPash Jul 5, 2023
dd50d99
Merge branch 'master' into ftSearchCommands
shacharPash Jul 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Examples/AdvancedJsonExamples.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The ability to query within a JSON object unlocks further value to the underlyin
```
## Data Loading <a name="dataload"></a>
```c#
IJsonCommands json = db.JSON();
JsonCommands json = db.JSON();
json.Set("warehouse:1", "$", new {
city = "Boston",
location = "42.361145, -71.057083",
Expand Down
8 changes: 4 additions & 4 deletions Examples/AdvancedQueryOperations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Aggregation and other more complex RediSearch queries
1. [Data Load](#vss_dataload)
2. [Index Creation](#vss_index)
3. [Search](#vss_search)
4. [Hybrid query Search](#vss_hybrid_query_search)
4. [Hybrid Query Search](#vss_hybrid_query_search)
4. [Advanced Search Queries](#adv_search)
1. [Data Set](#advs_dataset)
2. [Data Load](#advs_dataload)
Expand Down Expand Up @@ -66,7 +66,7 @@ db.HashSet("vec:4", new HashEntry[]
### Index Creation <a name="vss_index">
#### Command
```c#
ISearchCommands ft = db.FT();
SearchCommands ft = db.FT();
try {ft.DropIndex("vss_idx");} catch {};
Console.WriteLine(ft.Create("vss_idx", new FTCreateParams().On(IndexDataType.HASH).Prefix("vec:"),
new Schema()
Expand Down Expand Up @@ -193,7 +193,7 @@ vec:3 is not returned because it has tag B

### Data Load <a name="advs_dataload">
```c#
IJsonCommands json = db.JSON();
JsonCommands json = db.JSON();
json.Set("warehouse:1", "$", new {
city = "Boston",
location = "-71.057083, 42.361145",
Expand Down Expand Up @@ -253,7 +253,7 @@ json.Set("warehouse:2", "$", new {
### Index Creation <a name="advs_index">
#### Command
```c#
ISearchCommands ft = db.FT();
SearchCommands ft = db.FT();
try {ft.DropIndex("wh_idx");} catch {};
Console.WriteLine(ft.Create("wh_idx", new FTCreateParams()
.On(IndexDataType.JSON)
Expand Down
2 changes: 1 addition & 1 deletion Examples/BasicJsonExamples.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Document stores are a NoSQL database type that provide flexible schemas and acce
Insert a simple KVP as a JSON object.
#### Command
```c#
IJsonCommands json = db.JSON();
JsonCommands json = db.JSON();
Console.WriteLine(json.Set("ex1:1", "$", "\"val\""));
```
#### Result
Expand Down
4 changes: 2 additions & 2 deletions Examples/BasicQueryOperations.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ using NRedisStack.Search.Literals.Enums;
```
## Data Loading <a name="loading"></a>
```c#
IJsonCommands json = db.JSON();
JsonCommands json = db.JSON();
json.Set("product:15970", "$", new {
id = 15970,
gender = "Men",
Expand Down Expand Up @@ -100,7 +100,7 @@ json.Set("product:46885", "$", new {

#### Command
```c#
ISearchCommands ft = db.FT();
SearchCommands ft = db.FT();
try {ft.DropIndex("idx1");} catch {};
ft.Create("idx1", new FTCreateParams().On(IndexDataType.JSON)
.Prefix("product:"),
Expand Down
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ IDatabase db = redis.GetDatabase();
```
Now you can create a variable from any type of module in the following way:
```csharp
IBloomCommands bf = db.BF();
ICuckooCommands cf = db.CF();
ICmsCommands cms = db.CMS();
IGraphCommands graph = db.GRAPH();
ITopKCommands topk = db.TOPK();
ITdigestCommands tdigest = db.TDIGEST();
ISearchCommands ft = db.FT();
IJsonCommands json = db.JSON();
ITimeSeriesCommands ts = db.TS();
BloomCommands bf = db.BF();
CuckooCommands cf = db.CF();
CmsCommands cms = db.CMS();
GraphCommands graph = db.GRAPH();
TopKCommands topk = db.TOPK();
TdigestCommands tdigest = db.TDIGEST();
SearchCommands ft = db.FT();
JsonCommands json = db.JSON();
TimeSeriesCommands ts = db.TS();
```
Then, that variable will allow you to call all the commands of that module.

Expand All @@ -82,7 +82,7 @@ To store a json object in Redis:
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
IDatabase db = redis.GetDatabase();

IJsonCommands json = db.JSON();
JsonCommands json = db.JSON();
var key = "myKey";
json.Set(key, "$", new { Age = 35, Name = "Alice" });
```
Expand All @@ -99,8 +99,8 @@ using NRedisStack.Search.Literals.Enums;
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
IDatabase db = redis.GetDatabase();

ISearchCommands ft = db.FT();
IJsonCommands json = db.JSON();
SearchCommands ft = db.FT();
JsonCommands json = db.JSON();
```

Create an index with fields and weights:
Expand Down
18 changes: 9 additions & 9 deletions src/NRedisStack/Pipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public Pipeline(IDatabase db)
public void Execute() => _batch.Execute();


public IBloomCommandsAsync Bf => new BloomCommandsAsync(_batch);
public ICmsCommandsAsync Cms => new CmsCommandsAsync(_batch);
public ICuckooCommandsAsync Cf => new CuckooCommandsAsync(_batch);
public IGraphCommandsAsync Graph => new GraphCommandsAsync(_batch);
public IJsonCommandsAsync Json => new JsonCommandsAsync(_batch);
public ISearchCommandsAsync Ft => new SearchCommandsAsync(_batch);
public ITdigestCommandsAsync Tdigest => new TdigestCommandsAsync(_batch);
public ITimeSeriesCommandsAsync Ts => new TimeSeriesCommandsAsync(_batch);
public ITopKCommandsAsync TopK => new TopKCommandsAsync(_batch);
public BloomCommandsAsync Bf => new BloomCommandsAsync(_batch);
public CmsCommandsAsync Cms => new CmsCommandsAsync(_batch);
public CuckooCommandsAsync Cf => new CuckooCommandsAsync(_batch);
public GraphCommandsAsync Graph => new GraphCommandsAsync(_batch);
public JsonCommandsAsync Json => new JsonCommandsAsync(_batch);
public SearchCommandsAsync Ft => new SearchCommandsAsync(_batch);
public TdigestCommandsAsync Tdigest => new TdigestCommandsAsync(_batch);
public TimeSeriesCommandsAsync Ts => new TimeSeriesCommandsAsync(_batch);
public TopKCommandsAsync TopK => new TopKCommandsAsync(_batch);

public IDatabaseAsync Db => _batch;
}
89 changes: 89 additions & 0 deletions src/NRedisStack/ResponseParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using NRedisStack.CountMinSketch.DataTypes;
using NRedisStack.TopK.DataTypes;
using NRedisStack.Tdigest.DataTypes;
using NRedisStack.Search;
using NRedisStack.Search.Aggregation;

namespace NRedisStack
{
Expand Down Expand Up @@ -614,6 +616,92 @@ public static IEnumerable<HashSet<string>> ToHashSets(this RedisResult result)
return sets;
}

public static Dictionary<string, Dictionary<string, double>> ToFtSpellCheckResult(this RedisResult result)
{
var rawTerms = (RedisResult[])result!;
var returnTerms = new Dictionary<string, Dictionary<string, double>>(rawTerms.Length);
foreach (var term in rawTerms)
{
var rawElements = (RedisResult[])term!;
string header = rawElements[0].ToString()!; // Should be == "TERM"

string termValue = rawElements[1].ToString()!;

var list = (RedisResult[]) rawElements[2]!;
Dictionary<string, double> entries = new Dictionary<string, double>(list.Length);
foreach (var entry in list)
{
var entryElements = (RedisResult[])entry!;
string suggestion = entryElements[1].ToString()!;
double score = (double)entryElements[0];
entries.Add(suggestion, score);
}
returnTerms.Add(termValue, entries);
}

return returnTerms;
}

public static List<Tuple<string, double>> ToStringDoubleTupleList(this RedisResult result) // TODO: consider create class Suggestion instead of List<Tuple<string, double>>
{
var results = (RedisResult[])result!;
var list = new List<Tuple<string, double>>(results.Length / 2);
for (int i = 0; i < results.Length; i += 2)
{
var suggestion = results[i].ToString()!;
var score = (double)results[i + 1];
list.Add(new Tuple<string, double>(suggestion, score));
}
return list;
}
public static Dictionary<string, RedisResult> ToStringRedisResultDictionary(this RedisResult value)
{
var res = (RedisResult[])value!;
var dict = new Dictionary<string, RedisResult>();
foreach (var pair in res)
{
var arr = (RedisResult[])pair!;
dict.Add(arr[0].ToString(), arr[1]);
}
return dict;
}

public static Tuple<SearchResult, Dictionary<string, RedisResult>> ToProfileSearchResult(this RedisResult result, Query q)
{
var results = (RedisResult[])result!;

var searchResult = results[0].ToSearchResult(q);
var profile = results[1].ToStringRedisResultDictionary();
return new Tuple<SearchResult, Dictionary<string, RedisResult>>(searchResult, profile);
}

public static SearchResult ToSearchResult(this RedisResult result, Query q)
{
return new SearchResult((RedisResult[])result!, !q.NoContent, q.WithScores, q.WithPayloads/*, q.ExplainScore*/);
}

public static Tuple<AggregationResult, Dictionary<string, RedisResult>> ToProfileAggregateResult(this RedisResult result, AggregationRequest q)
{
var results = (RedisResult[])result!;
var aggregateResult = results[0].ToAggregationResult(q);
var profile = results[1].ToStringRedisResultDictionary();
return new Tuple<AggregationResult, Dictionary<string, RedisResult>>(aggregateResult, profile);
}

public static AggregationResult ToAggregationResult(this RedisResult result, AggregationRequest query)
{
if (query.IsWithCursor())
{
var results = (RedisResult[])result!;

return new AggregationResult(results[0], (long)results[1]);
}
else
{
return new AggregationResult(result);
}
}

public static Dictionary<string, RedisResult>[] ToDictionarys(this RedisResult result)
{
var resArr = (RedisResult[])result!;
Expand All @@ -624,6 +712,7 @@ public static Dictionary<string, RedisResult>[] ToDictionarys(this RedisResult r
}

return dicts;

}
}
}
103 changes: 103 additions & 0 deletions src/NRedisStack/Search/FTSpellCheckParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using NRedisStack.Search.Literals;
namespace NRedisStack.Search
{
public class FTSpellCheckParams
{
List<object> args = new List<object>();
private List<KeyValuePair<string, string>>? terms = null;
private int? distance = null;
private int? dialect = null;

public FTSpellCheckParams() { }

/// <summary>
/// Specifies an inclusion (INCLUDE) of a custom dictionary.
/// </summary>
public FTSpellCheckParams IncludeTerm(string dict)
{
return AddTerm(dict, SearchArgs.INCLUDE);
}

/// <summary>
/// Specifies an inclusion (EXCLUDE) of a custom dictionary.
/// </summary>
public FTSpellCheckParams ExcludeTerm(string dict)
{
return AddTerm(dict, SearchArgs.EXCLUDE);
}

/// <summary>
/// Specifies an inclusion (INCLUDE) or exclusion (EXCLUDE) of a custom dictionary.
/// </summary>
private FTSpellCheckParams AddTerm(string dict, string type)
{
if (terms == null)
{
terms = new List<KeyValuePair<string, string>>();
}
terms.Add(new KeyValuePair<string, string>(dict, type));
return this;
}

/// <summary>
/// Maximum Levenshtein distance for spelling suggestions (default: 1, max: 4).
/// </summary>
public FTSpellCheckParams Distance(int distance)
{
this.distance = distance;
return this;
}

/// <summary>
/// Selects the dialect version under which to execute the query.
/// </summary>
public FTSpellCheckParams Dialect(int dialect)
{
this.dialect = dialect;
return this;
}

public List<object> GetArgs()
{
return args;
}

public void SerializeRedisArgs()
{
Distance();
Terms();
Dialect();
}

private void Dialect()
{
if (dialect != null)
{
args.Add(SearchArgs.DIALECT);
args.Add(dialect);
}
}

private void Terms()
{
if (terms != null)
{
foreach (var term in terms)
{
args.Add(SearchArgs.TERMS);
args.Add(term.Value);
args.Add(term.Key);
}
}
}

private void Distance()
{
if (distance != null)
{
args.Add(SearchArgs.DISTANCE);
args.Add(distance);
}
}
}
}
Loading