Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
梦境迷离
Zio Redis
提交
617e6a2d
Z
Zio Redis
项目概览
梦境迷离
/
Zio Redis
9 个月 前同步成功
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Z
Zio Redis
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
617e6a2d
编写于
4月 15, 2023
作者:
D
Dragutin Marjanović
提交者:
GitHub
4月 15, 2023
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add 'WithScore' option both to 'zRank' and 'zRevRank' commands (#824)
上级
640b9a2c
变更
12
展开全部
隐藏空白更改
内联
并排
Showing
12 changed file
with
629 addition
and
601 deletion
+629
-601
docker/redis-cluster-compose.yml
docker/redis-cluster-compose.yml
+1
-1
docker/redis-compose.yml
docker/redis-compose.yml
+2
-2
modules/redis/src/main/scala/zio/redis/Input.scala
modules/redis/src/main/scala/zio/redis/Input.scala
+21
-16
modules/redis/src/main/scala/zio/redis/Output.scala
modules/redis/src/main/scala/zio/redis/Output.scala
+5
-5
modules/redis/src/main/scala/zio/redis/api/SortedSets.scala
modules/redis/src/main/scala/zio/redis/api/SortedSets.scala
+73
-32
modules/redis/src/main/scala/zio/redis/api/Strings.scala
modules/redis/src/main/scala/zio/redis/api/Strings.scala
+30
-38
modules/redis/src/main/scala/zio/redis/options/SortedSets.scala
...s/redis/src/main/scala/zio/redis/options/SortedSets.scala
+9
-1
modules/redis/src/main/scala/zio/redis/options/Strings.scala
modules/redis/src/main/scala/zio/redis/options/Strings.scala
+9
-21
modules/redis/src/test/scala/zio/redis/InputSpec.scala
modules/redis/src/test/scala/zio/redis/InputSpec.scala
+20
-13
modules/redis/src/test/scala/zio/redis/KeysSpec.scala
modules/redis/src/test/scala/zio/redis/KeysSpec.scala
+1
-1
modules/redis/src/test/scala/zio/redis/SortedSetsSpec.scala
modules/redis/src/test/scala/zio/redis/SortedSetsSpec.scala
+415
-375
modules/redis/src/test/scala/zio/redis/StringsSpec.scala
modules/redis/src/test/scala/zio/redis/StringsSpec.scala
+43
-96
未找到文件。
docker/redis-cluster-compose.yml
浏览文件 @
617e6a2d
version
:
'
3.3'
services
:
redis-cluster
:
image
:
'
redis:6.2.7-alpine'
image
:
redis:7.2-rc1-alpine
container_name
:
redis-cluster
volumes
:
-
./redis-cluster.sh:/data/redis-cluster.sh
...
...
docker/redis-compose.yml
浏览文件 @
617e6a2d
...
...
@@ -3,12 +3,12 @@ version: "3.2"
services
:
redis1
:
container_name
:
test_redis_1
image
:
redis:
6.2.7
-alpine
image
:
redis:
7.2-rc1
-alpine
ports
:
-
"
6379:6379"
redis2
:
container_name
:
test_redis_2
image
:
redis:
6.2.7
-alpine
image
:
redis:
7.2-rc1
-alpine
ports
:
-
"
6380:6379"
modules/redis/src/main/scala/zio/redis/Input.scala
浏览文件 @
617e6a2d
...
...
@@ -337,6 +337,22 @@ object Input {
RespCommand
(
RespCommandArgument
.
Literal
(
data
.
asString
))
}
case
object
LcsQueryTypeInput
extends
Input
[
LcsQueryType
]
{
def
encode
(
data
:
LcsQueryType
)
:
RespCommand
=
data
match
{
case
LcsQueryType
.
Len
=>
RespCommand
(
RespCommandArgument
.
Literal
(
"LEN"
))
case
LcsQueryType
.
Idx
(
minMatchLength
,
withMatchLength
)
=>
val
idx
=
Chunk
.
single
(
RespCommandArgument
.
Literal
(
"IDX"
))
val
min
=
if
(
minMatchLength
>
1
)
Chunk
(
RespCommandArgument
.
Literal
(
"MINMATCHLEN"
),
RespCommandArgument
.
Value
(
minMatchLength
.
toString
))
else
Chunk
.
empty
[
RespCommandArgument
]
val
length
=
if
(
withMatchLength
)
Chunk
.
single
(
RespCommandArgument
.
Literal
(
"WITHMATCHLEN"
))
else
Chunk
.
empty
[
RespCommandArgument
]
RespCommand
(
Chunk
(
idx
,
min
,
length
).
flatten
)
}
}
case
object
LimitInput
extends
Input
[
Limit
]
{
def
encode
(
data
:
Limit
)
:
RespCommand
=
RespCommand
(
...
...
@@ -458,22 +474,6 @@ object Input {
RespCommand
(
RespCommandArgument
.
Literal
(
"STORE"
),
RespCommandArgument
.
Value
(
data
.
key
))
}
case
object
StrAlgoLcsQueryTypeInput
extends
Input
[
StrAlgoLcsQueryType
]
{
def
encode
(
data
:
StrAlgoLcsQueryType
)
:
RespCommand
=
data
match
{
case
StrAlgoLcsQueryType
.
Len
=>
RespCommand
(
RespCommandArgument
.
Literal
(
"LEN"
))
case
StrAlgoLcsQueryType
.
Idx
(
minMatchLength
,
withMatchLength
)
=>
val
idx
=
Chunk
.
single
(
RespCommandArgument
.
Literal
(
"IDX"
))
val
min
=
if
(
minMatchLength
>
1
)
Chunk
(
RespCommandArgument
.
Literal
(
"MINMATCHLEN"
),
RespCommandArgument
.
Value
(
minMatchLength
.
toString
))
else
Chunk
.
empty
[
RespCommandArgument
]
val
length
=
if
(
withMatchLength
)
Chunk
.
single
(
RespCommandArgument
.
Literal
(
"WITHMATCHLEN"
))
else
Chunk
.
empty
[
RespCommandArgument
]
RespCommand
(
Chunk
(
idx
,
min
,
length
).
flatten
)
}
}
case
object
StreamMaxLenInput
extends
Input
[
StreamMaxLen
]
{
def
encode
(
data
:
StreamMaxLen
)
:
RespCommand
=
{
val
chunk
=
...
...
@@ -670,6 +670,11 @@ object Input {
RespCommand
(
RespCommandArgument
.
Literal
(
data
.
asString
))
}
case
object
WithScoreInput
extends
Input
[
WithScore
]
{
def
encode
(
data
:
WithScore
)
:
RespCommand
=
RespCommand
(
RespCommandArgument
.
Literal
(
data
.
asString
))
}
case
object
WithScoresInput
extends
Input
[
WithScores
]
{
def
encode
(
data
:
WithScores
)
:
RespCommand
=
RespCommand
(
RespCommandArgument
.
Literal
(
data
.
asString
))
...
...
modules/redis/src/main/scala/zio/redis/Output.scala
浏览文件 @
617e6a2d
...
...
@@ -409,11 +409,11 @@ object Output {
}
}
case
object
StrAlgoLcsOutput
extends
Output
[
LcsOutput
]
{
protected
def
tryDecode
(
respValue
:
RespValue
)
:
Lcs
Output
=
case
object
LcsOutput
extends
Output
[
Lcs
]
{
protected
def
tryDecode
(
respValue
:
RespValue
)
:
Lcs
=
respValue
match
{
case
result
@
RespValue
.
BulkString
(
_
)
=>
Lcs
Output
.
Lcs
(
result
.
asString
)
case
RespValue
.
Integer
(
length
)
=>
Lcs
Output
.
Length
(
length
)
case
result
@
RespValue
.
BulkString
(
_
)
=>
Lcs
.
Plain
Lcs
(
result
.
asString
)
case
RespValue
.
Integer
(
length
)
=>
Lcs
.
Length
(
length
)
case
RespValue
.
ArrayValues
(
RespValue
.
BulkString
(
_
),
RespValue
.
Array
(
items
),
...
...
@@ -435,7 +435,7 @@ object Output {
Match
(
matchIdxs
(
0
),
matchIdxs
(
1
),
matchLength
)
case
other
=>
throw
ProtocolError
(
s
"$other isn't a valid response"
)
}
Lcs
Output
.
Matches
(
matches
.
toList
,
length
)
Lcs
.
Matches
(
matches
.
toList
,
length
)
case
other
=>
throw
ProtocolError
(
s
"$other isn't a valid set response"
)
}
}
...
...
modules/redis/src/main/scala/zio/redis/api/SortedSets.scala
浏览文件 @
617e6a2d
...
...
@@ -50,7 +50,7 @@ trait SortedSets extends RedisEnvironment {
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
Option
[(
K
,
MemberScore
[
M
])]]
=
{
val
memberScoreOutput
=
Tuple3Output
(
ArbitraryOutput
[
K
](),
ArbitraryOutput
[
M
](),
DoubleOutput
).
map
{
case
(
k
,
m
,
s
)
=>
(
k
,
MemberScore
(
s
,
m
))
(
k
,
MemberScore
(
m
,
s
))
}
val
command
=
RedisCommand
(
BzPopMax
,
...
...
@@ -85,7 +85,7 @@ trait SortedSets extends RedisEnvironment {
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
Option
[(
K
,
MemberScore
[
M
])]]
=
{
val
memberScoreOutput
=
Tuple3Output
(
ArbitraryOutput
[
K
](),
ArbitraryOutput
[
M
](),
DoubleOutput
).
map
{
case
(
k
,
m
,
s
)
=>
(
k
,
MemberScore
(
s
,
m
))
(
k
,
MemberScore
(
m
,
s
))
}
val
command
=
RedisCommand
(
BzPopMin
,
...
...
@@ -245,13 +245,13 @@ trait SortedSets extends RedisEnvironment {
Tuple3
(
IntInput
,
NonEmptyList
(
ArbitraryKeyInput
[
K
]()),
ArbitraryValueInput
[
String
]()
WithScoresInput
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
WithScores
.
asString
))
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
WithScores
))
}
}
...
...
@@ -366,13 +366,13 @@ trait SortedSets extends RedisEnvironment {
NonEmptyList
(
ArbitraryKeyInput
[
K
]()),
OptionalInput
(
AggregateInput
),
OptionalInput
(
WeightsInput
),
ArbitraryValueInput
[
String
]()
WithScoresInput
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
aggregate
,
weights
,
WithScores
.
asString
))
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
aggregate
,
weights
,
WithScores
))
}
}
...
...
@@ -468,7 +468,7 @@ trait SortedSets extends RedisEnvironment {
ZPopMax
,
Tuple2
(
ArbitraryKeyInput
[
K
](),
OptionalInput
(
LongInput
)),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
count
))
...
...
@@ -494,7 +494,7 @@ trait SortedSets extends RedisEnvironment {
ZPopMin
,
Tuple2
(
ArbitraryKeyInput
[
K
](),
OptionalInput
(
LongInput
)),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
count
))
...
...
@@ -558,13 +558,13 @@ trait SortedSets extends RedisEnvironment {
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
Chunk
[
MemberScore
[
M
]]]
=
{
val
command
=
RedisCommand
(
ZRandMember
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
LongInput
,
ArbitraryValueInput
[
String
]()
),
Tuple3
(
ArbitraryKeyInput
[
K
](),
LongInput
,
WithScoresInput
),
ZRandMemberTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
count
,
WithScores
.
asString
))
command
.
run
((
key
,
count
,
WithScores
))
}
}
...
...
@@ -602,12 +602,12 @@ trait SortedSets extends RedisEnvironment {
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
Chunk
[
MemberScore
[
M
]]]
=
{
val
command
=
RedisCommand
(
ZRange
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
RangeInput
,
ArbitraryValueInput
[
String
]()
),
Tuple3
(
ArbitraryKeyInput
[
K
](),
RangeInput
,
WithScoresInput
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
range
,
WithScores
.
asString
))
command
.
run
((
key
,
range
,
WithScores
))
}
}
...
...
@@ -703,26 +703,26 @@ trait SortedSets extends RedisEnvironment {
ArbitraryKeyInput
[
K
](),
ArbitraryValueInput
[
String
](),
ArbitraryValueInput
[
String
](),
ArbitraryValueInput
[
String
]()
,
WithScoresInput
,
OptionalInput
(
LimitInput
)
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
scoreRange
.
min
.
asString
,
scoreRange
.
max
.
asString
,
WithScores
.
asString
,
limit
))
command
.
run
((
key
,
scoreRange
.
min
.
asString
,
scoreRange
.
max
.
asString
,
WithScores
,
limit
))
}
}
/**
* Determine the index of a member in a sorted set.
* Determine the index of a member in a sorted set
, with scores ordered from low to high
.
*
* @param key
* Key of a sorted set
* @param member
* Member of sorted set
* @return
* The rank of member in the sorted set stored at key
, with the scores ordered from low to high
.
* The rank of member in the sorted set stored at key.
*/
final
def
zRank
[
K:
Schema
,
M:
Schema
](
key
:
K
,
member
:
M
)
:
IO
[
RedisError
,
Option
[
Long
]]
=
{
val
command
=
...
...
@@ -735,6 +735,27 @@ trait SortedSets extends RedisEnvironment {
command
.
run
((
key
,
member
))
}
/**
* Determine the index and score of a member in a sorted set, with scores ordered from low to high.
*
* @param key
* Key of a sorted set
* @param member
* Member of sorted set
* @return
* The rank of member along with the score in the sorted set stored at key.
*/
final
def
zRankWithScore
[
K:
Schema
,
M:
Schema
](
key
:
K
,
member
:
M
)
:
IO
[
RedisError
,
Option
[
RankScore
]]
=
{
val
command
=
RedisCommand
(
ZRank
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
ArbitraryValueInput
[
M
](),
WithScoreInput
),
OptionalOutput
(
Tuple2Output
(
LongOutput
,
DoubleOutput
).
map
{
case
(
r
,
s
)
=>
RankScore
(
r
,
s
)
}),
executor
)
command
.
run
((
key
,
member
,
WithScore
))
}
/**
* Remove one or more members from a sorted set.
*
...
...
@@ -846,12 +867,12 @@ trait SortedSets extends RedisEnvironment {
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
Chunk
[
MemberScore
[
M
]]]
=
{
val
command
=
RedisCommand
(
ZRevRange
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
RangeInput
,
ArbitraryValueInput
[
String
]()
),
Tuple3
(
ArbitraryKeyInput
[
K
](),
RangeInput
,
WithScoresInput
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
range
,
WithScores
.
asString
))
command
.
run
((
key
,
range
,
WithScores
))
}
}
...
...
@@ -947,14 +968,14 @@ trait SortedSets extends RedisEnvironment {
ArbitraryKeyInput
[
K
](),
ArbitraryValueInput
[
String
](),
ArbitraryValueInput
[
String
](),
ArbitraryValueInput
[
String
]()
,
WithScoresInput
,
OptionalInput
(
LimitInput
)
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
key
,
scoreRange
.
max
.
asString
,
scoreRange
.
min
.
asString
,
WithScores
.
asString
,
limit
))
command
.
run
((
key
,
scoreRange
.
max
.
asString
,
scoreRange
.
min
.
asString
,
WithScores
,
limit
))
}
}
...
...
@@ -966,7 +987,7 @@ trait SortedSets extends RedisEnvironment {
* @param member
* Member of sorted set
* @return
* The rank of member.
* The rank of member
in the sorted set stored at key
.
*/
final
def
zRevRank
[
K:
Schema
,
M:
Schema
](
key
:
K
,
member
:
M
)
:
IO
[
RedisError
,
Option
[
Long
]]
=
{
val
command
=
RedisCommand
(
...
...
@@ -978,6 +999,26 @@ trait SortedSets extends RedisEnvironment {
command
.
run
((
key
,
member
))
}
/**
* Determine the index and score of a member in a sorted set, with scores ordered from high to low.
*
* @param key
* Key of a sorted set
* @param member
* Member of sorted set
* @return
* The rank of member along with the score in the sorted set stored at key.
*/
final
def
zRevRankWithScore
[
K:
Schema
,
M:
Schema
](
key
:
K
,
member
:
M
)
:
IO
[
RedisError
,
Option
[
RankScore
]]
=
{
val
command
=
RedisCommand
(
ZRevRank
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
ArbitraryValueInput
[
M
](),
WithScoreInput
),
OptionalOutput
(
Tuple2Output
(
LongOutput
,
DoubleOutput
).
map
{
case
(
r
,
s
)
=>
RankScore
(
r
,
s
)
}),
executor
)
command
.
run
((
key
,
member
,
WithScore
))
}
/**
* Incrementally iterate sorted sets elements and associated scores.
*
...
...
@@ -1001,7 +1042,7 @@ trait SortedSets extends RedisEnvironment {
new
ResultBuilder1
[({
type
lambda
[
x
]
=
(
Long
,
MemberScores
[
x
])
})
#
lambda
]
{
def
returning
[
M:
Schema
]
:
IO
[
RedisError
,
(
Long
,
Chunk
[
MemberScore
[
M
]])]
=
{
val
memberScoresOutput
=
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
).
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
})
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
).
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
})
val
command
=
RedisCommand
(
ZScan
,
Tuple4
(
ArbitraryKeyInput
[
K
](),
LongInput
,
OptionalInput
(
PatternInput
),
OptionalInput
(
CountInput
)),
...
...
@@ -1100,13 +1141,13 @@ trait SortedSets extends RedisEnvironment {
NonEmptyList
(
ArbitraryKeyInput
[
K
]()),
OptionalInput
(
WeightsInput
),
OptionalInput
(
AggregateInput
),
ArbitraryValueInput
[
String
]()
WithScoresInput
),
ChunkTuple2Output
(
ArbitraryOutput
[
M
](),
DoubleOutput
)
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
s
,
m
)
}),
.
map
(
_
.
map
{
case
(
m
,
s
)
=>
MemberScore
(
m
,
s
)
}),
executor
)
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
weights
,
aggregate
,
WithScores
.
asString
))
command
.
run
((
keys
.
size
+
1
,
(
key
,
keys
.
toList
),
weights
,
aggregate
,
WithScores
))
}
}
...
...
modules/redis/src/main/scala/zio/redis/api/Strings.scala
浏览文件 @
617e6a2d
...
...
@@ -360,6 +360,35 @@ trait Strings extends RedisEnvironment {
command
.
run
((
key
,
increment
))
}
/**
* Get the longest common subsequence of values stored in the given keys.
*
* @param keyA
* first value that will contain subsequence
* @param keyB
* second value that will contain subsequence
* @param lcsQueryType
* modifier that will affect the output
* @return
* Without modifiers returns the string representing the longest common substring. When LEN is given the command
* returns the length of the longest common substring. When IDX is given the command returns an array with the LCS
* length and all the ranges in both the strings, start and end offset for each string, where there are matches.
* When withMatchLen is given each array representing a match will also have the length of the match (see examples).
*/
final
def
lcs
[
K:
Schema
](
keyA
:
K
,
keyB
:
K
,
lcsQueryType
:
Option
[
LcsQueryType
]
=
None
)
:
IO
[
RedisError
,
Lcs
]
=
{
val
redisCommand
=
RedisCommand
(
Lcs
,
Tuple3
(
ArbitraryKeyInput
[
K
](),
ArbitraryKeyInput
[
K
](),
OptionalInput
(
LcsQueryTypeInput
)
),
LcsOutput
,
executor
)
redisCommand
.
run
((
keyA
,
keyB
,
lcsQueryType
))
}
/**
* Get all the values of the given keys.
*
...
...
@@ -593,43 +622,6 @@ trait Strings extends RedisEnvironment {
command
.
run
((
key
,
offset
,
value
))
}
/**
* Get the longest common subsequence of values stored in the given keys.
*
* @param command
* type of value it (possible values are Strings and Keys)
* @param keyA
* first value that will contain subsequence
* @param keyB
* second value that will contain subsequence
* @param lcsQueryType
* modifier that will affect the output
* @return
* Without modifiers returns the string representing the longest common substring. When LEN is given the command
* returns the length of the longest common substring. When IDX is given the command returns an array with the LCS
* length and all the ranges in both the strings, start and end offset for each string, where there are matches.
* When withMatchLen is given each array representing a match will also have the length of the match (see examples).
*/
final
def
strAlgoLcs
[
K:
Schema
](
command
:
StrAlgoLCS
,
keyA
:
K
,
keyB
:
K
,
lcsQueryType
:
Option
[
StrAlgoLcsQueryType
]
=
None
)
:
IO
[
RedisError
,
LcsOutput
]
=
{
val
redisCommand
=
RedisCommand
(
StrAlgoLcs
,
Tuple4
(
ArbitraryValueInput
[
String
](),
ArbitraryKeyInput
[
K
](),
ArbitraryKeyInput
[
K
](),
OptionalInput
(
StrAlgoLcsQueryTypeInput
)
),
StrAlgoLcsOutput
,
executor
)
redisCommand
.
run
((
command
.
asString
,
keyA
,
keyB
,
lcsQueryType
))
}
/**
* Get the length of a value stored in a key.
*
...
...
@@ -661,6 +653,7 @@ private[redis] object Strings {
final
val
Incr
=
"INCR"
final
val
IncrBy
=
"INCRBY"
final
val
IncrByFloat
=
"INCRBYFLOAT"
final
val
Lcs
=
"LCS"
final
val
MGet
=
"MGET"
final
val
MSet
=
"MSET"
final
val
MSetNx
=
"MSETNX"
...
...
@@ -670,6 +663,5 @@ private[redis] object Strings {
final
val
SetEx
=
"SETEX"
final
val
SetNx
=
"SETNX"
final
val
SetRange
=
"SETRANGE"
final
val
StrAlgoLcs
=
"STRALGO LCS"
final
val
StrLen
=
"STRLEN"
}
modules/redis/src/main/scala/zio/redis/options/SortedSets.scala
浏览文件 @
617e6a2d
...
...
@@ -78,10 +78,12 @@ trait SortedSets {
sealed
case
class
LexRange
(
min
:
LexMinimum
,
max
:
LexMaximum
)
sealed
case
class
MemberScore
[
+M
](
score
:
Double
,
member
:
M
)
sealed
case
class
MemberScore
[
+M
](
member
:
M
,
score
:
Double
)
type
MemberScores
[
+M
]
=
Chunk
[
MemberScore
[
M
]]
sealed
case
class
RankScore
(
rank
:
Long
,
score
:
Double
)
sealed
trait
ScoreMaximum
{
self
=>
private
[
redis
]
final
def
asString
:
String
=
self
match
{
...
...
@@ -114,6 +116,12 @@ trait SortedSets {
sealed
case
class
ScoreRange
(
min
:
ScoreMinimum
,
max
:
ScoreMaximum
)
case
object
WithScore
{
private
[
redis
]
def
asString
:
String
=
"WITHSCORE"
}
type
WithScore
=
WithScore
.
type
case
object
WithScores
{
private
[
redis
]
def
asString
:
String
=
"WITHSCORES"
}
...
...
modules/redis/src/main/scala/zio/redis/options/Strings.scala
浏览文件 @
617e6a2d
...
...
@@ -17,32 +17,20 @@
package
zio.redis.options
trait
Strings
{
sealed
trait
StrAlgoLCS
{
self
=>
private
[
redis
]
final
def
asString
:
String
=
self
match
{
case
StralgoLCS
.
Strings
=>
"STRINGS"
case
StralgoLCS
.
Keys
=>
"KEYS"
}
}
object
StralgoLCS
{
case
object
Strings
extends
StrAlgoLCS
case
object
Keys
extends
StrAlgoLCS
}
sealed
trait
StrAlgoLcsQueryType
sealed
trait
Lcs
object
StrAlgoLcsQueryType
{
case
object
Len
extends
StrAlgoLcsQueryType
case
class
Idx
(
minMatchLength
:
Int
=
1
,
withMatchLength
:
Boolean
=
false
)
extends
StrAlgoLcsQueryType
object
Lcs
{
case
class
PlainLcs
(
lcs
:
String
)
extends
Lcs
case
class
Length
(
length
:
Long
)
extends
Lcs
case
class
Matches
(
matches
:
List
[
Match
],
length
:
Long
)
extends
Lcs
}
sealed
trait
Lcs
Output
sealed
trait
Lcs
QueryType
object
LcsOutput
{
case
class
Lcs
(
lcs
:
String
)
extends
LcsOutput
case
class
Length
(
length
:
Long
)
extends
LcsOutput
case
class
Matches
(
matches
:
List
[
Match
],
length
:
Long
)
extends
LcsOutput
object
LcsQueryType
{
case
object
Len
extends
LcsQueryType
case
class
Idx
(
minMatchLength
:
Int
=
1
,
withMatchLength
:
Boolean
=
false
)
extends
LcsQueryType
}
case
class
MatchIdx
(
start
:
Long
,
end
:
Long
)
...
...
modules/redis/src/test/scala/zio/redis/InputSpec.scala
浏览文件 @
617e6a2d
...
...
@@ -16,7 +16,7 @@ object InputSpec extends BaseSpec {
import
BitOperation._
import
Order._
import
RadiusUnit._
import
StrAlgo
LcsQueryType._
import
LcsQueryType._
def
spec
:
Spec
[
Any
,
Throwable
]
=
suite
(
"Input encoders"
)(
...
...
@@ -89,29 +89,29 @@ object InputSpec extends BaseSpec {
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Literal
(
"0"
))))
}
),
suite
(
"
Stralgocommand
"
)(
suite
(
"
LcsQueryType
"
)(
test
(
"length option"
)
{
assert
(
StrAlgoLcsQueryTypeInput
.
encode
(
StrAlgo
LcsQueryType
.
Len
))(
assert
(
LcsQueryTypeInput
.
encode
(
LcsQueryType
.
Len
))(
equalTo
(
RespCommand
(
Literal
(
"LEN"
)))
)
},
test
(
"idx option default"
)
{
assert
(
StrAlgo
LcsQueryTypeInput
.
encode
(
Idx
()))(
assert
(
LcsQueryTypeInput
.
encode
(
Idx
()))(
equalTo
(
RespCommand
(
Literal
(
"IDX"
)))
)
},
test
(
"idx option with minmatchlength"
)
{
assert
(
StrAlgo
LcsQueryTypeInput
.
encode
(
Idx
(
minMatchLength
=
2
)))(
assert
(
LcsQueryTypeInput
.
encode
(
Idx
(
minMatchLength
=
2
)))(
equalTo
(
RespCommand
(
Literal
(
"IDX"
),
Literal
(
"MINMATCHLEN"
),
Value
(
"2"
)))
)
},
test
(
"idx option with withmatchlength"
)
{
assert
(
StrAlgo
LcsQueryTypeInput
.
encode
(
Idx
(
withMatchLength
=
true
)))(
assert
(
LcsQueryTypeInput
.
encode
(
Idx
(
withMatchLength
=
true
)))(
equalTo
(
RespCommand
(
Literal
(
"IDX"
),
Literal
(
"WITHMATCHLEN"
)))
)
},
test
(
"idx option with minmatchlength and withmatchlength"
)
{
assert
(
StrAlgo
LcsQueryTypeInput
.
encode
(
Idx
(
minMatchLength
=
2
,
withMatchLength
=
true
)))(
assert
(
LcsQueryTypeInput
.
encode
(
Idx
(
minMatchLength
=
2
,
withMatchLength
=
true
)))(
equalTo
(
RespCommand
(
Literal
(
"IDX"
),
Literal
(
"MINMATCHLEN"
),
Value
(
"2"
),
Literal
(
"WITHMATCHLEN"
)))
)
}
...
...
@@ -628,32 +628,32 @@ object InputSpec extends BaseSpec {
suite
(
"MemberScore"
)(
test
(
"with positive score and empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
4.2d
,
""
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
""
,
4.2d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"4.2"
),
Value
(
""
))))
},
test
(
"with negative score and empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
-
4.2d
,
""
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
""
,
-
4.2d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"-4.2"
),
Value
(
""
))))
},
test
(
"with zero score and empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
0d
,
""
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
""
,
0d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"0.0"
),
Value
(
""
))))
},
test
(
"with positive score and non-empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
4.2d
,
"member"
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
"member"
,
4.2d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"4.2"
),
Value
(
"member"
))))
},
test
(
"with negative score and non-empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
-
4.2d
,
"member"
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
"member"
,
-
4.2d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"-4.2"
),
Value
(
"member"
))))
},
test
(
"with zero score and non-empty member"
)
{
for
{
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
0d
,
"member"
)))
result
<-
ZIO
.
attempt
(
MemberScoreInput
[
String
]().
encode
(
MemberScore
(
"member"
,
0d
)))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Value
(
"0.0"
),
Value
(
"member"
))))
}
),
...
...
@@ -1118,6 +1118,13 @@ object InputSpec extends BaseSpec {
}
yield
assert
(
result
.
args
)(
isEmpty
)
}
),
suite
(
"WithScore"
)(
test
(
"valid value"
)
{
for
{
result
<-
ZIO
.
attempt
(
WithScoreInput
.
encode
(
WithScore
))
}
yield
assert
(
result
)(
equalTo
(
RespCommand
(
Literal
(
"WITHSCORE"
))))
}
),
suite
(
"WithScores"
)(
test
(
"valid value"
)
{
for
{
...
...
modules/redis/src/test/scala/zio/redis/KeysSpec.scala
浏览文件 @
617e6a2d
...
...
@@ -377,7 +377,7 @@ trait KeysSpec extends BaseSpec {
redis
<-
ZIO
.
service
[
Redis
]
key
<-
uuid
value
<-
uuid
_
<-
redis
.
zAdd
(
key
)(
MemberScore
(
1d
,
value
))
_
<-
redis
.
zAdd
(
key
)(
MemberScore
(
value
,
1d
))
zset
<-
redis
.
typeOf
(
key
)
}
yield
assert
(
zset
)(
equalTo
(
RedisType
.
SortedSet
))
},
...
...
modules/redis/src/test/scala/zio/redis/SortedSetsSpec.scala
浏览文件 @
617e6a2d
此差异已折叠。
点击以展开。
modules/redis/src/test/scala/zio/redis/StringsSpec.scala
浏览文件 @
617e6a2d
...
...
@@ -317,15 +317,8 @@ trait StringsSpec extends BaseSpec {
}
yield
assert
(
result
)(
equalTo
(
Chunk
(
Some
(
97L
),
Some
(
100L
),
Some
(
100L
))))
}
),
suite
(
"Stralgo"
)(
test
(
"get LCS from 2 strings"
)
{
val
str1
=
"foo"
val
str2
=
"fao"
assertZIO
(
ZIO
.
serviceWithZIO
[
Redis
](
_
.
strAlgoLcs
(
StralgoLCS
.
Strings
,
str1
,
str2
)))(
equalTo
(
LcsOutput
.
Lcs
(
"fo"
))
)
},
test
(
"get LCS from 2 keys"
)
{
suite
(
"LCS"
)(
test
(
"get LCS from two keys"
)
{
val
str1
=
"foo"
val
str2
=
"fao"
...
...
@@ -335,8 +328,8 @@ trait StringsSpec extends BaseSpec {
_
<-
redis
.
set
(
key1
,
str1
,
None
,
None
,
None
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
,
None
,
None
,
None
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
)
}
yield
assert
(
result
)(
equalTo
(
Lcs
Output
.
Lcs
(
"fo"
)))
result
<-
redis
.
lcs
(
key1
,
key2
)
}
yield
assert
(
result
)(
equalTo
(
Lcs
.
Plain
Lcs
(
"fo"
)))
},
test
(
"get LCS from unknown keys"
)
{
val
str1
=
"foo"
...
...
@@ -348,19 +341,10 @@ trait StringsSpec extends BaseSpec {
_
<-
redis
.
set
(
key1
,
str1
,
None
,
None
,
None
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
,
None
,
None
,
None
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
"unknown"
,
"unknown"
)
}
yield
assert
(
result
)(
equalTo
(
Lcs
Output
.
Lcs
(
""
)))
result
<-
redis
.
lcs
(
"unknown"
,
"unknown"
)
}
yield
assert
(
result
)(
equalTo
(
Lcs
.
Plain
Lcs
(
""
)))
},
test
(
"Get length of LCS for strings"
)
{
val
str1
=
"foo"
val
str2
=
"fao"
assertZIO
(
ZIO
.
serviceWithZIO
[
Redis
](
_
.
strAlgoLcs
(
StralgoLCS
.
Strings
,
str1
,
str2
,
Some
(
StrAlgoLcsQueryType
.
Len
)))
)(
equalTo
(
LcsOutput
.
Length
(
2
))
)
},
test
(
"get length of LCS for keys"
)
{
test
(
"get length of LCS"
)
{
val
str1
=
"foo"
val
str2
=
"fao"
...
...
@@ -370,45 +354,23 @@ trait StringsSpec extends BaseSpec {
_
<-
redis
.
set
(
key1
,
str1
,
None
,
None
,
None
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
,
None
,
None
,
None
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
,
Some
(
StrAlgo
LcsQueryType
.
Len
))
}
yield
assert
(
result
)(
equalTo
(
Lcs
Output
.
Length
(
2
)))
result
<-
redis
.
lcs
(
key1
,
key2
,
Some
(
LcsQueryType
.
Len
))
}
yield
assert
(
result
)(
equalTo
(
Lcs
.
Length
(
2
)))
},
test
(
"get length of LCS for unknown keys"
)
{
val
str1
=
"foo"
val
str2
=
"fao"
for
{
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
,
None
,
None
,
None
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
,
None
,
None
,
None
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
"unknown"
,
"unknown"
,
Some
(
StrAlgoLcsQueryType
.
Len
)
)
}
yield
assert
(
result
)(
equalTo
(
LcsOutput
.
Length
(
0
)))
},
test
(
"get index of LCS for strings"
)
{
val
str1
=
"ohmytext"
val
str2
=
"mynewtext"
assertZIO
(
ZIO
.
serviceWithZIO
[
Redis
](
_
.
strAlgoLcs
(
StralgoLCS
.
Strings
,
str1
,
str2
,
Some
(
StrAlgoLcsQueryType
.
Idx
())))
)(
equalTo
(
LcsOutput
.
Matches
(
List
(
Match
(
matchIdxA
=
MatchIdx
(
4
,
7
),
matchIdxB
=
MatchIdx
(
5
,
8
)),
Match
(
matchIdxA
=
MatchIdx
(
2
,
3
),
matchIdxB
=
MatchIdx
(
0
,
1
))
),
6
)
)
)
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
,
None
,
None
,
None
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
,
None
,
None
,
None
)
result
<-
redis
.
lcs
(
"unknown"
,
"unknown"
,
Some
(
LcsQueryType
.
Len
))
}
yield
assert
(
result
)(
equalTo
(
Lcs
.
Length
(
0
)))
},
test
(
"get index of LCS
for keys
"
)
{
test
(
"get index of LCS"
)
{
val
str1
=
"!ohmytext"
val
str2
=
"!mynewtext"
...
...
@@ -418,11 +380,11 @@ trait StringsSpec extends BaseSpec {
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
,
Some
(
StrAlgo
LcsQueryType
.
Idx
()))
result
<-
redis
.
lcs
(
key1
,
key2
,
Some
(
LcsQueryType
.
Idx
()))
}
yield
{
assert
(
result
)(
equalTo
(
Lcs
Output
.
Matches
(
Lcs
.
Matches
(
List
(
Match
(
matchIdxA
=
MatchIdx
(
5
,
8
),
matchIdxB
=
MatchIdx
(
6
,
9
)),
Match
(
matchIdxA
=
MatchIdx
(
3
,
4
),
matchIdxB
=
MatchIdx
(
1
,
2
)),
...
...
@@ -434,26 +396,21 @@ trait StringsSpec extends BaseSpec {
)
}
},
test
(
"get index of LCS
for keys
with MINMATCHLEN"
)
{
test
(
"get index of LCS with MINMATCHLEN"
)
{
val
str1
=
"!ohmytext"
val
str2
=
"!mynewtext"
for
{
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
,
Some
(
StrAlgoLcsQueryType
.
Idx
(
minMatchLength
=
2
))
)
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
lcs
(
key1
,
key2
,
Some
(
LcsQueryType
.
Idx
(
minMatchLength
=
2
)))
}
yield
{
assert
(
result
)(
equalTo
(
Lcs
Output
.
Matches
(
Lcs
.
Matches
(
List
(
Match
(
matchIdxA
=
MatchIdx
(
5
,
8
),
matchIdxB
=
MatchIdx
(
6
,
9
)),
Match
(
matchIdxA
=
MatchIdx
(
3
,
4
),
matchIdxB
=
MatchIdx
(
1
,
2
))
...
...
@@ -464,26 +421,21 @@ trait StringsSpec extends BaseSpec {
)
}
},
test
(
"get index of LCS
for keys
with WITHMATCHLEN"
)
{
test
(
"get index of LCS with WITHMATCHLEN"
)
{
val
str1
=
"!ohmytext"
val
str2
=
"!mynewtext"
for
{
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
,
Some
(
StrAlgoLcsQueryType
.
Idx
(
withMatchLength
=
true
))
)
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
lcs
(
key1
,
key2
,
Some
(
LcsQueryType
.
Idx
(
withMatchLength
=
true
)))
}
yield
{
assert
(
result
)(
equalTo
(
Lcs
Output
.
Matches
(
Lcs
.
Matches
(
List
(
Match
(
matchIdxA
=
MatchIdx
(
5
,
8
),
matchIdxB
=
MatchIdx
(
6
,
9
),
matchLength
=
Some
(
4
)),
Match
(
matchIdxA
=
MatchIdx
(
3
,
4
),
matchIdxB
=
MatchIdx
(
1
,
2
),
matchLength
=
Some
(
2
)),
...
...
@@ -495,26 +447,21 @@ trait StringsSpec extends BaseSpec {
)
}
},
test
(
"get index of LCS
for keys
with MINMATCHLEN and WITHMATCHLEN"
)
{
test
(
"get index of LCS
both
with MINMATCHLEN and WITHMATCHLEN"
)
{
val
str1
=
"!ohmytext"
val
str2
=
"!mynewtext"
for
{
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
strAlgoLcs
(
StralgoLCS
.
Keys
,
key1
,
key2
,
Some
(
StrAlgoLcsQueryType
.
Idx
(
minMatchLength
=
2
,
withMatchLength
=
true
))
)
redis
<-
ZIO
.
service
[
Redis
]
key1
<-
uuid
_
<-
redis
.
set
(
key1
,
str1
)
key2
<-
uuid
_
<-
redis
.
set
(
key2
,
str2
)
result
<-
redis
.
lcs
(
key1
,
key2
,
Some
(
LcsQueryType
.
Idx
(
minMatchLength
=
2
,
withMatchLength
=
true
)))
}
yield
{
assert
(
result
)(
equalTo
(
Lcs
Output
.
Matches
(
Lcs
.
Matches
(
List
(
Match
(
matchIdxA
=
MatchIdx
(
5
,
8
),
matchIdxB
=
MatchIdx
(
6
,
9
),
matchLength
=
Some
(
4
)),
Match
(
matchIdxA
=
MatchIdx
(
3
,
4
),
matchIdxB
=
MatchIdx
(
1
,
2
),
matchLength
=
Some
(
2
))
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录