@@ -284,6 +284,20 @@ public static Condition StringNotEqual(RedisKey key, RedisValue value)
284
284
/// <param name="member">The member the sorted set must not contain.</param>
285
285
public static Condition SortedSetNotContains ( RedisKey key , RedisValue member ) => new ExistsCondition ( key , RedisType . SortedSet , member , false ) ;
286
286
287
+ /// <summary>
288
+ /// Enforces that the given sorted set contains a member that starts with the specified prefix.
289
+ /// </summary>
290
+ /// <param name="key">The key of the sorted set to check.</param>
291
+ /// <param name="prefix">The sorted set must contain at least one member that starts with the specified prefix.</param>
292
+ public static Condition SortedSetContainsStarting ( RedisKey key , RedisValue prefix ) => new StartsWithCondition ( key , prefix , true ) ;
293
+
294
+ /// <summary>
295
+ /// Enforces that the given sorted set does not contain a member that starts with the specified prefix.
296
+ /// </summary>
297
+ /// <param name="key">The key of the sorted set to check.</param>
298
+ /// <param name="prefix">The sorted set must not contain at a member that starts with the specified prefix.</param>
299
+ public static Condition SortedSetNotContainsStarting ( RedisKey key , RedisValue prefix ) => new StartsWithCondition ( key , prefix , false ) ;
300
+
287
301
/// <summary>
288
302
/// Enforces that the given sorted set member must have the specified score.
289
303
/// </summary>
@@ -370,6 +384,9 @@ public static Message CreateMessage(Condition condition, int db, CommandFlags fl
370
384
public static Message CreateMessage ( Condition condition , int db , CommandFlags flags , RedisCommand command , in RedisKey key , in RedisValue value , in RedisValue value1 ) =>
371
385
new ConditionMessage ( condition , db , flags , command , key , value , value1 ) ;
372
386
387
+ public static Message CreateMessage ( Condition condition , int db , CommandFlags flags , RedisCommand command , in RedisKey key , in RedisValue value , in RedisValue value1 , in RedisValue value2 , in RedisValue value3 , in RedisValue value4 ) =>
388
+ new ConditionMessage ( condition , db , flags , command , key , value , value1 , value2 , value3 , value4 ) ;
389
+
373
390
[ System . Diagnostics . CodeAnalysis . SuppressMessage ( "Style" , "IDE0071:Simplify interpolation" , Justification = "Allocations (string.Concat vs. string.Format)" ) ]
374
391
protected override bool SetResultCore ( PhysicalConnection connection , Message message , in RawResult result )
375
392
{
@@ -389,6 +406,9 @@ private sealed class ConditionMessage : Message.CommandKeyBase
389
406
public readonly Condition Condition ;
390
407
private readonly RedisValue value ;
391
408
private readonly RedisValue value1 ;
409
+ private readonly RedisValue value2 ;
410
+ private readonly RedisValue value3 ;
411
+ private readonly RedisValue value4 ;
392
412
393
413
public ConditionMessage ( Condition condition , int db , CommandFlags flags , RedisCommand command , in RedisKey key , in RedisValue value )
394
414
: base ( db , flags , command , key )
@@ -403,6 +423,15 @@ public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCo
403
423
this . value1 = value1 ; // note no assert here
404
424
}
405
425
426
+ // Message with 3 or 4 values not used, therefore not implemented
427
+ public ConditionMessage ( Condition condition , int db , CommandFlags flags , RedisCommand command , in RedisKey key , in RedisValue value , in RedisValue value1 , in RedisValue value2 , in RedisValue value3 , in RedisValue value4 )
428
+ : this ( condition , db , flags , command , key , value , value1 )
429
+ {
430
+ this . value2 = value2 ; // note no assert here
431
+ this . value3 = value3 ; // note no assert here
432
+ this . value4 = value4 ; // note no assert here
433
+ }
434
+
406
435
protected override void WriteImpl ( PhysicalConnection physical )
407
436
{
408
437
if ( value . IsNull )
@@ -412,16 +441,20 @@ protected override void WriteImpl(PhysicalConnection physical)
412
441
}
413
442
else
414
443
{
415
- physical . WriteHeader ( command , value1 . IsNull ? 2 : 3 ) ;
444
+ physical . WriteHeader ( command , value1 . IsNull ? 2 : value2 . IsNull ? 3 : value3 . IsNull ? 4 : value4 . IsNull ? 5 : 6 ) ;
416
445
physical . Write ( Key ) ;
417
446
physical . WriteBulkString ( value ) ;
418
447
if ( ! value1 . IsNull )
419
- {
420
448
physical . WriteBulkString ( value1 ) ;
421
- }
449
+ if ( ! value2 . IsNull )
450
+ physical . WriteBulkString ( value2 ) ;
451
+ if ( ! value3 . IsNull )
452
+ physical . WriteBulkString ( value3 ) ;
453
+ if ( ! value4 . IsNull )
454
+ physical . WriteBulkString ( value4 ) ;
422
455
}
423
456
}
424
- public override int ArgCount => value . IsNull ? 1 : value1 . IsNull ? 2 : 3 ;
457
+ public override int ArgCount => value . IsNull ? 1 : value1 . IsNull ? 2 : value2 . IsNull ? 3 : value3 . IsNull ? 4 : value4 . IsNull ? 5 : 6 ;
425
458
}
426
459
}
427
460
@@ -501,6 +534,67 @@ internal override bool TryValidate(in RawResult result, out bool value)
501
534
}
502
535
}
503
536
537
+ internal sealed class StartsWithCondition : Condition
538
+ {
539
+ /* only usable for RedisType.SortedSet, members of SortedSets are always byte-arrays, expectedStartValue therefore is a byte-array
540
+ any Encoding and Conversion for the search-sequence has to be executed in calling application
541
+ working with byte arrays should prevent any encoding within this class, that could distort the comparison */
542
+
543
+ private readonly bool expectedResult ;
544
+ private readonly RedisValue prefix ;
545
+ private readonly RedisKey key ;
546
+
547
+ internal override Condition MapKeys ( Func < RedisKey , RedisKey > map ) =>
548
+ new StartsWithCondition ( map ( key ) , prefix , expectedResult ) ;
549
+
550
+ public StartsWithCondition ( in RedisKey key , in RedisValue prefix , bool expectedResult )
551
+ {
552
+ if ( key . IsNull ) throw new ArgumentNullException ( nameof ( key ) ) ;
553
+ if ( prefix . IsNull ) throw new ArgumentNullException ( nameof ( prefix ) ) ;
554
+ this . key = key ;
555
+ this . prefix = prefix ;
556
+ this . expectedResult = expectedResult ;
557
+ }
558
+
559
+ public override string ToString ( ) =>
560
+ $ "{ key } { nameof ( RedisType . SortedSet ) } > { ( expectedResult ? " member starting " : " no member starting " ) } { prefix } + prefix";
561
+
562
+ internal override void CheckCommands ( CommandMap commandMap ) => commandMap . AssertAvailable ( RedisCommand . ZRANGEBYLEX ) ;
563
+
564
+ internal override IEnumerable < Message > CreateMessages ( int db , IResultBox ? resultBox )
565
+ {
566
+ yield return Message . Create ( db , CommandFlags . None , RedisCommand . WATCH , key ) ;
567
+
568
+ // prepend '[' to prefix for inclusive search
569
+ var startValueWithToken = RedisDatabase . GetLexRange ( prefix , Exclude . None , isStart : true , Order . Ascending ) ;
570
+
571
+ var message = ConditionProcessor . CreateMessage (
572
+ this ,
573
+ db ,
574
+ CommandFlags . None ,
575
+ RedisCommand . ZRANGEBYLEX ,
576
+ key ,
577
+ startValueWithToken ,
578
+ RedisLiterals . PlusSymbol ,
579
+ RedisLiterals . LIMIT ,
580
+ 0 ,
581
+ 1 ) ;
582
+
583
+ message . SetSource ( ConditionProcessor . Default , resultBox ) ;
584
+ yield return message ;
585
+ }
586
+
587
+ internal override int GetHashSlot ( ServerSelectionStrategy serverSelectionStrategy ) => serverSelectionStrategy . HashSlot ( key ) ;
588
+
589
+ internal override bool TryValidate ( in RawResult result , out bool value )
590
+ {
591
+ value = result . ItemsCount == 1 && result [ 0 ] . AsRedisValue ( ) . StartsWith ( prefix ) ;
592
+
593
+ if ( ! expectedResult ) value = ! value ;
594
+ return true ;
595
+ }
596
+ }
597
+
504
598
internal sealed class EqualsCondition : Condition
505
599
{
506
600
internal override Condition MapKeys ( Func < RedisKey , RedisKey > map ) =>
0 commit comments