Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
2830ca53
Commit
2830ca53
authored
Mar 09, 2010
by
Pieter Noordhuis
Browse files
replaced ZMERGE by ZUNION and ZINTER. note: key preloading by the VM does not yet work
parent
b287c9bb
Changes
2
Hide whitespace changes
Inline
Side-by-side
redis.c
View file @
2830ca53
...
@@ -675,8 +675,8 @@ static void substrCommand(redisClient *c);
...
@@ -675,8 +675,8 @@ static void substrCommand(redisClient *c);
static
void
zrankCommand
(
redisClient
*
c
);
static
void
zrankCommand
(
redisClient
*
c
);
static
void
hsetCommand
(
redisClient
*
c
);
static
void
hsetCommand
(
redisClient
*
c
);
static
void
hgetCommand
(
redisClient
*
c
);
static
void
hgetCommand
(
redisClient
*
c
);
static
void
z
merge
Command
(
redisClient
*
c
);
static
void
z
union
Command
(
redisClient
*
c
);
static
void
z
mergeweighed
Command
(
redisClient
*
c
);
static
void
z
inter
Command
(
redisClient
*
c
);
/*================================= Globals ================================= */
/*================================= Globals ================================= */
...
@@ -724,8 +724,8 @@ static struct redisCommand cmdTable[] = {
...
@@ -724,8 +724,8 @@ static struct redisCommand cmdTable[] = {
{
"zincrby"
,
zincrbyCommand
,
4
,
REDIS_CMD_BULK
|
REDIS_CMD_DENYOOM
,
1
,
1
,
1
},
{
"zincrby"
,
zincrbyCommand
,
4
,
REDIS_CMD_BULK
|
REDIS_CMD_DENYOOM
,
1
,
1
,
1
},
{
"zrem"
,
zremCommand
,
3
,
REDIS_CMD_BULK
,
1
,
1
,
1
},
{
"zrem"
,
zremCommand
,
3
,
REDIS_CMD_BULK
,
1
,
1
,
1
},
{
"zremrangebyscore"
,
zremrangebyscoreCommand
,
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zremrangebyscore"
,
zremrangebyscoreCommand
,
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"z
merge"
,
zmerge
Command
,
-
3
,
REDIS_CMD_INLINE
|
REDIS_CMD_DENYOOM
,
2
,
-
1
,
1
},
{
"z
union"
,
zunion
Command
,
-
4
,
REDIS_CMD_INLINE
|
REDIS_CMD_DENYOOM
,
0
,
0
,
0
},
{
"z
mergeweighed"
,
zmergeweighed
Command
,
-
4
,
REDIS_CMD_INLINE
|
REDIS_CMD_DENYOOM
,
2
,
-
2
,
2
},
{
"z
inter"
,
zinter
Command
,
-
4
,
REDIS_CMD_INLINE
|
REDIS_CMD_DENYOOM
,
0
,
0
,
0
},
{
"zrange"
,
zrangeCommand
,
-
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zrange"
,
zrangeCommand
,
-
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zrangebyscore"
,
zrangebyscoreCommand
,
-
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zrangebyscore"
,
zrangebyscoreCommand
,
-
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zcount"
,
zcountCommand
,
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
{
"zcount"
,
zcountCommand
,
4
,
REDIS_CMD_INLINE
,
1
,
1
,
1
},
...
@@ -4771,6 +4771,7 @@ static void sinterstoreCommand(redisClient *c) {
...
@@ -4771,6 +4771,7 @@ static void sinterstoreCommand(redisClient *c) {
#define REDIS_OP_UNION 0
#define REDIS_OP_UNION 0
#define REDIS_OP_DIFF 1
#define REDIS_OP_DIFF 1
#define REDIS_OP_INTER 2
static
void
sunionDiffGenericCommand
(
redisClient
*
c
,
robj
**
setskeys
,
int
setsnum
,
robj
*
dstkey
,
int
op
)
{
static
void
sunionDiffGenericCommand
(
redisClient
*
c
,
robj
**
setskeys
,
int
setsnum
,
robj
*
dstkey
,
int
op
)
{
dict
**
dv
=
zmalloc
(
sizeof
(
dict
*
)
*
setsnum
);
dict
**
dv
=
zmalloc
(
sizeof
(
dict
*
)
*
setsnum
);
...
@@ -5329,103 +5330,166 @@ static void zremrangebyscoreCommand(redisClient *c) {
...
@@ -5329,103 +5330,166 @@ static void zremrangebyscoreCommand(redisClient *c) {
}
}
}
}
/* This command merges 2 or more zsets to a destination. When an element
static
void
zunionInterGenericCommand
(
redisClient
*
c
,
robj
*
dstkey
,
int
op
)
{
* does not exist in a certain set, score 0 is assumed. The score for an
int
i
,
j
,
k
,
zsetnum
;
* element across sets is summed. */
dict
**
srcdicts
;
static
void
zmergeGenericCommand
(
redisClient
*
c
,
int
readweights
)
{
int
i
,
j
,
zsetnum
;
dict
**
srcdict
;
double
*
weights
;
double
*
weights
;
robj
*
dstkey
=
c
->
argv
[
1
],
*
dstobj
;
robj
*
dstobj
;
zset
*
dst
;
zset
*
dst
zset
;
dictIterator
*
di
;
dictIterator
*
di
;
dictEntry
*
de
;
dictEntry
*
de
;
zsetnum
=
c
->
argc
-
2
;
/* expect zsetnum input keys to be given */
if
(
readweights
)
{
zsetnum
=
atoi
(
c
->
argv
[
2
]
->
ptr
);
/* force number of arguments to be even */
if
(
zsetnum
<
1
)
{
if
(
zsetnum
%
2
>
0
)
{
addReplySds
(
c
,
sdsnew
(
"-ERR at least 1 input key is needed for ZUNION/ZINTER
\r\n
"
));
addReplySds
(
c
,
sdsnew
(
"-ERR wrong number of arguments for ZMERGEWEIGHED
\r\n
"
));
return
;
return
;
}
zsetnum
/=
2
;
}
}
if
(
!
zsetnum
)
{
/* test if the expected number of keys would overflow */
if
(
3
+
zsetnum
>
c
->
argc
)
{
addReply
(
c
,
shared
.
syntaxerr
);
addReply
(
c
,
shared
.
syntaxerr
);
return
;
return
;
}
}
srcdict
=
zmalloc
(
sizeof
(
dict
*
)
*
zsetnum
);
/* read keys to be used for input */
srcdicts
=
zmalloc
(
sizeof
(
dict
*
)
*
zsetnum
);
weights
=
zmalloc
(
sizeof
(
double
)
*
zsetnum
);
weights
=
zmalloc
(
sizeof
(
double
)
*
zsetnum
);
for
(
i
=
0
;
i
<
zsetnum
;
i
++
)
{
for
(
i
=
0
,
j
=
3
;
i
<
zsetnum
;
i
++
,
j
++
)
{
if
(
readweights
)
{
j
=
2
+
2
*
i
;
weights
[
i
]
=
strtod
(
c
->
argv
[
j
+
1
]
->
ptr
,
NULL
);
}
else
{
j
=
2
+
i
;
weights
[
i
]
=
1
.
0
;
}
robj
*
zsetobj
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
j
]);
robj
*
zsetobj
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
j
]);
if
(
!
zsetobj
)
{
if
(
!
zsetobj
)
{
srcdict
[
i
]
=
NULL
;
srcdict
s
[
i
]
=
NULL
;
}
else
{
}
else
{
if
(
zsetobj
->
type
!=
REDIS_ZSET
)
{
if
(
zsetobj
->
type
!=
REDIS_ZSET
)
{
zfree
(
srcdict
);
zfree
(
srcdict
s
);
zfree
(
weights
);
zfree
(
weights
);
addReply
(
c
,
shared
.
wrongtypeerr
);
addReply
(
c
,
shared
.
wrongtypeerr
);
return
;
return
;
}
}
srcdict
[
i
]
=
((
zset
*
)
zsetobj
->
ptr
)
->
dict
;
srcdict
s
[
i
]
=
((
zset
*
)
zsetobj
->
ptr
)
->
dict
;
}
}
/* default all weights to 1 */
weights
[
i
]
=
1
.
0
;
}
}
dstobj
=
createZsetObject
();
/* parse optional extra arguments */
dst
=
dstobj
->
ptr
;
if
(
j
<
c
->
argc
)
{
for
(
i
=
0
;
i
<
zsetnum
;
i
++
)
{
int
remaining
=
c
->
argc
-
j
;
if
(
!
srcdict
[
i
])
continue
;
di
=
dictGetIterator
(
srcdict
[
i
]);
while
(
remaining
)
{
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
if
(
!
strcasecmp
(
c
->
argv
[
j
]
->
ptr
,
"weights"
))
{
/* skip key when already processed */
j
++
;
remaining
--
;
if
(
dictFind
(
dst
->
dict
,
dictGetEntryKey
(
de
))
!=
NULL
)
continue
;
if
(
remaining
<
zsetnum
)
{
zfree
(
srcdicts
);
zfree
(
weights
);
addReplySds
(
c
,
sdsnew
(
"-ERR not enough weights for ZUNION/ZINTER
\r\n
"
));
return
;
}
for
(
i
=
0
;
i
<
zsetnum
;
i
++
,
j
++
,
remaining
--
)
{
weights
[
i
]
=
strtod
(
c
->
argv
[
j
]
->
ptr
,
NULL
);
}
}
else
{
zfree
(
srcdicts
);
zfree
(
weights
);
addReply
(
c
,
shared
.
syntaxerr
);
return
;
}
}
}
double
*
score
=
zmalloc
(
sizeof
(
double
));
dstobj
=
createZsetObject
();
*
score
=
0
.
0
;
dstzset
=
dstobj
->
ptr
;
for
(
j
=
0
;
j
<
zsetnum
;
j
++
)
{
if
(
!
srcdict
[
j
])
continue
;
if
(
op
==
REDIS_OP_INTER
)
{
/* store index of smallest zset in variable j */
for
(
i
=
0
,
j
=
0
;
i
<
zsetnum
;
i
++
)
{
if
(
!
srcdicts
[
i
]
||
dictSize
(
srcdicts
[
i
])
==
0
)
{
break
;
}
if
(
dictSize
(
srcdicts
[
i
])
<
dictSize
(
srcdicts
[
j
]))
{
j
=
i
;
}
}
/* skip going over all entries if at least one dict was NULL or empty */
if
(
i
==
zsetnum
)
{
/* precondition: all srcdicts are non-NULL and non-empty */
di
=
dictGetIterator
(
srcdicts
[
j
]);
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
double
*
score
=
zmalloc
(
sizeof
(
double
));
*
score
=
0
.
0
;
for
(
k
=
0
;
k
<
zsetnum
;
k
++
)
{
dictEntry
*
other
=
(
k
==
j
)
?
de
:
dictFind
(
srcdicts
[
k
],
dictGetEntryKey
(
de
));
if
(
other
)
{
*
score
=
*
score
+
weights
[
k
]
*
(
*
(
double
*
)
dictGetEntryVal
(
other
));
}
else
{
break
;
}
}
dictEntry
*
other
=
dictFind
(
srcdict
[
j
],
dictGetEntryKey
(
de
));
/* skip entry when not present in every source dict */
if
(
other
)
{
if
(
k
!=
zsetnum
)
{
*
score
=
*
score
+
weights
[
j
]
*
(
*
(
double
*
)
dictGetEntryVal
(
other
));
zfree
(
score
);
}
else
{
robj
*
o
=
dictGetEntryKey
(
de
);
dictAdd
(
dstzset
->
dict
,
o
,
score
);
incrRefCount
(
o
);
/* added to dictionary */
zslInsert
(
dstzset
->
zsl
,
*
score
,
o
);
incrRefCount
(
o
);
/* added to skiplist */
}
}
}
}
dictReleaseIterator
(
di
);
}
}
else
if
(
op
==
REDIS_OP_UNION
)
{
for
(
i
=
0
;
i
<
zsetnum
;
i
++
)
{
if
(
!
srcdicts
[
i
])
continue
;
di
=
dictGetIterator
(
srcdicts
[
i
]);
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
/* skip key when already processed */
if
(
dictFind
(
dstzset
->
dict
,
dictGetEntryKey
(
de
))
!=
NULL
)
continue
;
double
*
score
=
zmalloc
(
sizeof
(
double
));
*
score
=
0
.
0
;
for
(
j
=
0
;
j
<
zsetnum
;
j
++
)
{
if
(
!
srcdicts
[
j
])
continue
;
dictEntry
*
other
=
(
i
==
j
)
?
de
:
dictFind
(
srcdicts
[
j
],
dictGetEntryKey
(
de
));
if
(
other
)
{
*
score
=
*
score
+
weights
[
j
]
*
(
*
(
double
*
)
dictGetEntryVal
(
other
));
}
}
robj
*
o
=
dictGetEntryKey
(
de
);
robj
*
o
=
dictGetEntryKey
(
de
);
dictAdd
(
dst
->
dict
,
o
,
score
);
dictAdd
(
dstzset
->
dict
,
o
,
score
);
incrRefCount
(
o
);
/* added to dictionary */
incrRefCount
(
o
);
/* added to dictionary */
zslInsert
(
dst
->
zsl
,
*
score
,
o
);
zslInsert
(
dstzset
->
zsl
,
*
score
,
o
);
incrRefCount
(
o
);
/* added to skiplist */
incrRefCount
(
o
);
/* added to skiplist */
}
dictReleaseIterator
(
di
);
}
}
dictReleaseIterator
(
di
);
}
else
{
/* unknown operator */
redisAssert
(
op
==
REDIS_OP_INTER
||
op
==
REDIS_OP_UNION
);
}
}
deleteKey
(
c
->
db
,
dstkey
);
deleteKey
(
c
->
db
,
dstkey
);
dictAdd
(
c
->
db
->
dict
,
dstkey
,
dstobj
);
dictAdd
(
c
->
db
->
dict
,
dstkey
,
dstobj
);
incrRefCount
(
dstkey
);
incrRefCount
(
dstkey
);
addReplyLong
(
c
,
dst
->
zsl
->
length
);
addReplyLong
(
c
,
dst
zset
->
zsl
->
length
);
server
.
dirty
++
;
server
.
dirty
++
;
zfree
(
srcdict
);
zfree
(
srcdict
s
);
zfree
(
weights
);
zfree
(
weights
);
}
}
static
void
z
merge
Command
(
redisClient
*
c
)
{
static
void
z
union
Command
(
redisClient
*
c
)
{
z
merge
GenericCommand
(
c
,
0
);
z
unionInter
GenericCommand
(
c
,
c
->
argv
[
1
],
REDIS_OP_UNION
);
}
}
static
void
z
mergeweighed
Command
(
redisClient
*
c
)
{
static
void
z
inter
Command
(
redisClient
*
c
)
{
z
merge
GenericCommand
(
c
,
1
);
z
unionInter
GenericCommand
(
c
,
c
->
argv
[
1
],
REDIS_OP_INTER
);
}
}
static
void
zrangeGenericCommand
(
redisClient
*
c
,
int
reverse
)
{
static
void
zrangeGenericCommand
(
redisClient
*
c
,
int
reverse
)
{
...
...
test-redis.tcl
View file @
2830ca53
...
@@ -1462,7 +1462,7 @@ proc main {server port} {
...
@@ -1462,7 +1462,7 @@ proc main {server port} {
list
[
$r
zremrangebyscore zset -inf +inf
]
[
$r
zrange zset 0 -1
]
list
[
$r
zremrangebyscore zset -inf +inf
]
[
$r
zrange zset 0 -1
]
}
{
5
{}}
}
{
5
{}}
test
{
Z
MERGE
basics
}
{
test
{
Z
UNION
basics
}
{
$r del zseta zsetb zsetc
$r del zseta zsetb zsetc
$r zadd zseta 1 a
$r zadd zseta 1 a
$r zadd zseta 2 b
$r zadd zseta 2 b
...
@@ -1470,13 +1470,21 @@ proc main {server port} {
...
@@ -1470,13 +1470,21 @@ proc main {server port} {
$r zadd zsetb 1 b
$r zadd zsetb 1 b
$r zadd zsetb 2 c
$r zadd zsetb 2 c
$r zadd zsetb 3 d
$r zadd zsetb 3 d
list
[
$r
z
merge
zsetc zseta zsetb
]
[
$r
zrange zsetc 0 -1 withscores
]
list
[
$r
z
union
zsetc
2
zseta zsetb
]
[
$r
zrange zsetc 0 -1 withscores
]
}
{
4
{
a 1 b 3 d 3 c 5
}}
}
{
4
{
a 1 b 3 d 3 c 5
}}
test
{
Z
MERGEWEIGHED basic
s
}
{
test
{
Z
UNION with weight
s
}
{
list
[
$r
z
mergeweighed
zsetc zseta
2
zsetb 3
]
[
$r
zrange zsetc 0 -1 withscores
]
list
[
$r
z
union
zsetc
2
zseta zsetb
weights 2
3
]
[
$r
zrange zsetc 0 -1 withscores
]
}
{
4
{
a 2 b 7 d 9 c 12
}}
}
{
4
{
a 2 b 7 d 9 c 12
}}
test
{
ZINTER basics
}
{
list
[
$r
zinter zsetc 2 zseta zsetb
]
[
$r
zrange zsetc 0 -1 withscores
]
}
{
2
{
b 3 c 5
}}
test
{
ZINTER with weights
}
{
list
[
$r
zinter zsetc 2 zseta zsetb weights 2 3
]
[
$r
zrange zsetc 0 -1 withscores
]
}
{
2
{
b 7 c 12
}}
test
{
SORT against sorted sets
}
{
test
{
SORT against sorted sets
}
{
$r del zset
$r del zset
$r zadd zset 1 a
$r zadd zset 1 a
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment