DATAREDIS-525 - Add support for ReactiveRedisConnection & ReactiveRedisClusterConnection.
We introduced reactor based reactive connection support via ReactiveRedisConnection and ReactiveRedisClusterConnection. A reactive connection can be obtained via the ConnectionFactory. This is currently only possible when using the Lettuce Redis driver. Original pull request: #229.
This commit is contained in:
committed by
Mark Paluch
parent
f6411b3811
commit
d257ae1074
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -52,6 +56,33 @@ public final class ClusterSlotHashUtil {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
public static boolean isSameSlotForAllKeys(Collection<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
|
||||
if (keys.size() <= 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isSameSlotForAllKeys((byte[][]) keys.stream().map(ByteBuffer::array).toArray(size -> new byte[size][]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
public static boolean isSameSlotForAllKeys(ByteBuffer... keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
return isSameSlotForAllKeys(Arrays.asList(keys));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterGeoCommands extends ReactiveGeoCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterHashCommands extends ReactiveHashCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterHyperLogLogCommands extends ReactiveHyperLogLogCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterKeyCommands extends ReactiveKeyCommands {
|
||||
|
||||
/**
|
||||
* Retrieve all {@literal keys} for a given {@literal pattern} from {@link RedisNode}.
|
||||
*
|
||||
* @param node must not be {@literal null}.
|
||||
* @param pattern must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<List<ByteBuffer>> keys(RedisClusterNode node, ByteBuffer pattern);
|
||||
|
||||
/**
|
||||
* Retrieve a random {@literal key} from {@link RedisNode}.
|
||||
*
|
||||
* @param node must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Mono<ByteBuffer> randomKey(RedisClusterNode node);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterListCommands extends ReactiveListCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterNumberCommands extends ReactiveNumberCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterSetCommands extends ReactiveSetCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterStringCommands extends ReactiveStringCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveClusterZSetCommands extends ReactiveZSetCommands {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.Flag;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveGeoCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoAddCommand extends KeyCommand {
|
||||
|
||||
private final List<GeoLocation<ByteBuffer>> geoLocations;
|
||||
|
||||
public GeoAddCommand(ByteBuffer key, List<GeoLocation<ByteBuffer>> geoLocations) {
|
||||
|
||||
super(key);
|
||||
this.geoLocations = geoLocations;
|
||||
}
|
||||
|
||||
public static GeoAddCommand location(GeoLocation<ByteBuffer> geoLocation) {
|
||||
return new GeoAddCommand(null, Collections.singletonList(geoLocation));
|
||||
}
|
||||
|
||||
public static GeoAddCommand locations(List<GeoLocation<ByteBuffer>> geoLocations) {
|
||||
return new GeoAddCommand(null, new ArrayList<>(geoLocations));
|
||||
}
|
||||
|
||||
public GeoAddCommand to(ByteBuffer key) {
|
||||
return new GeoAddCommand(key, geoLocations);
|
||||
}
|
||||
|
||||
public List<GeoLocation<ByteBuffer>> getGeoLocations() {
|
||||
return geoLocations;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link Point} with given {@literal member} to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param point must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> geoAdd(ByteBuffer key, Point point, ByteBuffer member) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(point, "point must not be null");
|
||||
Assert.notNull(member, "member must not be null");
|
||||
|
||||
return geoAdd(key, new GeoLocation<>(member, point));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation} to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param location must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> geoAdd(ByteBuffer key, GeoLocation<ByteBuffer> location) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(location, "location must not be null");
|
||||
|
||||
return geoAdd(key, Collections.singletonList(location));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation} to {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param locations must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> geoAdd(ByteBuffer key, List<GeoLocation<ByteBuffer>> locations) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(locations, "locations must not be null");
|
||||
|
||||
return geoAdd(Mono.just(GeoAddCommand.locations(locations).to(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link GeoLocation}s to {@literal key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<GeoAddCommand, Long>> geoAdd(Publisher<GeoAddCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoDistCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer from;
|
||||
private final ByteBuffer to;
|
||||
private final Metric metric;
|
||||
|
||||
private GeoDistCommand(ByteBuffer key, ByteBuffer from, ByteBuffer to, Metric metric) {
|
||||
super(key);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
static GeoDistCommand units(Metric unit) {
|
||||
return new GeoDistCommand(null, null, null, unit);
|
||||
}
|
||||
|
||||
public static GeoDistCommand meters() {
|
||||
return units(DistanceUnit.METERS);
|
||||
}
|
||||
|
||||
public static GeoDistCommand kiometers() {
|
||||
return units(DistanceUnit.KILOMETERS);
|
||||
}
|
||||
|
||||
public static GeoDistCommand miles() {
|
||||
return units(DistanceUnit.MILES);
|
||||
}
|
||||
|
||||
public static GeoDistCommand feet() {
|
||||
return units(DistanceUnit.FEET);
|
||||
}
|
||||
|
||||
public GeoDistCommand between(ByteBuffer from) {
|
||||
return new GeoDistCommand(getKey(), from, to, metric);
|
||||
}
|
||||
|
||||
public GeoDistCommand and(ByteBuffer to) {
|
||||
return new GeoDistCommand(getKey(), from, to, metric);
|
||||
}
|
||||
|
||||
public GeoDistCommand forKey(ByteBuffer key) {
|
||||
return new GeoDistCommand(key, from, to, metric);
|
||||
}
|
||||
|
||||
public ByteBuffer getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public ByteBuffer getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public Optional<Metric> getMetric() {
|
||||
return Optional.ofNullable(metric);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Distance} between {@literal from} and {@literal to}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Distance> geoDist(ByteBuffer key, ByteBuffer from, ByteBuffer to) {
|
||||
return geoDist(key, from, to, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Distance} between {@literal from} and {@literal to}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @param metric can be {@literal null} and defaults to {@link DistanceUnit#METERS}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Distance> geoDist(ByteBuffer key, ByteBuffer from, ByteBuffer to, Metric metric) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(from, "from must not be null");
|
||||
Assert.notNull(to, "to must not be null");
|
||||
|
||||
return geoDist(Mono.just(GeoDistCommand.units(metric).between(from).and(to).forKey(key))).next()
|
||||
.map(CommandResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Distance} between {@literal from} and {@literal to}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<CommandResponse<GeoDistCommand, Distance>> geoDist(Publisher<GeoDistCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoHashCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> members;
|
||||
|
||||
private GeoHashCommand(ByteBuffer key, List<ByteBuffer> members) {
|
||||
|
||||
super(key);
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public static GeoHashCommand member(ByteBuffer member) {
|
||||
return new GeoHashCommand(null, Collections.singletonList(member));
|
||||
}
|
||||
|
||||
public static GeoHashCommand members(List<ByteBuffer> members) {
|
||||
return new GeoHashCommand(null, new ArrayList<>(members));
|
||||
}
|
||||
|
||||
public GeoHashCommand of(ByteBuffer key) {
|
||||
return new GeoHashCommand(key, members);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getMembers() {
|
||||
return members;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geohash representation of the position for the one {@literal member}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<String> geoHash(ByteBuffer key, ByteBuffer member) {
|
||||
|
||||
Assert.notNull(member, "member must not be null");
|
||||
|
||||
return geoHash(key, Collections.singletonList(member)).map(vals -> vals.isEmpty() ? null : vals.iterator().next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geohash representation of the position for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<String>> geoHash(ByteBuffer key, List<ByteBuffer> members) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(members, "members must not be null");
|
||||
|
||||
return geoHash(Mono.just(GeoHashCommand.members(members).of(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get geohash representation of the position for one or more {@literal member}s.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<GeoHashCommand, String>> geoHash(Publisher<GeoHashCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoPosCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> members;
|
||||
|
||||
private GeoPosCommand(ByteBuffer key, List<ByteBuffer> members) {
|
||||
|
||||
super(key);
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public static GeoPosCommand member(ByteBuffer member) {
|
||||
return new GeoPosCommand(null, Collections.singletonList(member));
|
||||
}
|
||||
|
||||
public static GeoPosCommand members(List<ByteBuffer> members) {
|
||||
return new GeoPosCommand(null, new ArrayList<>(members));
|
||||
}
|
||||
|
||||
public GeoPosCommand of(ByteBuffer key) {
|
||||
return new GeoPosCommand(key, members);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getMembers() {
|
||||
return members;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Point} representation of positions for the {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Point> geoPos(ByteBuffer key, ByteBuffer member) {
|
||||
|
||||
Assert.notNull(member, "member must not be null");
|
||||
|
||||
return geoPos(key, Collections.singletonList(member)).map(vals -> vals.isEmpty() ? null : vals.iterator().next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Point} representation of positions for one or more {@literal member}s.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param members must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<Point>> geoPos(ByteBuffer key, List<ByteBuffer> members) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(members, "members must not be null");
|
||||
|
||||
return geoPos(Mono.just(GeoPosCommand.members(members).of(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Point} representation of positions for one or more {@literal member}s.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<GeoPosCommand, Point>> geoPos(Publisher<GeoPosCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoRadiusCommand extends KeyCommand {
|
||||
|
||||
private final Distance distance;
|
||||
private final Point point;
|
||||
private final GeoRadiusCommandArgs args;
|
||||
private final ByteBuffer store;
|
||||
private final ByteBuffer storeDist;
|
||||
|
||||
private GeoRadiusCommand(ByteBuffer key, Point point, Distance distance, GeoRadiusCommandArgs args,
|
||||
ByteBuffer store, ByteBuffer storeDist) {
|
||||
super(key);
|
||||
this.distance = distance;
|
||||
this.point = point;
|
||||
this.args = args == null ? GeoRadiusCommandArgs.newGeoRadiusArgs() : args;
|
||||
this.store = store;
|
||||
this.storeDist = storeDist;
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand within(Distance distance) {
|
||||
return new GeoRadiusCommand(null, null, distance, null, null, null);
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand withinMeters(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.METERS));
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand withinKiometers(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.KILOMETERS));
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand withinMiles(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.MILES));
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand withinFeet(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.FEET));
|
||||
}
|
||||
|
||||
public static GeoRadiusCommand within(Circle circle) {
|
||||
return within(circle.getRadius()).from(circle.getCenter());
|
||||
}
|
||||
|
||||
public GeoRadiusCommand from(Point center) {
|
||||
return new GeoRadiusCommand(getKey(), center, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand withFlag(Flag flag) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
args.flags.add(flag);
|
||||
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand withCoord() {
|
||||
return withFlag(Flag.WITHCOORD);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand withDist() {
|
||||
return withFlag(Flag.WITHDIST);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand withArgs(GeoRadiusCommandArgs args) {
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand limitTo(Long limit) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
if (limit != null) {
|
||||
args = args.limit(limit);
|
||||
}
|
||||
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand sort(Direction direction) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
args.sortDirection = direction;
|
||||
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand orderByDistanceAsc() {
|
||||
return sort(Direction.ASC);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand orderByDistanceDesc() {
|
||||
return sort(Direction.DESC);
|
||||
}
|
||||
|
||||
public GeoRadiusCommand forKey(ByteBuffer key) {
|
||||
return new GeoRadiusCommand(key, point, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>NOTE:</b> STORE option is not compatible with WITHDIST, WITHHASH and WITHCOORDS options.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public GeoRadiusCommand storeAt(ByteBuffer key) {
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, key, storeDist);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>NOTE:</b> STOREDIST option is not compatible with WITHDIST, WITHHASH and WITHCOORDS options.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public GeoRadiusCommand storeDistAt(ByteBuffer key) {
|
||||
return new GeoRadiusCommand(getKey(), point, distance, args, store, key);
|
||||
}
|
||||
|
||||
public Optional<Direction> getDirection() {
|
||||
return Optional.ofNullable(args.getSortDirection());
|
||||
}
|
||||
|
||||
public Distance getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public Set<Flag> getFlags() {
|
||||
return args.getFlags();
|
||||
}
|
||||
|
||||
public Optional<Long> getLimit() {
|
||||
return Optional.ofNullable(args.getLimit());
|
||||
}
|
||||
|
||||
public Point getPoint() {
|
||||
return point;
|
||||
}
|
||||
|
||||
public Optional<ByteBuffer> getStore() {
|
||||
return Optional.ofNullable(store);
|
||||
}
|
||||
|
||||
public Optional<ByteBuffer> getStoreDist() {
|
||||
return Optional.ofNullable(storeDist);
|
||||
}
|
||||
|
||||
public Optional<GeoRadiusCommandArgs> getArgs() {
|
||||
return Optional.ofNullable(args);
|
||||
}
|
||||
|
||||
private GeoRadiusCommandArgs cloneArgs() {
|
||||
|
||||
if (args == null) {
|
||||
return GeoRadiusCommandArgs.newGeoRadiusArgs();
|
||||
}
|
||||
|
||||
return args.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the boundaries of a given {@link Circle}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param circle must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<GeoLocation<ByteBuffer>>> geoRadius(ByteBuffer key, Circle circle) {
|
||||
return geoRadius(key, circle, null)
|
||||
.map(res -> res.getContent().stream().map(val -> val.getContent()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the boundaries of a given {@link Circle} applying given parameters.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param circle must not be {@literal null}.
|
||||
* @param geoRadiusArgs can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<GeoResults<GeoLocation<ByteBuffer>>> geoRadius(ByteBuffer key, Circle circle,
|
||||
GeoRadiusCommandArgs geoRadiusArgs) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(circle, "circle must not be null");
|
||||
|
||||
return geoRadius(Mono.just(GeoRadiusCommand.within(circle).withArgs(geoRadiusArgs).forKey(key))).next()
|
||||
.map(CommandResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within the boundaries of a given {@link Circle} applying given parameters.
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<CommandResponse<GeoRadiusCommand, GeoResults<GeoLocation<ByteBuffer>>>> geoRadius(
|
||||
Publisher<GeoRadiusCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class GeoRadiusByMemberCommand extends KeyCommand {
|
||||
|
||||
private final Distance distance;
|
||||
private final ByteBuffer member;
|
||||
private final GeoRadiusCommandArgs args;
|
||||
private final ByteBuffer store;
|
||||
private final ByteBuffer storeDist;
|
||||
|
||||
private GeoRadiusByMemberCommand(ByteBuffer key, ByteBuffer member, Distance distance, GeoRadiusCommandArgs args,
|
||||
ByteBuffer store, ByteBuffer storeDist) {
|
||||
|
||||
super(key);
|
||||
this.distance = distance;
|
||||
this.member = member;
|
||||
this.args = args == null ? GeoRadiusCommandArgs.newGeoRadiusArgs() : args;
|
||||
this.store = store;
|
||||
this.storeDist = storeDist;
|
||||
}
|
||||
|
||||
public static GeoRadiusByMemberCommand within(Distance distance) {
|
||||
return new GeoRadiusByMemberCommand(null, null, distance, GeoRadiusCommandArgs.newGeoRadiusArgs(), null, null);
|
||||
}
|
||||
|
||||
public static GeoRadiusByMemberCommand withinMeters(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.METERS));
|
||||
}
|
||||
|
||||
public static GeoRadiusByMemberCommand withinKiometers(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.KILOMETERS));
|
||||
}
|
||||
|
||||
public static GeoRadiusByMemberCommand withinMiles(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.MILES));
|
||||
}
|
||||
|
||||
public static GeoRadiusByMemberCommand withinFeet(Double distance) {
|
||||
return within(new Distance(distance, DistanceUnit.FEET));
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand from(ByteBuffer member) {
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand withArgs(GeoRadiusCommandArgs args) {
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand withFlag(Flag flag) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
args.flags.add(flag);
|
||||
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand withCoord() {
|
||||
return withFlag(Flag.WITHCOORD);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand withDist() {
|
||||
return withFlag(Flag.WITHDIST);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand limitTo(Long limit) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
if (limit != null) {
|
||||
args = args.limit(limit);
|
||||
}
|
||||
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand sort(Direction direction) {
|
||||
|
||||
GeoRadiusCommandArgs args = cloneArgs();
|
||||
args.sortDirection = direction;
|
||||
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand orderByDistanceAsc() {
|
||||
return sort(Direction.ASC);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand orderByDistanceDesc() {
|
||||
return sort(Direction.DESC);
|
||||
}
|
||||
|
||||
public GeoRadiusByMemberCommand forKey(ByteBuffer key) {
|
||||
return new GeoRadiusByMemberCommand(key, member, distance, args, store, storeDist);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>NOTE:</b> STORE option is not compatible with WITHDIST, WITHHASH and WITHCOORDS options.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public GeoRadiusByMemberCommand storeAt(ByteBuffer key) {
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, key, storeDist);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>NOTE:</b> STOREDIST option is not compatible with WITHDIST, WITHHASH and WITHCOORDS options.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public GeoRadiusByMemberCommand storeDistAt(ByteBuffer key) {
|
||||
return new GeoRadiusByMemberCommand(getKey(), member, distance, args, store, key);
|
||||
}
|
||||
|
||||
public Optional<Direction> getDirection() {
|
||||
return Optional.ofNullable(args.getSortDirection());
|
||||
}
|
||||
|
||||
public Distance getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public Set<Flag> getFlags() {
|
||||
return args.getFlags();
|
||||
}
|
||||
|
||||
public Optional<Long> getLimit() {
|
||||
return Optional.ofNullable(args.getLimit());
|
||||
}
|
||||
|
||||
public ByteBuffer getMember() {
|
||||
return member;
|
||||
}
|
||||
|
||||
public Optional<ByteBuffer> getStore() {
|
||||
return Optional.ofNullable(store);
|
||||
}
|
||||
|
||||
public Optional<ByteBuffer> getStoreDist() {
|
||||
return Optional.ofNullable(storeDist);
|
||||
}
|
||||
|
||||
public Optional<GeoRadiusCommandArgs> getArgs() {
|
||||
return Optional.ofNullable(args);
|
||||
}
|
||||
|
||||
private GeoRadiusCommandArgs cloneArgs() {
|
||||
|
||||
if (args == null) {
|
||||
return GeoRadiusCommandArgs.newGeoRadiusArgs();
|
||||
}
|
||||
|
||||
return args.clone();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within given {@link Distance} from {@literal member} applying given parameters.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<GeoLocation<ByteBuffer>>> geoRadiusByMember(ByteBuffer key, ByteBuffer member, Distance distance) {
|
||||
return geoRadiusByMember(key, member, distance, null)
|
||||
.map(res -> res.getContent().stream().map(val -> val.getContent()).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within given {@link Distance} from {@literal member} applying given parameters.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param member must not be {@literal null}.
|
||||
* @param geoRadiusArgs can be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<GeoResults<GeoLocation<ByteBuffer>>> geoRadiusByMember(ByteBuffer key, ByteBuffer member,
|
||||
Distance distance, GeoRadiusCommandArgs geoRadiusArgs) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(member, "member must not be null");
|
||||
Assert.notNull(distance, "distance must not be null");
|
||||
|
||||
return geoRadiusByMember(
|
||||
Mono.just(GeoRadiusByMemberCommand.within(distance).from(member).forKey(key).withArgs(geoRadiusArgs))).next()
|
||||
.map(CommandResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@literal member}s within given {@link Distance} from {@literal member} applying given parameters.
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<CommandResponse<GeoRadiusByMemberCommand, GeoResults<GeoLocation<ByteBuffer>>>> geoRadiusByMember(
|
||||
Publisher<GeoRadiusByMemberCommand> commands);
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveHashCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class HSetCommand extends KeyCommand {
|
||||
|
||||
private static final ByteBuffer SINGLE_VALUE_KEY = ByteBuffer.allocate(0);
|
||||
private final Map<ByteBuffer, ByteBuffer> fieldValueMap;
|
||||
private final Boolean upsert;
|
||||
|
||||
private HSetCommand(ByteBuffer key, Map<ByteBuffer, ByteBuffer> keyValueMap, Boolean upsert) {
|
||||
|
||||
super(key);
|
||||
this.fieldValueMap = keyValueMap;
|
||||
this.upsert = upsert;
|
||||
}
|
||||
|
||||
public static HSetCommand value(ByteBuffer value) {
|
||||
return new HSetCommand(null, Collections.singletonMap(SINGLE_VALUE_KEY, value), Boolean.TRUE);
|
||||
}
|
||||
|
||||
public static HSetCommand fieldValues(Map<ByteBuffer, ByteBuffer> fieldValueMap) {
|
||||
return new HSetCommand(null, fieldValueMap, Boolean.TRUE);
|
||||
}
|
||||
|
||||
public HSetCommand ofField(ByteBuffer field) {
|
||||
|
||||
if (!fieldValueMap.containsKey(SINGLE_VALUE_KEY)) {
|
||||
throw new InvalidDataAccessApiUsageException("Value has not been set.");
|
||||
}
|
||||
|
||||
return new HSetCommand(getKey(), Collections.singletonMap(field, fieldValueMap.get(SINGLE_VALUE_KEY)), upsert);
|
||||
}
|
||||
|
||||
public HSetCommand forKey(ByteBuffer key) {
|
||||
return new HSetCommand(key, fieldValueMap, upsert);
|
||||
}
|
||||
|
||||
public HSetCommand ifValueNotExists() {
|
||||
return new HSetCommand(getKey(), fieldValueMap, Boolean.FALSE);
|
||||
}
|
||||
|
||||
public Boolean isUpsert() {
|
||||
return upsert;
|
||||
}
|
||||
|
||||
public Map<ByteBuffer, ByteBuffer> getFieldValueMap() {
|
||||
return fieldValueMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code value} of a hash {@code field}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> hSet(ByteBuffer key, ByteBuffer field, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(field, "field must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return hSet(Mono.just(HSetCommand.value(value).ofField(field).forKey(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code value} of a hash {@code field}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> hSetNX(ByteBuffer key, ByteBuffer field, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(field, "field must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return hSet(Mono.just(HSetCommand.value(value).ofField(field).forKey(key).ifValueNotExists())).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple hash fields to multiple values using data provided in {@code fieldValueMap}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param fieldValueMap must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> hMSet(ByteBuffer key, Map<ByteBuffer, ByteBuffer> fieldValueMap) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(fieldValueMap, "field must not be null");
|
||||
|
||||
return hSet(Mono.just(HSetCommand.fieldValues(fieldValueMap).forKey(key).ifValueNotExists())).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code value} of a hash {@code field}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<HSetCommand>> hSet(Publisher<HSetCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class HGetCommand extends KeyCommand {
|
||||
|
||||
private List<ByteBuffer> fields;
|
||||
|
||||
private HGetCommand(ByteBuffer key, List<ByteBuffer> fields) {
|
||||
|
||||
super(key);
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public static HGetCommand field(ByteBuffer field) {
|
||||
return new HGetCommand(null, Collections.singletonList(field));
|
||||
}
|
||||
|
||||
public static HGetCommand fields(List<ByteBuffer> fields) {
|
||||
return new HGetCommand(null, new ArrayList<>(fields));
|
||||
}
|
||||
|
||||
public HGetCommand from(ByteBuffer key) {
|
||||
return new HGetCommand(key, fields);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getFields() {
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value for given {@code field} from hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> hGet(ByteBuffer key, ByteBuffer field) {
|
||||
return hMGet(key, Collections.singletonList(field)).map(val -> val.isEmpty() ? null : val.iterator().next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values for given {@code fields} from hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> hMGet(ByteBuffer key, List<ByteBuffer> fields) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(fields, "fields must not be null");
|
||||
|
||||
return hMGet(Mono.just(HGetCommand.fields(fields).from(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values for given {@code fields} from hash at {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<HGetCommand, ByteBuffer>> hMGet(Publisher<HGetCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class HExistsCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer field;
|
||||
|
||||
private HExistsCommand(ByteBuffer key, ByteBuffer field) {
|
||||
|
||||
super(key);
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public static HExistsCommand field(ByteBuffer field) {
|
||||
return new HExistsCommand(null, field);
|
||||
}
|
||||
|
||||
public HExistsCommand in(ByteBuffer key) {
|
||||
return new HExistsCommand(key, field);
|
||||
}
|
||||
|
||||
public ByteBuffer getField() {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if given hash {@code field} exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> hExists(ByteBuffer key, ByteBuffer field) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(field, "field must not be null");
|
||||
|
||||
return hExists(Mono.just(HExistsCommand.field(field).in(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if given hash {@code field} exists.
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<HExistsCommand>> hExists(Publisher<HExistsCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class HDelCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> fields;
|
||||
|
||||
private HDelCommand(ByteBuffer key, List<ByteBuffer> fields) {
|
||||
super(key);
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public static HDelCommand field(ByteBuffer field) {
|
||||
return new HDelCommand(null, Collections.singletonList(field));
|
||||
}
|
||||
|
||||
public static HDelCommand fields(List<ByteBuffer> fields) {
|
||||
return new HDelCommand(null, new ArrayList<>(fields));
|
||||
}
|
||||
|
||||
public HDelCommand from(ByteBuffer key) {
|
||||
return new HDelCommand(key, fields);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getFields() {
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete given hash {@code field}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> hDel(ByteBuffer key, ByteBuffer field) {
|
||||
|
||||
Assert.notNull(field, "field must not be null");
|
||||
|
||||
return hDel(key, Collections.singletonList(field)).map(val -> val > 0 ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete given hash {@code fields}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> hDel(ByteBuffer key, List<ByteBuffer> fields) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(fields, "fields must not be null");
|
||||
|
||||
return hDel(Mono.just(HDelCommand.fields(fields).from(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete given hash {@code fields}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<HDelCommand, Long>> hDel(Publisher<HDelCommand> commands);
|
||||
|
||||
/**
|
||||
* Get size of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> hLen(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return hLen(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of hash at {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> hLen(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get key set (fields) of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> hKeys(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return hKeys(Mono.just(new KeyCommand(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key set (fields) of hash at {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<KeyCommand, ByteBuffer>> hKeys(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get entry set (values) of hash at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> hVals(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return hVals(Mono.just(new KeyCommand(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entry set (values) of hash at {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<KeyCommand, ByteBuffer>> hVals(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get entire hash stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Map<ByteBuffer, ByteBuffer>> hGetAll(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return hGetAll(Mono.just(new KeyCommand(key))).next().map(CommandResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get entire hash stored at {@code key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<CommandResponse<KeyCommand, Map<ByteBuffer, ByteBuffer>>> hGetAll(Publisher<KeyCommand> commands);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.Command;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveHyperLogLogCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PfAddCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> values;
|
||||
|
||||
private PfAddCommand(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
super(key);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public static PfAddCommand value(ByteBuffer value) {
|
||||
return values(Collections.singletonList(value));
|
||||
}
|
||||
|
||||
public static PfAddCommand values(List<ByteBuffer> values) {
|
||||
return new PfAddCommand(null, new ArrayList<>(values));
|
||||
}
|
||||
|
||||
public PfAddCommand to(ByteBuffer key) {
|
||||
return new PfAddCommand(key, values);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds given {@literal value} to the HyperLogLog stored at given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> pfAdd(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return pfAdd(key, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds given {@literal values} to the HyperLogLog stored at given {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> pfAdd(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(values, "values must not be null");
|
||||
|
||||
return pfAdd(Mono.just(PfAddCommand.values(values).to(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds given {@literal values} to the HyperLogLog stored at given {@literal key}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<PfAddCommand, Long>> pfAdd(Publisher<PfAddCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PfCountCommand implements Command {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private PfCountCommand(List<ByteBuffer> keys) {
|
||||
|
||||
super();
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static PfCountCommand valueIn(ByteBuffer key) {
|
||||
return valuesIn(Collections.singletonList(key));
|
||||
}
|
||||
|
||||
public static PfCountCommand valuesIn(List<ByteBuffer> keys) {
|
||||
return new PfCountCommand(keys);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the approximated cardinality of the structures observed by the HyperLogLog at {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> pfCount(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return pfCount(Collections.singletonList(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the approximated cardinality of the structures observed by the HyperLogLog at {@literal key(s)}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> pfCount(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return pfCount(Mono.just(PfCountCommand.valuesIn(keys))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the approximated cardinality of the structures observed by the HyperLogLog at {@literal key(s)}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<PfCountCommand, Long>> pfCount(Publisher<PfCountCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PfMergeCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> sourceKeys;
|
||||
|
||||
private PfMergeCommand(ByteBuffer key, List<ByteBuffer> sourceKeys) {
|
||||
|
||||
super(key);
|
||||
this.sourceKeys = sourceKeys;
|
||||
}
|
||||
|
||||
public static PfMergeCommand valuesIn(List<ByteBuffer> sourceKeys) {
|
||||
return new PfMergeCommand(null, sourceKeys);
|
||||
}
|
||||
|
||||
public PfMergeCommand into(ByteBuffer destinationKey) {
|
||||
return new PfMergeCommand(destinationKey, sourceKeys);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getSourceKeys() {
|
||||
return sourceKeys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge N different HyperLogLogs at {@literal sourceKeys} into a single {@literal destinationKey}.
|
||||
*
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param sourceKeys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> pfMerge(ByteBuffer destinationKey, List<ByteBuffer> sourceKeys) {
|
||||
|
||||
Assert.notNull(destinationKey, "destinationKey must not be null");
|
||||
Assert.notNull(sourceKeys, "sourceKeys must not be null");
|
||||
|
||||
return pfMerge(Mono.just(PfMergeCommand.valuesIn(sourceKeys).into(destinationKey))).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge N different HyperLogLogs at {@literal sourceKeys} into a single {@literal destinationKey}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<PfMergeCommand>> pfMerge(Publisher<PfMergeCommand> commands);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveKeyCommands {
|
||||
|
||||
/**
|
||||
* Determine if given {@code key} exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> exists(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if given {@code key} exists.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<KeyCommand>> exists(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* Determine the type stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<DataType> type(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return type(Mono.just(new KeyCommand(key))).next().map(CommandResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type stored at {@code key}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<CommandResponse<KeyCommand, DataType>> type(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* Find all keys matching the given {@code pattern}.
|
||||
*
|
||||
* @param pattern must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> keys(ByteBuffer pattern) {
|
||||
|
||||
Assert.notNull(pattern, "pattern must not be null");
|
||||
|
||||
return keys(Mono.just(pattern)).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random key from the keyspace.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Mono<ByteBuffer> randomKey();
|
||||
|
||||
/**
|
||||
* Find all keys matching the given {@code pattern}.
|
||||
*
|
||||
* @param patterns must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<ByteBuffer, ByteBuffer>> keys(Publisher<ByteBuffer> patterns);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class RenameCommand extends KeyCommand {
|
||||
|
||||
private ByteBuffer newName;
|
||||
|
||||
private RenameCommand(ByteBuffer key, ByteBuffer newName) {
|
||||
|
||||
super(key);
|
||||
this.newName = newName;
|
||||
}
|
||||
|
||||
public static ReactiveKeyCommands.RenameCommand key(ByteBuffer key) {
|
||||
return new RenameCommand(key, null);
|
||||
}
|
||||
|
||||
public ReactiveKeyCommands.RenameCommand to(ByteBuffer newName) {
|
||||
return new RenameCommand(getKey(), newName);
|
||||
}
|
||||
|
||||
public ByteBuffer getNewName() {
|
||||
return newName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename key {@code oleName} to {@code newName}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param newName must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> rename(ByteBuffer key, ByteBuffer newName) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return rename(Mono.just(RenameCommand.key(key).to(newName))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
Flux<BooleanResponse<ReactiveKeyCommands.RenameCommand>> rename(Publisher<ReactiveKeyCommands.RenameCommand> cmd);
|
||||
|
||||
/**
|
||||
* Rename key {@code oleName} to {@code newName} only if {@code newName} does not exist.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param newName must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> renameNX(ByteBuffer key, ByteBuffer newName) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return renameNX(Mono.just(RenameCommand.key(key).to(newName))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename key {@code oleName} to {@code newName} only if {@code newName} does not exist.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param newName must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveKeyCommands.RenameCommand>> renameNX(
|
||||
Publisher<ReactiveKeyCommands.RenameCommand> command);
|
||||
|
||||
/**
|
||||
* Delete {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> del(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return del(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete {@literal keys} one by one.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link DelResponse} holding the {@literal key} removed along with the deletion result.
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* Delete multiple {@literal keys} one in one batch.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> mDel(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notEmpty(keys, "Keys must not be empty or null!");
|
||||
|
||||
return mDel(Mono.just(keys)).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete multiple {@literal keys} in batches.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link MDelResponse} holding the {@literal keys} removed along with the deletion result.
|
||||
*/
|
||||
Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keys);
|
||||
}
|
||||
@@ -0,0 +1,820 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.Command;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveListCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
enum Direction {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PushCommand extends KeyCommand {
|
||||
|
||||
private List<ByteBuffer> values;
|
||||
private boolean upsert;
|
||||
private Direction direction;
|
||||
|
||||
private PushCommand(ByteBuffer key, List<ByteBuffer> values, Direction direction, boolean upsert) {
|
||||
|
||||
super(key);
|
||||
this.values = values;
|
||||
this.upsert = upsert;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public static PushCommand right() {
|
||||
return new PushCommand(null, null, Direction.RIGHT, true);
|
||||
}
|
||||
|
||||
public static PushCommand left() {
|
||||
return new PushCommand(null, null, Direction.LEFT, true);
|
||||
}
|
||||
|
||||
public PushCommand value(ByteBuffer value) {
|
||||
return new PushCommand(null, Collections.singletonList(value), direction, upsert);
|
||||
}
|
||||
|
||||
public PushCommand values(List<ByteBuffer> values) {
|
||||
return new PushCommand(null, values, direction, upsert);
|
||||
}
|
||||
|
||||
public PushCommand to(ByteBuffer key) {
|
||||
return new PushCommand(key, values, direction, upsert);
|
||||
}
|
||||
|
||||
public PushCommand ifExists() {
|
||||
return new PushCommand(getKey(), values, direction, false);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public boolean getUpsert() {
|
||||
return upsert;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> rPush(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "command must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
return push(Mono.just(PushCommand.right().values(values).to(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append {@code values} to {@code key} only if {@code key} already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> rPushX(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "command must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return push(Mono.just(PushCommand.right().value(value).to(key).ifExists())).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend {@code values} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lPush(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "command must not be null!");
|
||||
Assert.notNull(values, "Values must not be null!");
|
||||
|
||||
return push(Mono.just(PushCommand.left().values(values).to(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend {@code value} to {@code key} if {@code key} already exists.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lPushX(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "command must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return push(Mono.just(PushCommand.left().value(value).to(key).ifExists())).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend {@link PushCommand#getValues()} to {@link PushCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<PushCommand, Long>> push(Publisher<PushCommand> commands);
|
||||
|
||||
/**
|
||||
* Get the size of list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lLen(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return lLen(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of list stored at {@link KeyCommand#getKey()}
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> lLen(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* Get elements between {@code begin} and {@code end} from list at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> lRange(ByteBuffer key, long start, long end) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return lRange(Mono.just(RangeCommand.key(key).fromIndex(start).toIndex(end))).next()
|
||||
.map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get elements in {@link RangeCommand#getRange()} from list at {@link RangeCommand#getKey()}
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<RangeCommand, ByteBuffer>> lRange(Publisher<RangeCommand> commands);
|
||||
|
||||
/**
|
||||
* Trim list at {@code key} to elements between {@code begin} and {@code end}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> lTrim(ByteBuffer key, long start, long end) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return lTrim(Mono.just(RangeCommand.key(key).fromIndex(start).toIndex(end))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim list at {@link RangeCommand#getKey()} to elements within {@link RangeCommand#getRange()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<RangeCommand>> lTrim(Publisher<RangeCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class LIndexCommand extends KeyCommand {
|
||||
|
||||
private final Long index;
|
||||
|
||||
private LIndexCommand(ByteBuffer key, Long index) {
|
||||
|
||||
super(key);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public static LIndexCommand elementAt(Long index) {
|
||||
return new LIndexCommand(null, index);
|
||||
}
|
||||
|
||||
public LIndexCommand from(ByteBuffer key) {
|
||||
return new LIndexCommand(key, index);
|
||||
}
|
||||
|
||||
public Long getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element at {@code index} form list at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> lIndex(ByteBuffer key, long index) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return lIndex(Mono.just(LIndexCommand.elementAt(index).from(key))).next().map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element at {@link LIndexCommand#getIndex()} form list at {@link LIndexCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<LIndexCommand>> lIndex(Publisher<LIndexCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class LInsertCommand extends KeyCommand {
|
||||
|
||||
private final Position position;
|
||||
private final ByteBuffer pivot;
|
||||
private final ByteBuffer value;
|
||||
|
||||
public LInsertCommand(ByteBuffer key, Position position, ByteBuffer pivot, ByteBuffer value) {
|
||||
|
||||
super(key);
|
||||
this.position = position;
|
||||
this.pivot = pivot;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static LInsertCommand value(ByteBuffer value) {
|
||||
return new LInsertCommand(null, null, null, value);
|
||||
}
|
||||
|
||||
public LInsertCommand before(ByteBuffer pivot) {
|
||||
return new LInsertCommand(getKey(), Position.BEFORE, pivot, value);
|
||||
}
|
||||
|
||||
public LInsertCommand after(ByteBuffer pivot) {
|
||||
return new LInsertCommand(getKey(), Position.AFTER, pivot, value);
|
||||
}
|
||||
|
||||
public LInsertCommand forKey(ByteBuffer key) {
|
||||
return new LInsertCommand(key, position, pivot, value);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public ByteBuffer getPivot() {
|
||||
return pivot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert {@code value} {@link Position#BEFORE} or {@link Position#AFTER} existing {@code pivot} for {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lInsert(ByteBuffer key, Position position, ByteBuffer pivot, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null!");
|
||||
Assert.notNull(position, "position must not be null!");
|
||||
Assert.notNull(pivot, "pivot must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
LInsertCommand command = LInsertCommand.value(value);
|
||||
command = Position.BEFORE.equals(position) ? command.before(pivot) : command.after(pivot);
|
||||
command = command.forKey(key);
|
||||
return lInsert(Mono.just(command)).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert {@link LInsertCommand#getValue()} {@link Position#BEFORE} or {@link Position#AFTER} existing
|
||||
* {@link LInsertCommand#getPivot()} for {@link LInsertCommand#getKey()}
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class LSetCommand extends KeyCommand {
|
||||
|
||||
private final Long index;
|
||||
private final ByteBuffer value;
|
||||
|
||||
private LSetCommand(ByteBuffer key, Long index, ByteBuffer value) {
|
||||
|
||||
super(key);
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static LSetCommand elementAt(Long index) {
|
||||
return new LSetCommand(null, index, null);
|
||||
}
|
||||
|
||||
public LSetCommand to(ByteBuffer value) {
|
||||
return new LSetCommand(getKey(), index, value);
|
||||
}
|
||||
|
||||
public LSetCommand forKey(ByteBuffer key) {
|
||||
return new LSetCommand(key, index, value);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Long getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code value} list element at {@code index}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param index
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> lSet(ByteBuffer key, long index, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return lSet(Mono.just(LSetCommand.elementAt(index).to(value).forKey(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link LSetCommand#getValue()} list element at {@link LSetCommand#getKey()}.
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<LSetCommand>> lSet(Publisher<LSetCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class LRemCommand extends KeyCommand {
|
||||
|
||||
private final Long count;
|
||||
private final ByteBuffer value;
|
||||
|
||||
private LRemCommand(ByteBuffer key, Long count, ByteBuffer value) {
|
||||
super(key);
|
||||
this.count = count;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static LRemCommand all() {
|
||||
return new LRemCommand(null, 0L, null);
|
||||
}
|
||||
|
||||
public static LRemCommand first(Long count) {
|
||||
return new LRemCommand(null, count, null);
|
||||
}
|
||||
|
||||
public static LRemCommand last(Long count) {
|
||||
|
||||
Long value = count < 0L ? count : Math.negateExact(count);
|
||||
return new LRemCommand(null, value, null);
|
||||
}
|
||||
|
||||
public LRemCommand occurancesOf(ByteBuffer value) {
|
||||
return new LRemCommand(getKey(), count, value);
|
||||
}
|
||||
|
||||
public LRemCommand from(ByteBuffer key) {
|
||||
return new LRemCommand(key, count, value);
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all occurrences of {@code value} from the list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lRem(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return lRem(Mono.just(LRemCommand.all().occurancesOf(value).from(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first {@code count} occurrences of {@code value} from the list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param count must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> lRem(ByteBuffer key, Long count, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(count, "count must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return lRem(Mono.just(LRemCommand.first(count).occurancesOf(value).from(key))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@link LRemCommand#getCount()} occurrences of {@link LRemCommand#getValue()} from the list stored at
|
||||
* {@link LRemCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<LRemCommand, Long>> lRem(Publisher<LRemCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PopCommand extends KeyCommand {
|
||||
|
||||
private final Direction direction;
|
||||
|
||||
private PopCommand(ByteBuffer key, Direction direction) {
|
||||
|
||||
super(key);
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public static PopCommand right() {
|
||||
return new PopCommand(null, Direction.RIGHT);
|
||||
}
|
||||
|
||||
public static PopCommand left() {
|
||||
return new PopCommand(null, Direction.LEFT);
|
||||
}
|
||||
|
||||
public PopCommand from(ByteBuffer key) {
|
||||
return new PopCommand(key, direction);
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns first element in list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> lPop(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return pop(Mono.just(PopCommand.left().from(key))).next().map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns last element in list stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> rPop(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return pop(Mono.just(PopCommand.right().from(key))).next().map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns last element in list stored at {@link KeyCommand#getKey()}
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<PopCommand>> pop(Publisher<PopCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class BPopCommand implements Command {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
private final Duration timeout;
|
||||
private final Direction direction;
|
||||
|
||||
private BPopCommand(List<ByteBuffer> keys, Duration timeout, Direction direction) {
|
||||
this.keys = keys;
|
||||
this.timeout = timeout;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public static BPopCommand right() {
|
||||
return new BPopCommand(null, Duration.ZERO, Direction.RIGHT);
|
||||
}
|
||||
|
||||
public static BPopCommand left() {
|
||||
return new BPopCommand(null, Duration.ZERO, Direction.LEFT);
|
||||
}
|
||||
|
||||
public BPopCommand from(List<ByteBuffer> keys) {
|
||||
return new BPopCommand(keys, Duration.ZERO, direction);
|
||||
}
|
||||
|
||||
public BPopCommand blockingFor(Duration timeout) {
|
||||
return new BPopCommand(keys, timeout, direction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public Direction getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PopResult {
|
||||
|
||||
private final List<ByteBuffer> result;
|
||||
|
||||
public PopResult(List<ByteBuffer> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public ByteBuffer getKey() {
|
||||
return ObjectUtils.isEmpty(result) ? null : result.get(0);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return ObjectUtils.isEmpty(result) ? null : result.get(1);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getRaw() {
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class PopResponse extends CommandResponse<BPopCommand, PopResult> {
|
||||
|
||||
public PopResponse(BPopCommand input, PopResult output) {
|
||||
super(input, output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns first element from lists stored at {@code keys}. <br>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<PopResult> blPop(List<ByteBuffer> keys, Duration timeout) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null.");
|
||||
Assert.notNull(timeout, "timeout must not be null.");
|
||||
|
||||
return bPop(Mono.just(BPopCommand.left().from(keys).blockingFor(timeout))).next().map(PopResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns last element from lists stored at {@code keys}. <br>
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param timeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<PopResult> brPop(List<ByteBuffer> keys, Duration timeout) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null.");
|
||||
Assert.notNull(timeout, "timeout must not be null.");
|
||||
|
||||
return bPop(Mono.just(BPopCommand.right().from(keys).blockingFor(timeout))).next().map(PopResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the top {@link BPopCommand#getDirection()} element from lists stored at
|
||||
* {@link BPopCommand#getKeys()}.<br>
|
||||
* <b>Blocks connection</b> until element available or {@link BPopCommand#getTimeout()} reached.
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<PopResponse> bPop(Publisher<BPopCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class RPopLPushCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer destination;
|
||||
|
||||
private RPopLPushCommand(ByteBuffer key, ByteBuffer destination) {
|
||||
|
||||
super(key);
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public static RPopLPushCommand from(ByteBuffer sourceKey) {
|
||||
return new RPopLPushCommand(sourceKey, null);
|
||||
}
|
||||
|
||||
public RPopLPushCommand to(ByteBuffer destinationKey) {
|
||||
return new RPopLPushCommand(getKey(), destinationKey);
|
||||
}
|
||||
|
||||
public ByteBuffer getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@code source}, append it to {@code destination} and return its value.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> rPopLPush(ByteBuffer source, ByteBuffer destination) {
|
||||
|
||||
Assert.notNull(source, "source must not be null");
|
||||
Assert.notNull(destination, "destination must not be null");
|
||||
|
||||
return rPopLPush(Mono.just(RPopLPushCommand.from(source).to(destination))).next()
|
||||
.map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@link RPopLPushCommand#getKey()}, append it to
|
||||
* {@link RPopLPushCommand#getDestination()} and return its value.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<RPopLPushCommand>> rPopLPush(Publisher<RPopLPushCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class BRPopLPushCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer destination;
|
||||
private final Duration timeout;
|
||||
|
||||
private BRPopLPushCommand(ByteBuffer key, ByteBuffer destination, Duration timeout) {
|
||||
|
||||
super(key);
|
||||
this.destination = destination;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public static BRPopLPushCommand from(ByteBuffer sourceKey) {
|
||||
return new BRPopLPushCommand(sourceKey, null, null);
|
||||
}
|
||||
|
||||
public BRPopLPushCommand to(ByteBuffer destinationKey) {
|
||||
return new BRPopLPushCommand(getKey(), destinationKey, timeout);
|
||||
}
|
||||
|
||||
public BRPopLPushCommand blockingFor(Duration timeout) {
|
||||
return new BRPopLPushCommand(getKey(), destination, timeout);
|
||||
}
|
||||
|
||||
public ByteBuffer getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@code source}, append it to {@code destination} and return its value.
|
||||
* <b>Blocks connection</b> until element available or {@code timeout} reached. <br />
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> bRPopLPush(ByteBuffer source, ByteBuffer destination, Duration timeout) {
|
||||
|
||||
Assert.notNull(source, "source must not be null");
|
||||
Assert.notNull(destination, "destination must not be null");
|
||||
|
||||
return bRPopLPush(Mono.just(BRPopLPushCommand.from(source).to(destination).blockingFor(timeout))).next()
|
||||
.map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last element from list at {@link BRPopLPushCommand#getKey()}, append it to
|
||||
* {@link BRPopLPushCommand#getDestination()} and return its value. <br />
|
||||
* <b>Blocks connection</b> until element available or {@link BRPopLPushCommand#getTimeout()} reached.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<BRPopLPushCommand>> bRPopLPush(Publisher<BRPopLPushCommand> commands);
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveNumberCommands {
|
||||
|
||||
/**
|
||||
* Increment value of {@code key} by 1.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> incr(ByteBuffer key) {
|
||||
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return incr(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment value of {@code key} by 1.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> incr(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class IncrByCommand<T extends Number> extends KeyCommand {
|
||||
|
||||
private T value;
|
||||
|
||||
private IncrByCommand(ByteBuffer key, T value) {
|
||||
super(key);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static <T extends Number> IncrByCommand<T> incr(ByteBuffer key) {
|
||||
return new IncrByCommand<T>(key, null);
|
||||
}
|
||||
|
||||
public IncrByCommand<T> by(T value) {
|
||||
return new IncrByCommand<T>(getKey(), value);
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment value of {@code key} by {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default <T extends Number> Mono<T> incrBy(ByteBuffer key, T value) {
|
||||
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
|
||||
return incrBy(Mono.just(IncrByCommand.<T> incr(key).by(value))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment value of {@code key} by {@code value}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T extends Number> Flux<NumericResponse<ReactiveNumberCommands.IncrByCommand<T>, T>> incrBy(
|
||||
Publisher<ReactiveNumberCommands.IncrByCommand<T>> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class DecrByCommand<T extends Number> extends KeyCommand {
|
||||
|
||||
private T value;
|
||||
|
||||
private DecrByCommand(ByteBuffer key, T value) {
|
||||
super(key);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static <T extends Number> ReactiveNumberCommands.DecrByCommand<T> decr(ByteBuffer key) {
|
||||
return new DecrByCommand<T>(key, null);
|
||||
}
|
||||
|
||||
public ReactiveNumberCommands.DecrByCommand<T> by(T value) {
|
||||
return new DecrByCommand<T>(getKey(), value);
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement value of {@code key} by 1.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> decr(ByteBuffer key) {
|
||||
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
|
||||
return decr(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement value of {@code key} by 1.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> decr(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* Decrement value of {@code key} by {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default <T extends Number> Mono<T> decrBy(ByteBuffer key, T value) {
|
||||
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
|
||||
return decrBy(Mono.just(DecrByCommand.<T> decr(key).by(value))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement value of {@code key} by {@code value}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
<T extends Number> Flux<NumericResponse<ReactiveNumberCommands.DecrByCommand<T>, T>> decrBy(
|
||||
Publisher<ReactiveNumberCommands.DecrByCommand<T>> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class HIncrByCommand<T extends Number> extends KeyCommand {
|
||||
|
||||
private final ByteBuffer field;
|
||||
private final T value;
|
||||
|
||||
private HIncrByCommand(ByteBuffer key, ByteBuffer field, T value) {
|
||||
|
||||
super(key);
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static <T extends Number> HIncrByCommand<T> incr(ByteBuffer field) {
|
||||
return new HIncrByCommand<T>(null, field, null);
|
||||
}
|
||||
|
||||
public HIncrByCommand<T> by(T value) {
|
||||
return new HIncrByCommand<T>(getKey(), field, value);
|
||||
}
|
||||
|
||||
public HIncrByCommand<T> forKey(ByteBuffer key) {
|
||||
return new HIncrByCommand<T>(key, field, value);
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public ByteBuffer getField() {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment {@code value} of a hash {@code field} by the given {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default <T extends Number> Mono<T> hIncrBy(ByteBuffer key, ByteBuffer field, T value) {
|
||||
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(field, "field must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
|
||||
return hIncrBy(Mono.just(HIncrByCommand.<T> incr(field).by(value).forKey(key))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment {@code value} of a hash {@code field} by the given {@code value}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
<T extends Number> Flux<NumericResponse<HIncrByCommand<T>, T>> hIncrBy(Publisher<HIncrByCommand<T>> commands);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRedisClusterConnection extends ReactiveRedisConnection {
|
||||
|
||||
@Override
|
||||
ReactiveClusterKeyCommands keyCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterStringCommands stringCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterNumberCommands numberCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterListCommands listCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterSetCommands setCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterZSetCommands zSetCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterHashCommands hashCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterGeoCommands geoCommands();
|
||||
|
||||
@Override
|
||||
ReactiveClusterHyperLogLogCommands hyperLogLogCommands();
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.data.domain.Range;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRedisConnection extends Closeable {
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveKeyCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveKeyCommands keyCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveStringCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveStringCommands stringCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveNumberCommands}
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveNumberCommands numberCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveListCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveListCommands listCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveSetCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveSetCommands setCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveZSetCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveZSetCommands zSetCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveHashCommands}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
ReactiveHashCommands hashCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReacktiveGeoCommands}
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveGeoCommands geoCommands();
|
||||
|
||||
/**
|
||||
* Get {@link ReactiveHyperLogLogCommands}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
ReactiveHyperLogLogCommands hyperLogLogCommands();
|
||||
|
||||
interface Command {
|
||||
|
||||
ByteBuffer getKey();
|
||||
|
||||
default String getName() {
|
||||
return getClass().getSimpleName().replace("Command", "").toUpperCase();
|
||||
}
|
||||
|
||||
static <T extends Command> Builder<T> create(Class<T> type) {
|
||||
return new CommandBuilder<T>(type);
|
||||
}
|
||||
|
||||
interface Builder<T extends Command> extends Consumer<Object> {
|
||||
|
||||
default Builder<T> forKey(String key) {
|
||||
return forKey(key.getBytes(Charset.forName("UTF-8")));
|
||||
}
|
||||
|
||||
default Builder<T> forKey(byte[] key) {
|
||||
return forKey(ByteBuffer.wrap(key));
|
||||
}
|
||||
|
||||
default Builder<T> forKey(ByteBuffer key) {
|
||||
return forKey(() -> key);
|
||||
}
|
||||
|
||||
Builder<T> forKey(Supplier<ByteBuffer> keySupplier);
|
||||
|
||||
T build();
|
||||
}
|
||||
|
||||
class CommandBuilder<T extends Command> implements Builder<T> {
|
||||
|
||||
List<Object> argumentList = new ArrayList<>();
|
||||
|
||||
Class<T> type;
|
||||
Supplier<ByteBuffer> key;
|
||||
|
||||
public CommandBuilder(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Builder<T> forKey(Supplier<ByteBuffer> key) {
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Object t) {
|
||||
argumentList.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T build() {
|
||||
|
||||
try {
|
||||
T x = BeanUtils.instantiateClass(type);
|
||||
|
||||
DirectFieldAccessor dfa = new DirectFieldAccessor(x);
|
||||
dfa.setPropertyValue("key", key);
|
||||
return x;
|
||||
|
||||
} catch (IllegalArgumentException | SecurityException e) {
|
||||
throw new IllegalArgumentException(" ¯\\_(ツ)_/¯", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class KeyCommand implements Command {
|
||||
|
||||
private ByteBuffer key;
|
||||
|
||||
public KeyCommand(ByteBuffer key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class RangeCommand extends KeyCommand {
|
||||
|
||||
Range<Long> range;
|
||||
|
||||
public RangeCommand(ByteBuffer key, Range<Long> range) {
|
||||
|
||||
super(key);
|
||||
this.range = range != null ? range : new Range<>(0L, Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static RangeCommand key(ByteBuffer key) {
|
||||
return new RangeCommand(key, null);
|
||||
}
|
||||
|
||||
public RangeCommand within(Range<Long> range) {
|
||||
return new RangeCommand(getKey(), range);
|
||||
}
|
||||
|
||||
public RangeCommand fromIndex(Long start) {
|
||||
return new RangeCommand(getKey(), new Range<>(start, range.getUpperBound()));
|
||||
}
|
||||
|
||||
public RangeCommand toIndex(Long end) {
|
||||
return new RangeCommand(getKey(), new Range<>(range.getLowerBound(), end));
|
||||
}
|
||||
|
||||
public Range<Long> getRange() {
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
class CommandResponse<I, O> {
|
||||
|
||||
private final I input;
|
||||
private final O output;
|
||||
}
|
||||
|
||||
class BooleanResponse<I> extends CommandResponse<I, Boolean> {
|
||||
|
||||
public BooleanResponse(I input, Boolean output) {
|
||||
super(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
class ByteBufferResponse<I> extends CommandResponse<I, ByteBuffer> {
|
||||
|
||||
public ByteBufferResponse(I input, ByteBuffer output) {
|
||||
super(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
class MultiValueResponse<I, O> extends CommandResponse<I, List<O>> {
|
||||
|
||||
public MultiValueResponse(I input, List<O> output) {
|
||||
super(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
class NumericResponse<I, O extends Number> extends CommandResponse<I, O> {
|
||||
|
||||
public NumericResponse(I input, O output) {
|
||||
super(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.Command;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveSetCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SAddCommand extends KeyCommand {
|
||||
|
||||
private List<ByteBuffer> values;
|
||||
|
||||
private SAddCommand(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
super(key);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public static SAddCommand value(ByteBuffer values) {
|
||||
return values(Collections.singletonList(values));
|
||||
}
|
||||
|
||||
public static SAddCommand values(List<ByteBuffer> values) {
|
||||
return new SAddCommand(null, values);
|
||||
}
|
||||
|
||||
public SAddCommand to(ByteBuffer key) {
|
||||
return new SAddCommand(key, values);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given {@code value} to set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> sAdd(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return sAdd(key, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given {@code values} to set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> sAdd(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(values, "values must not be null");
|
||||
|
||||
return sAdd(Mono.just(SAddCommand.values(values).to(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given {@link SAddCommand#getValues()} to set at {@link SAddCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<SAddCommand, Long>> sAdd(Publisher<SAddCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SRemCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> values;
|
||||
|
||||
public SRemCommand(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
super(key);
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public static SRemCommand value(ByteBuffer values) {
|
||||
return values(Collections.singletonList(values));
|
||||
}
|
||||
|
||||
public static SRemCommand values(List<ByteBuffer> values) {
|
||||
return new SRemCommand(null, values);
|
||||
}
|
||||
|
||||
public SRemCommand from(ByteBuffer key) {
|
||||
return new SRemCommand(key, values);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove given {@code value} from set at {@code key} and return the number of removed elements.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> sRem(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return sRem(key, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove given {@code values} from set at {@code key} and return the number of removed elements.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> sRem(ByteBuffer key, List<ByteBuffer> values) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(values, "values must not be null");
|
||||
|
||||
return sRem(Mono.just(SRemCommand.values(values).from(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove given {@link SRemCommand#getValues()} from set at {@link SRemCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<SRemCommand, Long>> sRem(Publisher<SRemCommand> commands);
|
||||
|
||||
/**
|
||||
* Remove and return a random member from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> sPop(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return sPop(Mono.just(new KeyCommand(key))).next().map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and return a random member from set at {@link KeyCommand#getKey()}
|
||||
*
|
||||
* @param commands
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<KeyCommand>> sPop(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SMoveCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer destination;
|
||||
private final ByteBuffer value;
|
||||
|
||||
private SMoveCommand(ByteBuffer key, ByteBuffer destination, ByteBuffer value) {
|
||||
|
||||
super(key);
|
||||
this.destination = destination;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static SMoveCommand value(ByteBuffer value) {
|
||||
return new SMoveCommand(null, null, value);
|
||||
}
|
||||
|
||||
public SMoveCommand from(ByteBuffer source) {
|
||||
return new SMoveCommand(source, destination, value);
|
||||
}
|
||||
|
||||
public SMoveCommand to(ByteBuffer destination) {
|
||||
return new SMoveCommand(getKey(), destination, value);
|
||||
}
|
||||
|
||||
public ByteBuffer getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move {@code value} from {@code sourceKey} to {@code destinationKey}
|
||||
*
|
||||
* @param sourceKey must not be {@literal null}.
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> sMove(ByteBuffer sourceKey, ByteBuffer destinationKey, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(sourceKey, "sourceKey must not be null");
|
||||
Assert.notNull(destinationKey, "destinationKey must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return sMove(Mono.just(SMoveCommand.value(value).from(sourceKey).to(destinationKey))).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move {@link SMoveCommand#getValue()} from {@link SMoveCommand#getKey()} to {@link SMoveCommand#getDestination()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<SMoveCommand>> sMove(Publisher<SMoveCommand> commands);
|
||||
|
||||
/**
|
||||
* Get size of set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> sCard(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return sCard(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of set at {@link KeyCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> sCard(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SIsMemberCommand extends KeyCommand {
|
||||
|
||||
private final ByteBuffer value;
|
||||
|
||||
private SIsMemberCommand(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
super(key);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static SIsMemberCommand value(ByteBuffer value) {
|
||||
return new SIsMemberCommand(null, value);
|
||||
}
|
||||
|
||||
public SIsMemberCommand of(ByteBuffer set) {
|
||||
return new SIsMemberCommand(set, value);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if set at {@code key} contains {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> sIsMember(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(value, "value must not be null");
|
||||
|
||||
return sIsMember(Mono.just(SIsMemberCommand.value(value).of(key))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if set at {@link SIsMemberCommand#getKey()} contains {@link SIsMemberCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<SIsMemberCommand>> sIsMember(Publisher<SIsMemberCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SInterCommand implements Command {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SInterCommand(List<ByteBuffer> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SInterCommand keys(List<ByteBuffer> keys) {
|
||||
return new SInterCommand(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code keys}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> sInter(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sInter(Mono.just(SInterCommand.keys(keys))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@link SInterCommand#getKeys()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<SInterCommand, ByteBuffer>> sInter(Publisher<SInterCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SInterStoreCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SInterStoreCommand(ByteBuffer key, List<ByteBuffer> keys) {
|
||||
|
||||
super(key);
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SInterStoreCommand keys(List<ByteBuffer> keys) {
|
||||
return new SInterStoreCommand(null, keys);
|
||||
}
|
||||
|
||||
public SInterStoreCommand storeAt(ByteBuffer key) {
|
||||
return new SInterStoreCommand(key, keys);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return size of set stored a {@code destinationKey}.
|
||||
*/
|
||||
default Mono<Long> sInterStore(ByteBuffer destinationKey, List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(destinationKey, "destinationKey must not be null");
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sInterStore(Mono.just(SInterStoreCommand.keys(keys).storeAt(destinationKey))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<SInterStoreCommand, Long>> sInterStore(Publisher<SInterStoreCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SUnionCommand implements Command {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SUnionCommand(List<ByteBuffer> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SUnionCommand keys(List<ByteBuffer> keys) {
|
||||
return new SUnionCommand(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@code keys}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> sUnion(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sUnion(Mono.just(SUnionCommand.keys(keys))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the members intersecting all given sets at {@link SInterCommand#getKeys()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<SUnionCommand, ByteBuffer>> sUnion(Publisher<SUnionCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SUnionStoreCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SUnionStoreCommand(ByteBuffer key, List<ByteBuffer> keys) {
|
||||
|
||||
super(key);
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SUnionStoreCommand keys(List<ByteBuffer> keys) {
|
||||
return new SUnionStoreCommand(null, keys);
|
||||
}
|
||||
|
||||
public SUnionStoreCommand storeAt(ByteBuffer key) {
|
||||
return new SUnionStoreCommand(key, keys);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Union all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return size of set stored a {@code destinationKey}.
|
||||
*/
|
||||
default Mono<Long> sUnionStore(ByteBuffer destinationKey, List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(destinationKey, "destinationKey must not be null");
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sUnionStore(Mono.just(SUnionStoreCommand.keys(keys).storeAt(destinationKey))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Union all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<SUnionStoreCommand, Long>> sUnionStore(Publisher<SUnionStoreCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SDiffCommand implements Command {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SDiffCommand(List<ByteBuffer> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SDiffCommand keys(List<ByteBuffer> keys) {
|
||||
return new SDiffCommand(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the diff of the members of all given sets at {@code keys}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> sDiff(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sDiff(Mono.just(SDiffCommand.keys(keys))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the diff of the members of all given sets at {@link SInterCommand#getKeys()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<SDiffCommand, ByteBuffer>> sDiff(Publisher<SDiffCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SDiffStoreCommand extends KeyCommand {
|
||||
|
||||
private final List<ByteBuffer> keys;
|
||||
|
||||
private SDiffStoreCommand(ByteBuffer key, List<ByteBuffer> keys) {
|
||||
|
||||
super(key);
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public static SDiffStoreCommand keys(List<ByteBuffer> keys) {
|
||||
return new SDiffStoreCommand(null, keys);
|
||||
}
|
||||
|
||||
public SDiffStoreCommand storeAt(ByteBuffer key) {
|
||||
return new SDiffStoreCommand(key, keys);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param destinationKey must not be {@literal null}.
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return size of set stored a {@code destinationKey}.
|
||||
*/
|
||||
default Mono<Long> sDiffStore(ByteBuffer destinationKey, List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(destinationKey, "destinationKey must not be null");
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return sDiffStore(Mono.just(SDiffStoreCommand.keys(keys).storeAt(destinationKey))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff all given sets at {@code keys} and store result in {@code destinationKey}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<SDiffStoreCommand, Long>> sDiffStore(Publisher<SDiffStoreCommand> commands);
|
||||
|
||||
/**
|
||||
* Get all elements of set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> sMembers(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return sMembers(Mono.just(new KeyCommand(key))).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all elements of set at {@link KeyCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<KeyCommand, ByteBuffer>> sMembers(Publisher<KeyCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SRandMembersCommand extends KeyCommand {
|
||||
|
||||
private final Long count;
|
||||
|
||||
private SRandMembersCommand(ByteBuffer key, Long count) {
|
||||
|
||||
super(key);
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public static SRandMembersCommand valueCount(Long nrValuesToRetrieve) {
|
||||
return new SRandMembersCommand(null, nrValuesToRetrieve);
|
||||
}
|
||||
|
||||
public static SRandMembersCommand singleValue() {
|
||||
return new SRandMembersCommand(null, null);
|
||||
}
|
||||
|
||||
public SRandMembersCommand from(ByteBuffer key) {
|
||||
return new SRandMembersCommand(key, count);
|
||||
}
|
||||
|
||||
public Optional<Long> getCount() {
|
||||
return Optional.ofNullable(count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get random element from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> sRandMember(ByteBuffer key) {
|
||||
return sRandMember(key, 1L).map(vals -> vals.isEmpty() ? null : vals.iterator().next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@code count} random elements from set at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param count must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> sRandMember(ByteBuffer key, Long count) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
Assert.notNull(count, "count must not be null");
|
||||
|
||||
return sRandMember(Mono.just(SRandMembersCommand.valueCount(count).from(key))).next()
|
||||
.map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link SRandMembersCommand#getCount()} random elements from set at {@link SRandMembersCommand#getKey()}.
|
||||
*
|
||||
* @param commands must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<SRandMembersCommand, ByteBuffer>> sRandMember(Publisher<SRandMembersCommand> commands);
|
||||
}
|
||||
@@ -0,0 +1,752 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.Command;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
|
||||
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveStringCommands {
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SetCommand extends KeyCommand {
|
||||
|
||||
private ByteBuffer value;
|
||||
private Expiration expiration;
|
||||
private SetOption option;
|
||||
|
||||
private SetCommand(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option) {
|
||||
|
||||
super(key);
|
||||
this.value = value;
|
||||
this.expiration = expiration;
|
||||
this.option = option;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.SetCommand set(ByteBuffer key) {
|
||||
return new SetCommand(key, null, null, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetCommand value(ByteBuffer value) {
|
||||
return new SetCommand(getKey(), value, expiration, option);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetCommand expiring(Expiration expiration) {
|
||||
return new SetCommand(getKey(), value, expiration, option);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetCommand withSetOption(SetOption option) {
|
||||
return new SetCommand(getKey(), value, expiration, option);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Optional<Expiration> getExpiration() {
|
||||
return Optional.ofNullable(expiration);
|
||||
}
|
||||
|
||||
public Optional<SetOption> getOption() {
|
||||
return Optional.ofNullable(option);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@literal value} for {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> set(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return set(Mono.just(SetCommand.set(key).value(value))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@literal value} for {@literal key} with {@literal expiration} and {@literal options}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @param expiration must not be {@literal null}.
|
||||
* @param option must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> set(ByteBuffer key, ByteBuffer value, Expiration expiration, SetOption option) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return set(Mono.just(SetCommand.set(key).value(value).withSetOption(option).expiring(expiration))).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set each and every {@link KeyValue} item separately.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link SetResponse} holding the {@link KeyValue} pair to set along with the command result.
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.SetCommand>> set(Publisher<ReactiveStringCommands.SetCommand> commands);
|
||||
|
||||
/**
|
||||
* Get single element stored at {@literal key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return empty {@link ByteBuffer} in case {@literal key} does not exist.
|
||||
*/
|
||||
default Mono<ByteBuffer> get(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return get(Mono.just(new KeyCommand(key))).next().map((result) -> result.getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get elements one by one.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link GetResponse} holding the {@literal key} to get along with the value retrieved.
|
||||
*/
|
||||
Flux<ByteBufferResponse<KeyCommand>> get(Publisher<KeyCommand> keys);
|
||||
|
||||
/**
|
||||
* Set {@literal value} for {@literal key} and return the existing value.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> getSet(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return getSet(Mono.just(SetCommand.set(key).value(value))).next().map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@literal value} for {@literal key} and return the existing value one by one.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return {@link Flux} of {@link GetSetResponse} holding the {@link KeyValue} pair to set along with the previously
|
||||
* existing value.
|
||||
*/
|
||||
Flux<ByteBufferResponse<ReactiveStringCommands.SetCommand>> getSet(
|
||||
Publisher<ReactiveStringCommands.SetCommand> command);
|
||||
|
||||
/**
|
||||
* Get multiple values in one batch.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<List<ByteBuffer>> mGet(List<ByteBuffer> keys) {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
|
||||
return mGet(Mono.just(keys)).next().map(MultiValueResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple values at in batches.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<MultiValueResponse<List<ByteBuffer>, ByteBuffer>> mGet(Publisher<List<ByteBuffer>> keysets);
|
||||
|
||||
/**
|
||||
* Set {@code value} for {@code key}, only if {@code key} does not exist.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> setNX(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "Keys must not be null!");
|
||||
Assert.notNull(value, "Keys must not be null!");
|
||||
|
||||
return setNX(Mono.just(SetCommand.set(key).value(value))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@code key value} pairs, only if {@code key} does not exist.
|
||||
*
|
||||
* @param values must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.SetCommand>> setNX(Publisher<ReactiveStringCommands.SetCommand> values);
|
||||
|
||||
/**
|
||||
* Set {@code key value} pair and {@link Expiration}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @param expireTimeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> setEX(ByteBuffer key, ByteBuffer value, Expiration expireTimeout) {
|
||||
|
||||
Assert.notNull(key, "Keys must not be null!");
|
||||
Assert.notNull(value, "Keys must not be null!");
|
||||
Assert.notNull(key, "ExpireTimeout must not be null!");
|
||||
|
||||
return setEX(Mono.just(SetCommand.set(key).value(value).expiring(expireTimeout))).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@code key value} pairs and {@link Expiration}.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param expireTimeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.SetCommand>> setEX(Publisher<ReactiveStringCommands.SetCommand> command);
|
||||
|
||||
/**
|
||||
* Set {@code key value} pair and {@link Expiration}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @param expireTimeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> pSetEX(ByteBuffer key, ByteBuffer value, Expiration expireTimeout) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
Assert.notNull(key, "ExpireTimeout must not be null!");
|
||||
|
||||
return pSetEX(Mono.just(SetCommand.set(key).value(value).expiring(expireTimeout))).next()
|
||||
.map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@code key value} pairs and {@link Expiration}.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @param expireTimeout must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.SetCommand>> pSetEX(Publisher<ReactiveStringCommands.SetCommand> command);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class MSetCommand implements Command {
|
||||
|
||||
private Map<ByteBuffer, ByteBuffer> keyValuePairs;
|
||||
|
||||
private MSetCommand(Map<ByteBuffer, ByteBuffer> keyValuePairs) {
|
||||
this.keyValuePairs = keyValuePairs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.MSetCommand mset(Map<ByteBuffer, ByteBuffer> keyValuePairs) {
|
||||
return new MSetCommand(keyValuePairs);
|
||||
}
|
||||
|
||||
public Map<ByteBuffer, ByteBuffer> getKeyValuePairs() {
|
||||
return keyValuePairs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
|
||||
*
|
||||
* @param tuples must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> mSet(Map<ByteBuffer, ByteBuffer> tuples) {
|
||||
|
||||
Assert.notNull(tuples, "Tuples must not be null!");
|
||||
|
||||
return mSet(Mono.just(MSetCommand.mset(tuples))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code source}.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.MSetCommand>> mSet(Publisher<ReactiveStringCommands.MSetCommand> source);
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code tuples} only if the provided key does
|
||||
* not exist.
|
||||
*
|
||||
* @param tuples must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> mSetNX(Map<ByteBuffer, ByteBuffer> tuples) {
|
||||
|
||||
Assert.notNull(tuples, "Tuples must not be null!");
|
||||
|
||||
return mSetNX(Mono.just(MSetCommand.mset(tuples))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple keys to multiple values using key-value pairs provided in {@code tuples} only if the provided key does
|
||||
* not exist.
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.MSetCommand>> mSetNX(
|
||||
Publisher<ReactiveStringCommands.MSetCommand> source);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class AppendCommand extends KeyCommand {
|
||||
|
||||
private ByteBuffer value;
|
||||
|
||||
private AppendCommand(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
super(key);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.AppendCommand key(ByteBuffer key) {
|
||||
return new AppendCommand(key, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.AppendCommand append(ByteBuffer value) {
|
||||
return new AppendCommand(getKey(), value);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a {@code value} to {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> append(ByteBuffer key, ByteBuffer value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return append(Mono.just(AppendCommand.key(key).append(value))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a {@link KeyValue#value} to {@link KeyValue#key}
|
||||
*
|
||||
* @param source must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<ReactiveStringCommands.AppendCommand, Long>> append(
|
||||
Publisher<ReactiveStringCommands.AppendCommand> source);
|
||||
|
||||
/**
|
||||
* Get a substring of value of {@code key} between {@code begin} and {@code end}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param begin
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
default Mono<ByteBuffer> getRange(ByteBuffer key, long begin, long end) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return getRange(Mono.just(RangeCommand.key(key).fromIndex(begin).toIndex(end))).next()
|
||||
.map(ByteBufferResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a substring of value of {@code key} between {@code begin} and {@code end}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param begin
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
Flux<ByteBufferResponse<RangeCommand>> getRange(Publisher<RangeCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class SetRangeCommand extends KeyCommand {
|
||||
|
||||
private ByteBuffer value;
|
||||
private Long offset;
|
||||
|
||||
private SetRangeCommand(ByteBuffer key, ByteBuffer value, Long offset) {
|
||||
|
||||
super(key);
|
||||
this.value = value;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.SetRangeCommand overwrite(ByteBuffer key) {
|
||||
return new SetRangeCommand(key, null, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetRangeCommand withValue(ByteBuffer value) {
|
||||
return new SetRangeCommand(getKey(), value, offset);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetRangeCommand atPosition(Long index) {
|
||||
return new SetRangeCommand(getKey(), value, index);
|
||||
}
|
||||
|
||||
public ByteBuffer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite parts of {@code key} starting at the specified {@code offset} with given {@code value}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> setRange(ByteBuffer key, ByteBuffer value, long offset) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
|
||||
return setRange(Mono.just(SetRangeCommand.overwrite(key).withValue(value).atPosition(offset))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite parts of {@link KeyValue#key} starting at the specified {@code offset} with given {@link KeyValue#value}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param offset must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<ReactiveStringCommands.SetRangeCommand, Long>> setRange(
|
||||
Publisher<ReactiveStringCommands.SetRangeCommand> commands);
|
||||
|
||||
class GetBitCommand extends KeyCommand {
|
||||
|
||||
public Long offset;
|
||||
|
||||
public GetBitCommand(ByteBuffer key, Long offset) {
|
||||
|
||||
super(key);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.GetBitCommand bit(ByteBuffer key) {
|
||||
return new GetBitCommand(key, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.GetBitCommand atOffset(Long offset) {
|
||||
return new GetBitCommand(getKey(), offset);
|
||||
}
|
||||
|
||||
public Long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bit value at {@code offset} of value at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param offset
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> getBit(ByteBuffer key, long offset) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return getBit(Mono.just(GetBitCommand.bit(key).atOffset(offset))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bit value at {@code offset} of value at {@code key}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param offset must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.GetBitCommand>> getBit(
|
||||
Publisher<ReactiveStringCommands.GetBitCommand> commands);
|
||||
|
||||
class SetBitCommand extends KeyCommand {
|
||||
|
||||
private Long offset;
|
||||
private Boolean value;
|
||||
|
||||
private SetBitCommand(ByteBuffer key, Long offset, Boolean value) {
|
||||
|
||||
super(key);
|
||||
this.offset = offset;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.SetBitCommand bit(ByteBuffer key) {
|
||||
return new SetBitCommand(key, null, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetBitCommand atOffset(Long index) {
|
||||
return new ReactiveStringCommands.SetBitCommand(getKey(), index, value);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.SetBitCommand to(Boolean bit) {
|
||||
return new ReactiveStringCommands.SetBitCommand(getKey(), offset, bit);
|
||||
}
|
||||
|
||||
public Long getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public Boolean getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bit at {@code offset} in value stored at {@code key} and return the original value.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param offset
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
default Mono<Boolean> setBit(ByteBuffer key, long offset, boolean value) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null!");
|
||||
|
||||
return setBit(Mono.just(SetBitCommand.bit(key).atOffset(offset).to(value))).next().map(BooleanResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bit at {@code offset} in value stored at {@code key} and return the original value.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param offset must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<BooleanResponse<ReactiveStringCommands.SetBitCommand>> setBit(
|
||||
Publisher<ReactiveStringCommands.SetBitCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class BitCountCommand extends KeyCommand {
|
||||
|
||||
private Range<Long> range;
|
||||
|
||||
public BitCountCommand(ByteBuffer key, Range<Long> range) {
|
||||
|
||||
super(key);
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.BitCountCommand bitCount(ByteBuffer key) {
|
||||
return new ReactiveStringCommands.BitCountCommand(key, null);
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.BitCountCommand within(Range<Long> range) {
|
||||
return new ReactiveStringCommands.BitCountCommand(getKey(), range);
|
||||
}
|
||||
|
||||
public Range<Long> getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of set bits (population counting) in value stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> bitCount(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
return bitCount(Mono.just(BitCountCommand.bitCount(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of set bits (population counting) of value stored at {@code key} between {@code begin} and
|
||||
* {@code end}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @param begin
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> bitCount(ByteBuffer key, long begin, long end) {
|
||||
|
||||
Assert.notNull(key, "Key must not be null");
|
||||
|
||||
return bitCount(Mono.just(BitCountCommand.bitCount(key).within(new Range<>(begin, end)))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of set bits (population counting) of value stored at {@code key} between {@code begin} and
|
||||
* {@code end}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param begin must not be {@literal null}.
|
||||
* @param end must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<ReactiveStringCommands.BitCountCommand, Long>> bitCount(
|
||||
Publisher<ReactiveStringCommands.BitCountCommand> commands);
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class BitOpCommand {
|
||||
|
||||
private List<ByteBuffer> keys;
|
||||
private BitOperation bitOp;
|
||||
private ByteBuffer destinationKey;
|
||||
|
||||
private BitOpCommand(List<ByteBuffer> keys, BitOperation bitOp, ByteBuffer destinationKey) {
|
||||
|
||||
this.keys = keys;
|
||||
this.bitOp = bitOp;
|
||||
this.destinationKey = destinationKey;
|
||||
}
|
||||
|
||||
public static ReactiveStringCommands.BitOpCommand perform(BitOperation bitOp) {
|
||||
return new ReactiveStringCommands.BitOpCommand(null, bitOp, null);
|
||||
}
|
||||
|
||||
public BitOperation getBitOp() {
|
||||
return bitOp;
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.BitOpCommand onKeys(List<ByteBuffer> keys) {
|
||||
return new ReactiveStringCommands.BitOpCommand(keys, bitOp, destinationKey);
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public ReactiveStringCommands.BitOpCommand andSaveAs(ByteBuffer destinationKey) {
|
||||
return new ReactiveStringCommands.BitOpCommand(keys, bitOp, destinationKey);
|
||||
}
|
||||
|
||||
public ByteBuffer getDestinationKey() {
|
||||
return destinationKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bitwise operations between strings.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param bitOp must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> bitOp(List<ByteBuffer> keys, BitOperation bitOp, ByteBuffer destination) {
|
||||
|
||||
Assert.notNull(keys, "keys must not be null");
|
||||
|
||||
return bitOp(Mono.just(BitOpCommand.perform(bitOp).onKeys(keys).andSaveAs(destination))).next()
|
||||
.map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bitwise operations between strings.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @param bitOp must not be {@literal null}.
|
||||
* @param destination must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<ReactiveStringCommands.BitOpCommand, Long>> bitOp(
|
||||
Publisher<ReactiveStringCommands.BitOpCommand> commands);
|
||||
|
||||
/**
|
||||
* Get the length of the value stored at {@code key}.
|
||||
*
|
||||
* @param key must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
default Mono<Long> strLen(ByteBuffer key) {
|
||||
|
||||
Assert.notNull(key, "key must not be null");
|
||||
|
||||
return strLen(Mono.just(new KeyCommand(key))).next().map(NumericResponse::getOutput);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the value stored at {@code key}.
|
||||
*
|
||||
* @param keys must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
Flux<NumericResponse<KeyCommand, Long>> strLen(Publisher<KeyCommand> keys);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,11 +37,23 @@ public interface RedisConnectionFactory extends PersistenceExceptionTranslator {
|
||||
* Provides a suitable connection for interacting with Redis Cluster.
|
||||
*
|
||||
* @return
|
||||
* @throws
|
||||
* @since 1.7
|
||||
*/
|
||||
RedisClusterConnection getClusterConnection();
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 2.0.
|
||||
*/
|
||||
ReactiveRedisConnection getReactiveConnection();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
ReactiveRedisClusterConnection getReactiveClusterConnection();
|
||||
|
||||
/**
|
||||
* Specifies if pipelined results should be converted to the expected data type. If false, results of
|
||||
* {@link RedisConnection#closePipeline()} and {RedisConnection#exec()} will be of the type returned by the underlying
|
||||
|
||||
@@ -199,7 +199,7 @@ public interface RedisGeoCommands {
|
||||
* @author Christoph Strobl
|
||||
* @since 1.8
|
||||
*/
|
||||
public class GeoRadiusCommandArgs {
|
||||
public class GeoRadiusCommandArgs implements Cloneable {
|
||||
|
||||
Set<Flag> flags = new LinkedHashSet<Flag>(2, 1);
|
||||
Long limit;
|
||||
@@ -309,6 +309,16 @@ public interface RedisGeoCommands {
|
||||
public static enum Flag {
|
||||
WITHCOORD, WITHDIST
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GeoRadiusCommandArgs clone() {
|
||||
|
||||
GeoRadiusCommandArgs tmp = new GeoRadiusCommandArgs();
|
||||
tmp.flags = this.flags != null ? new LinkedHashSet<>(this.flags) : new LinkedHashSet<>(2);
|
||||
tmp.limit = this.limit;
|
||||
tmp.sortDirection = this.sortDirection;
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -174,6 +174,10 @@ abstract public class Converters {
|
||||
};
|
||||
}
|
||||
|
||||
public static Boolean stringToBoolean(String s) {
|
||||
return ObjectUtils.nullSafeEquals("OK", s);
|
||||
}
|
||||
|
||||
public static Converter<String, Properties> stringToProps() {
|
||||
return STRING_TO_PROPS;
|
||||
}
|
||||
@@ -378,7 +382,7 @@ abstract public class Converters {
|
||||
* @author Christoph Strobl
|
||||
* @since 1.8
|
||||
*/
|
||||
static enum DistanceConverterFactory {
|
||||
enum DistanceConverterFactory {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
|
||||
@@ -33,14 +33,7 @@ import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.ExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.PassThroughExceptionTranslationStrategy;
|
||||
import org.springframework.data.redis.RedisConnectionFailureException;
|
||||
import org.springframework.data.redis.connection.ClusterCommandExecutor;
|
||||
import org.springframework.data.redis.connection.RedisClusterConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisSentinelConnection;
|
||||
import org.springframework.data.redis.connection.*;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -362,8 +355,22 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getReactiveConnection()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveRedisConnection getReactiveConnection() {
|
||||
throw new UnsupportedOperationException("Jedis does not support racative connections");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveRedisClusterConnection getReactiveClusterConnection() {
|
||||
throw new UnsupportedOperationException("Jedis does not support racative connections");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator#translateExceptionIfPossible(java.lang.RuntimeException)
|
||||
*/
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
return EXCEPTION_TRANSLATION.translate(ex);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,24 @@ public class LettuceConnectionFactory implements InitializingBean, DisposableBea
|
||||
return new LettuceClusterConnection((RedisClusterClient) client, clusterCommandExecutor);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory#getReactiveConnection()
|
||||
*/
|
||||
@Override
|
||||
public LettuceReactiveRedisConnection getReactiveConnection() {
|
||||
return new LettuceReactiveRedisConnection(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveRedisClusterConnection getReactiveClusterConnection() {
|
||||
if(!isClusterAware()) {
|
||||
throw new InvalidDataAccessApiUsageException("Cluster is not configured!");
|
||||
}
|
||||
|
||||
return new LettuceReactiveRedisClusterConnection((RedisClusterClient)client);
|
||||
}
|
||||
|
||||
public void initConnection() {
|
||||
|
||||
synchronized (this.connectionMonitor) {
|
||||
|
||||
@@ -345,6 +345,10 @@ abstract public class LettuceConverters extends Converters {
|
||||
return BYTES_LIST_TO_TUPLE_LIST_CONVERTER;
|
||||
}
|
||||
|
||||
public static Point geoCoordinatesToPoint(GeoCoordinates geoCoordinates) {
|
||||
return GEO_COORDINATE_TO_POINT_CONVERTER.convert(geoCoordinates);
|
||||
}
|
||||
|
||||
public static Converter<String, List<RedisClientInfo>> stringToRedisClientListConverter() {
|
||||
return new Converter<String, List<RedisClientInfo>>() {
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveClusterGeoCommands;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterGeoCommands extends LettuceReactiveGeoCommands
|
||||
implements ReactiveClusterGeoCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveGeoCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterGeoCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveClusterHashCommands;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterHashCommands extends LettuceReactiveHashCommands
|
||||
implements ReactiveClusterHashCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveHashCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterHashCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterHyperLogLogCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterHyperLogLogCommands extends LettuceReactiveHyperLogLogCommands
|
||||
implements ReactiveClusterHyperLogLogCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveHyperLogLogCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterHyperLogLogCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.BooleanResponse<PfMergeCommand>> pfMerge(Publisher<PfMergeCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null for PFMERGE");
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty for PFMERGE!");
|
||||
|
||||
List<ByteBuffer> keys = new ArrayList<>(command.getSourceKeys());
|
||||
keys.add(command.getKey());
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys.toArray(new ByteBuffer[keys.size()]))) {
|
||||
return super.pfMerge(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to same slot for PFMERGE in cluster mode."));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<PfCountCommand, Long>> pfCount(
|
||||
Publisher<PfCountCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getKeys(), "Keys must be null or empty for PFCOUNT!");
|
||||
|
||||
if (ClusterSlotHashUtil
|
||||
.isSameSlotForAllKeys(command.getKeys().toArray(new ByteBuffer[command.getKeys().size()]))) {
|
||||
return super.pfCount(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to same slot for PFCOUNT in cluster mode."));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.RedisSystemException;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterKeyCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisClusterNode;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.lambdaworks.redis.RedisException;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl.
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterKeyCommands extends LettuceReactiveKeyCommands
|
||||
implements ReactiveClusterKeyCommands {
|
||||
|
||||
private LettuceReactiveRedisClusterConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveKeyCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterKeyCommands(LettuceReactiveRedisClusterConnection connection) {
|
||||
|
||||
super(connection);
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<List<ByteBuffer>> keys(RedisClusterNode node, ByteBuffer pattern) {
|
||||
|
||||
return connection.execute(node, cmd -> {
|
||||
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
|
||||
Observable<List<ByteBuffer>> result = cmd.keys(pattern.array()).map(ByteBuffer::wrap).toList();
|
||||
return Flux.from(LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result));
|
||||
}).next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByteBuffer> randomKey(RedisClusterNode node) {
|
||||
|
||||
return connection.execute(node, cmd -> {
|
||||
|
||||
Observable<ByteBuffer> result = cmd.randomkey().map(ByteBuffer::wrap);
|
||||
return Flux.from(LettuceReactiveRedisConnection.<ByteBuffer> monoConverter().convert(result));
|
||||
}).next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#rename(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.BooleanResponse<RenameCommand>> rename(Publisher<RenameCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "key must not be null.");
|
||||
Assert.notNull(command.getNewName(), "NewName must not be null");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKey(), command.getNewName())) {
|
||||
return super.rename(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<Boolean> result = cmd.dump(command.getKey().array())
|
||||
.switchIfEmpty(Observable.error(new RedisSystemException("Cannot rename key that does not exist",
|
||||
new RedisException("ERR no such key."))))
|
||||
.concatMap(value -> cmd.restore(command.getNewName().array(), 0, value)
|
||||
.concatMap(res -> cmd.del(command.getKey().array())))
|
||||
.map(LettuceConverters.longToBooleanConverter()::convert);
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(result)
|
||||
.map(val -> new ReactiveRedisConnection.BooleanResponse<>(command, val));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#renameNX(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.BooleanResponse<RenameCommand>> renameNX(Publisher<RenameCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null.");
|
||||
Assert.notNull(command.getNewName(), "NewName must not be null");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKey(), command.getNewName())) {
|
||||
return super.renameNX(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<Boolean> result =
|
||||
|
||||
cmd.exists(command.getNewName().array()).concatMap(exists -> {
|
||||
|
||||
if (exists == 1) {
|
||||
return Observable.just(Boolean.FALSE);
|
||||
}
|
||||
|
||||
return cmd.dump(command.getKey().array())
|
||||
.switchIfEmpty(Observable.error(new RedisSystemException("Cannot rename key that does not exist",
|
||||
new RedisException("ERR no such key."))))
|
||||
.concatMap(value -> cmd.restore(command.getNewName().array(), 0, value)
|
||||
.concatMap(res -> cmd.del(command.getKey().array())))
|
||||
.map(LettuceConverters.longToBooleanConverter()::convert);
|
||||
|
||||
});
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(result)
|
||||
.map(val -> new ReactiveRedisConnection.BooleanResponse<>(command, val));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterListCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterListCommands extends LettuceReactiveListCommands
|
||||
implements ReactiveClusterListCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveListCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterListCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<PopResponse> bPop(Publisher<BPopCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
Assert.notNull(command.getDirection(), "Direction must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) {
|
||||
return super.bPop(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for BPOP command."));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.ByteBufferResponse<RPopLPushCommand>> rPopLPush(
|
||||
Publisher<RPopLPushCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDestination(), "Destination key must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKey(), command.getDestination())) {
|
||||
return super.rPopLPush(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<ByteBuffer> result = cmd.rpop(command.getKey().array())
|
||||
.concatMap(value -> cmd.lpush(command.getDestination().array(), value).map(x -> value)).map(ByteBuffer::wrap);
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveClusterNumberCommands;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterNumberCommands extends LettuceReactiveNumberCommands
|
||||
implements ReactiveClusterNumberCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveStringCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterNumberCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterSetCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterSetCommands extends LettuceReactiveSetCommands
|
||||
implements ReactiveClusterSetCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveSetCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterSetCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.MultiValueResponse<SUnionCommand, ByteBuffer>> sUnion(
|
||||
Publisher<SUnionCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) {
|
||||
return super.sUnion(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<List<ByteBuffer>> result = Observable
|
||||
.merge(command.getKeys().stream().map(key -> cmd.smembers(key.array())).collect(Collectors.toList()))
|
||||
.map(ByteBuffer::wrap).distinct().toList();
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<SUnionStoreCommand, Long>> sUnionStore(
|
||||
Publisher<SUnionStoreCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Source keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
List<ByteBuffer> keys = new ArrayList<>(command.getKeys());
|
||||
keys.add(command.getKey());
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
|
||||
return super.sUnionStore(Mono.just(command));
|
||||
}
|
||||
|
||||
return sUnion(Mono.just(SUnionCommand.keys(command.getKeys()))).next().flatMap(values -> {
|
||||
Observable<Long> result = cmd.sadd(command.getKey().array(),
|
||||
values.getOutput().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]));
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.NumericResponse<>(command, value));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.MultiValueResponse<SInterCommand, ByteBuffer>> sInter(
|
||||
Publisher<SInterCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) {
|
||||
return super.sInter(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<List<ByteBuffer>> sourceSet = cmd.smembers(command.getKeys().get(0).array()).map(ByteBuffer::wrap)
|
||||
.distinct().toList();
|
||||
|
||||
List<Observable<List<ByteBuffer>>> intersectingSets = new ArrayList<>();
|
||||
|
||||
for (int i = 1; i < command.getKeys().size(); i++) {
|
||||
intersectingSets.add(cmd.smembers(command.getKeys().get(i).array()).map(ByteBuffer::wrap).distinct().toList());
|
||||
}
|
||||
|
||||
Observable<List<ByteBuffer>> result = Observable.zip(sourceSet, Observable.merge(intersectingSets),
|
||||
(source, intersecting) -> {
|
||||
|
||||
source.retainAll(intersecting);
|
||||
return source;
|
||||
});
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<SInterStoreCommand, Long>> sInterStore(
|
||||
Publisher<SInterStoreCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Source keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
List<ByteBuffer> keys = new ArrayList<>(command.getKeys());
|
||||
keys.add(command.getKey());
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
|
||||
return super.sInterStore(Mono.just(command));
|
||||
}
|
||||
|
||||
return sInter(Mono.just(SInterCommand.keys(command.getKeys()))).next().flatMap(values -> {
|
||||
Observable<Long> result = cmd.sadd(command.getKey().array(),
|
||||
values.getOutput().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]));
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.NumericResponse<>(command, value));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.MultiValueResponse<SDiffCommand, ByteBuffer>> sDiff(
|
||||
Publisher<SDiffCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) {
|
||||
return super.sDiff(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<List<ByteBuffer>> sourceSet = cmd.smembers(command.getKeys().get(0).array()).map(ByteBuffer::wrap)
|
||||
.distinct().toList();
|
||||
|
||||
List<Observable<List<ByteBuffer>>> intersectingSets = new ArrayList<>();
|
||||
|
||||
for (int i = 1; i < command.getKeys().size(); i++) {
|
||||
intersectingSets.add(cmd.smembers(command.getKeys().get(i).array()).map(ByteBuffer::wrap).distinct().toList());
|
||||
}
|
||||
|
||||
Observable<List<ByteBuffer>> result = Observable.zip(sourceSet, Observable.merge(intersectingSets),
|
||||
(source, intersecting) -> {
|
||||
|
||||
source.removeAll(intersecting);
|
||||
return source;
|
||||
});
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.MultiValueResponse<>(command, value));
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<SDiffStoreCommand, Long>> sDiffStore(
|
||||
Publisher<SDiffStoreCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Source keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
List<ByteBuffer> keys = new ArrayList<>(command.getKeys());
|
||||
keys.add(command.getKey());
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
|
||||
return super.sDiffStore(Mono.just(command));
|
||||
}
|
||||
|
||||
return sDiff(Mono.just(SDiffCommand.keys(command.getKeys()))).next().flatMap(values -> {
|
||||
Observable<Long> result = cmd.sadd(command.getKey().array(),
|
||||
values.getOutput().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]));
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new ReactiveRedisConnection.NumericResponse<>(command, value));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.BooleanResponse<SMoveCommand>> sMove(Publisher<SMoveCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Source key must not be null!");
|
||||
Assert.notNull(command.getDestination(), "Destination key must not be null!");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKey(), command.getDestination())) {
|
||||
return super.sMove(Mono.just(command));
|
||||
}
|
||||
|
||||
Observable<Boolean> result = cmd.exists(command.getKey().array()).flatMap(nrKeys -> nrKeys == 0
|
||||
? Observable.empty() : cmd.sismember(command.getKey().array(), command.getValue().array()))
|
||||
.flatMap(exists -> {
|
||||
|
||||
if (!exists) {
|
||||
return Observable.just(Boolean.FALSE);
|
||||
}
|
||||
return cmd.sismember(command.getDestination().array(), command.getValue().array())
|
||||
.flatMap(existsInTarget -> {
|
||||
|
||||
Observable<Boolean> tmp = cmd.srem(command.getKey().array(), command.getValue().array())
|
||||
.map(nrRemoved -> nrRemoved > 0);
|
||||
if (!existsInTarget) {
|
||||
return tmp.flatMap(removed -> cmd.sadd(command.getDestination().array(), command.getValue().array())
|
||||
.map(LettuceConverters::toBoolean));
|
||||
}
|
||||
return tmp;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(result.defaultIfEmpty(Boolean.FALSE))
|
||||
.map(value -> new ReactiveRedisConnection.BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterStringCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterStringCommands extends LettuceReactiveStringCommands
|
||||
implements ReactiveClusterStringCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveStringCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterStringCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<BitOpCommand, Long>> bitOp(Publisher<BitOpCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
List<ByteBuffer> keys = new ArrayList<>(command.getKeys());
|
||||
keys.add(command.getDestinationKey());
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
|
||||
return super.bitOp(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for BITOP command."));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.BooleanResponse<MSetCommand>> mSetNX(Publisher<MSetCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeyValuePairs().keySet())) {
|
||||
return super.mSetNX(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for MSETNX command."));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
|
||||
import org.springframework.data.redis.connection.ReactiveClusterZSetCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveClusterZSetCommands extends LettuceReactiveZSetCommands
|
||||
implements ReactiveClusterZSetCommands {
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveSetCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveClusterZSetCommands(LettuceReactiveRedisConnection connection) {
|
||||
super(connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(
|
||||
Publisher<ZUnionStoreCommand> commands) {
|
||||
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty.");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getSourceKeys())) {
|
||||
return super.zUnionStore(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for ZUNIONSTORE command."));
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ReactiveRedisConnection.NumericResponse<ZInterStoreCommand, Long>> zInterStore(
|
||||
Publisher<ZInterStoreCommand> commands) {
|
||||
return getConnection().execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty.");
|
||||
|
||||
if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getSourceKeys())) {
|
||||
return super.zInterStore(Mono.just(command));
|
||||
}
|
||||
|
||||
return Mono
|
||||
.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for ZINTERSTORE command."));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.GeoResult;
|
||||
import org.springframework.data.geo.GeoResults;
|
||||
import org.springframework.data.geo.Metric;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.redis.connection.ReactiveGeoCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands;
|
||||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.lambdaworks.redis.GeoArgs;
|
||||
import com.lambdaworks.redis.GeoWithin;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveGeoCommands implements ReactiveGeoCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveGeoCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveGeoCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoAdd(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<GeoAddCommand, Long>> geoAdd(Publisher<GeoAddCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getGeoLocations(), "Locations must not be null!");
|
||||
|
||||
List<Object> values = new ArrayList<>();
|
||||
for (GeoLocation<ByteBuffer> location : command.getGeoLocations()) {
|
||||
|
||||
Assert.notNull(location.getName(), "Location.Name must not be null!");
|
||||
Assert.notNull(location.getPoint(), "Location.Point must not be null!");
|
||||
|
||||
values.add(location.getPoint().getX());
|
||||
values.add(location.getPoint().getY());
|
||||
values.add(location.getName().array());
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.geoadd(command.getKey().array(), values.toArray()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoDist(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<CommandResponse<GeoDistCommand, Distance>> geoDist(Publisher<GeoDistCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getFrom(), "From member must not be null!");
|
||||
Assert.notNull(command.getTo(), "To member must not be null!");
|
||||
|
||||
Metric metric = command.getMetric().isPresent() ? command.getMetric().get() : RedisGeoCommands.DistanceUnit.METERS;
|
||||
|
||||
GeoArgs.Unit geoUnit = LettuceConverters.toGeoArgsUnit(metric);
|
||||
Converter<Double, Distance> distanceConverter = LettuceConverters.distanceConverterForMetric(metric);
|
||||
|
||||
Observable<Distance> result = cmd
|
||||
.geodist(command.getKey().array(), command.getFrom().array(), command.getTo().array(), geoUnit)
|
||||
.map(distanceConverter::convert);
|
||||
|
||||
return LettuceReactiveRedisConnection.<Distance> monoConverter().convert(result)
|
||||
.map(value -> new CommandResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoHash(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<GeoHashCommand, String>> geoHash(Publisher<GeoHashCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getMembers(), "Members must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<String>> monoConverter()
|
||||
.convert(cmd.geohash(command.getKey().array(),
|
||||
command.getMembers().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoPos(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<GeoPosCommand, Point>> geoPos(Publisher<GeoPosCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getMembers(), "Members must not be null!");
|
||||
|
||||
Observable<List<Point>> result = cmd
|
||||
.geopos(command.getKey().array(),
|
||||
command.getMembers().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(LettuceConverters::geoCoordinatesToPoint).toList();
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<Point>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoRadius(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<CommandResponse<GeoRadiusCommand, GeoResults<GeoLocation<ByteBuffer>>>> geoRadius(
|
||||
Publisher<GeoRadiusCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getPoint(), "Point must not be null!");
|
||||
Assert.notNull(command.getDistance(), "Distance must not be null!");
|
||||
|
||||
GeoArgs geoArgs = command.getArgs().isPresent() ? LettuceConverters.toGeoArgs(command.getArgs().get()) : new GeoArgs();
|
||||
|
||||
Observable<GeoResults<GeoLocation<ByteBuffer>>> result = cmd
|
||||
.georadius(command.getKey().array(), command.getPoint().getX(), command.getPoint().getY(),
|
||||
command.getDistance().getValue(), LettuceConverters.toGeoArgsUnit(command.getDistance().getMetric()),
|
||||
geoArgs)
|
||||
.map(converter(command.getDistance().getMetric())::convert).toList().map(vals -> new GeoResults<>(vals));
|
||||
|
||||
return LettuceReactiveRedisConnection.<GeoResults<GeoLocation<ByteBuffer>>> monoConverter().convert(result)
|
||||
.map(value -> new CommandResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveGeoCommands#geoRadiusByMember(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<CommandResponse<GeoRadiusByMemberCommand, GeoResults<GeoLocation<ByteBuffer>>>> geoRadiusByMember(
|
||||
Publisher<GeoRadiusByMemberCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getMember(), "Member must not be null!");
|
||||
Assert.notNull(command.getDistance(), "Distance must not be null!");
|
||||
|
||||
GeoArgs geoArgs = command.getArgs().isPresent() ? LettuceConverters.toGeoArgs(command.getArgs().get()) : new GeoArgs();
|
||||
|
||||
Observable<GeoResults<GeoLocation<ByteBuffer>>> result = cmd
|
||||
.georadiusbymember(command.getKey().array(), command.getMember().array(), command.getDistance().getValue(),
|
||||
LettuceConverters.toGeoArgsUnit(command.getDistance().getMetric()), geoArgs)
|
||||
.map(converter(command.getDistance().getMetric())::convert).toList().map(vals -> new GeoResults<>(vals));
|
||||
|
||||
return LettuceReactiveRedisConnection.<GeoResults<GeoLocation<ByteBuffer>>> monoConverter().convert(result)
|
||||
.map(value -> new CommandResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
private Converter<GeoWithin<byte[]>, GeoResult<GeoLocation<ByteBuffer>>> converter(Metric metric) {
|
||||
|
||||
return (source) -> {
|
||||
|
||||
Point point = LettuceConverters.geoCoordinatesToPoint(source.coordinates);
|
||||
|
||||
return new GeoResult<>(new GeoLocation<>(ByteBuffer.wrap(source.member), point),
|
||||
new Distance(source.distance != null ? source.distance : 0D, metric));
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveHashCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveHashCommands implements ReactiveHashCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveHashCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveHashCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hSet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<HSetCommand>> hSet(Publisher<HSetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getFieldValueMap(), "FieldValueMap must not be null!");
|
||||
|
||||
Observable<Boolean> result = Observable.empty();
|
||||
|
||||
if (command.getFieldValueMap().size() == 1) {
|
||||
|
||||
Entry<ByteBuffer, ByteBuffer> entry = command.getFieldValueMap().entrySet().iterator().next();
|
||||
|
||||
result = ObjectUtils.nullSafeEquals(command.isUpsert(), Boolean.TRUE)
|
||||
? cmd.hset(command.getKey().array(), entry.getKey().array(), entry.getValue().array())
|
||||
: cmd.hsetnx(command.getKey().array(), entry.getKey().array(), entry.getValue().array());
|
||||
} else {
|
||||
|
||||
Map<byte[], byte[]> entries = command.getFieldValueMap().entrySet().stream()
|
||||
.collect(Collectors.toMap(e -> e.getKey().array(), e -> e.getValue().array()));
|
||||
|
||||
result = cmd.hmset(command.getKey().array(), entries).map(LettuceConverters::stringToBoolean);
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(result)
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hMGet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<HGetCommand, ByteBuffer>> hMGet(Publisher<HGetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getFields(), "Fields must not be null!");
|
||||
|
||||
Observable<List<ByteBuffer>> result = null;
|
||||
|
||||
if (command.getFields().size() == 1) {
|
||||
result = cmd.hget(command.getKey().array(), command.getFields().iterator().next().array()).map(ByteBuffer::wrap)
|
||||
.map(val -> val != null ? Collections.singletonList(val) : Collections.emptyList());
|
||||
} else {
|
||||
result = cmd
|
||||
.hmget(command.getKey().array(),
|
||||
command.getFields().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(val -> val != null ? ByteBuffer.wrap(val) : null).toList();
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hExists(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<HExistsCommand>> hExists(Publisher<HExistsCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getName(), "Name must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.hexists(command.getKey().array(), command.getField().array()))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hDel(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<HDelCommand, Long>> hDel(Publisher<HDelCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getFields(), "Fields must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.hdel(command.getKey().array(),
|
||||
command.getFields().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hLen(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> hLen(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Command.getKey() must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.hlen(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hKeys(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<KeyCommand, ByteBuffer>> hKeys(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
Observable<List<ByteBuffer>> result = cmd.hkeys(command.getKey().array()).map(ByteBuffer::wrap).toList();
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hKeys(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<KeyCommand, ByteBuffer>> hVals(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
Observable<List<ByteBuffer>> result = cmd.hvals(command.getKey().array()).map(ByteBuffer::wrap).toList();
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHashCommands#hGetAll(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<CommandResponse<KeyCommand, Map<ByteBuffer, ByteBuffer>>> hGetAll(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
Observable<Map<ByteBuffer, ByteBuffer>> result = cmd.hgetall(command.getKey().array()).map(val -> val.entrySet()
|
||||
.stream().collect(Collectors.toMap(e -> ByteBuffer.wrap(e.getKey()), e -> ByteBuffer.wrap(e.getValue()))));
|
||||
|
||||
return LettuceReactiveRedisConnection.<Map<ByteBuffer, ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new CommandResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveHyperLogLogCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveHyperLogLogCommands implements ReactiveHyperLogLogCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveHyperLogLogCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveHyperLogLogCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHyperLogLogCommands#pfAdd(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<PfAddCommand, Long>> pfAdd(Publisher<PfAddCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.pfadd(command.getKey().array(),
|
||||
command.getValues().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHyperLogLogCommands#pfCount(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<PfCountCommand, Long>> pfCount(Publisher<PfCountCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getKeys(), "Keys must not be empty for PFCOUNT.");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.pfcount(command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveHyperLogLogCommands#pfMerge(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<PfMergeCommand>> pfMerge(Publisher<PfMergeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null for PFMERGE.");
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null for PFMERGE.");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd
|
||||
.pfmerge(command.getKey().array(),
|
||||
command.getSourceKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(LettuceConverters::stringToBoolean))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.DataType;
|
||||
import org.springframework.data.redis.connection.ReactiveKeyCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveKeyCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveKeyCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#exists(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<KeyCommand>> exists(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<BooleanResponse<KeyCommand>> monoConverter()
|
||||
.convert(cmd.exists(command.getKey().array()).map(LettuceConverters.longToBooleanConverter()::convert)
|
||||
.map((value) -> new BooleanResponse<>(command, value)));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#type(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<CommandResponse<KeyCommand, DataType>> type(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<DataType> monoConverter()
|
||||
.convert(cmd.type(command.getKey().array()).map(LettuceConverters::toDataType))
|
||||
.map(respValue -> new CommandResponse<>(command, respValue));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#del(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<NumericResponse<KeyCommand, Long>> monoConverter()
|
||||
.convert(cmd.del(command.getKey().array()).map((value) -> new NumericResponse<>(command, value)));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mDel(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keysCollection) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(keysCollection).flatMap((keys) -> {
|
||||
|
||||
Assert.notEmpty(keys, "Keys must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<NumericResponse<List<ByteBuffer>, Long>> monoConverter()
|
||||
.convert(cmd
|
||||
.del(keys.stream().map(ByteBuffer::array).collect(Collectors.toList()).toArray(new byte[keys.size()][]))
|
||||
.map((value) -> new NumericResponse<>(keys, value)));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#keys(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<ByteBuffer, ByteBuffer>> keys(Publisher<ByteBuffer> patterns) {
|
||||
return connection.execute(cmd -> Flux.from(patterns).flatMap(pattern -> {
|
||||
|
||||
Assert.notNull(pattern, "Pattern must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd.keys(pattern.array()).map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(pattern, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#randomKey()
|
||||
*/
|
||||
@Override
|
||||
public Mono<ByteBuffer> randomKey() {
|
||||
return connection.execute(cmd -> LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.randomkey().map(ByteBuffer::wrap))).next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#rename(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<RenameCommand>> rename(Publisher<RenameCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getNewName(), "New name must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(
|
||||
cmd.rename(command.getKey().array(), command.getNewName().array()).map(LettuceConverters::stringToBoolean))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#rename(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<RenameCommand>> renameNX(Publisher<RenameCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getNewName(), "New name must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.renamenx(command.getKey().array(), command.getNewName().array()))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.redis.connection.ReactiveListCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
|
||||
import org.springframework.data.redis.connection.RedisListCommands.Position;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveListCommands implements ReactiveListCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveListCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveListCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lPush(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<PushCommand, Long>> push(Publisher<PushCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notEmpty(command.getValues(), "Values must not be null or empty!");
|
||||
|
||||
if (!command.getUpsert() && command.getValues().size() > 1) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("%s PUSHX only allows one value!", command.getDirection()));
|
||||
}
|
||||
|
||||
byte[][] values = command.getValues().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]);
|
||||
|
||||
Observable<Long> pushResult = null;
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(Direction.RIGHT, command.getDirection())) {
|
||||
pushResult = command.getUpsert() ? cmd.rpush(command.getKey().array(), values)
|
||||
: cmd.rpushx(command.getKey().array(), values[0]);
|
||||
} else {
|
||||
pushResult = command.getUpsert() ? cmd.lpush(command.getKey().array(), values)
|
||||
: cmd.lpushx(command.getKey().array(), values[0]);
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(pushResult)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lLen(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> lLen(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.llen(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lRange(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<RangeCommand, ByteBuffer>> lRange(Publisher<RangeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd
|
||||
.lrange(command.getKey().array(), command.getRange().getLowerBound(), command.getRange().getUpperBound())
|
||||
.map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lTrim(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<RangeCommand>> lTrim(Publisher<RangeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd
|
||||
.ltrim(command.getKey().array(), command.getRange().getLowerBound(), command.getRange().getUpperBound())
|
||||
.map(LettuceConverters::stringToBoolean))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lIndex(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<LIndexCommand>> lIndex(Publisher<LIndexCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getIndex(), "Index value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.lindex(command.getKey().array(), command.getIndex()).map(ByteBuffer::wrap))
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lInsert(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.notNull(command.getPivot(), "Pivot must not be null!");
|
||||
Assert.notNull(command.getPosition(), "Position must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.linsert(command.getKey().array(), Position.BEFORE.equals(command.getPosition()),
|
||||
command.getPivot().array(), command.getValue().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lSet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<LSetCommand>> lSet(Publisher<LSetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> {
|
||||
|
||||
return Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "value must not be null!");
|
||||
Assert.notNull(command.getIndex(), "Index must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.lset(command.getKey().array(), command.getIndex(), command.getValue().array())
|
||||
.map(LettuceConverters::stringToBoolean))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#lRem(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<LRemCommand, Long>> lRem(Publisher<LRemCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.notNull(command.getCount(), "Count must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.lrem(command.getKey().array(), command.getCount(), command.getValue().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#rPop(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<PopCommand>> pop(Publisher<PopCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDirection(), "Direction must not be null!");
|
||||
|
||||
Observable<byte[]> popResult = ObjectUtils.nullSafeEquals(Direction.RIGHT, command.getDirection())
|
||||
? cmd.rpop(command.getKey().array()) : cmd.lpop(command.getKey().array());
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter().convert(popResult.map(ByteBuffer::wrap))
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#bPop(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<PopResponse> bPop(Publisher<BPopCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
Assert.notNull(command.getDirection(), "Direction must not be null!");
|
||||
|
||||
byte[][] keys = command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]);
|
||||
long timeout = command.getTimeout().get(ChronoUnit.SECONDS);
|
||||
|
||||
Observable<PopResult> mappedObservable = (ObjectUtils.nullSafeEquals(Direction.RIGHT, command.getDirection())
|
||||
? cmd.brpop(timeout, keys) : cmd.blpop(timeout, keys))
|
||||
.map(kv -> Arrays.asList(ByteBuffer.wrap(kv.key), ByteBuffer.wrap(kv.value)))
|
||||
.map(val -> new PopResult(val));
|
||||
|
||||
return LettuceReactiveRedisConnection.<PopResult> monoConverter().convert(mappedObservable)
|
||||
.map(value -> new PopResponse(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#rPopLPush(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<RPopLPushCommand>> rPopLPush(Publisher<RPopLPushCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDestination(), "Destination key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.rpoplpush(command.getKey().array(), command.getDestination().array()).map(ByteBuffer::wrap))
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveListCommands#bRPopLPush(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<BRPopLPushCommand>> bRPopLPush(Publisher<BRPopLPushCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDestination(), "Destination key must not be null!");
|
||||
Assert.notNull(command.getTimeout(), "Timeout must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.brpoplpush(command.getTimeout().get(ChronoUnit.SECONDS), command.getKey().array(),
|
||||
command.getDestination().array()).map(ByteBuffer::wrap))
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveNumberCommands;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveNumberCommands implements ReactiveNumberCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveStringCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveNumberCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveNumberCommands#incr(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> incr(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.incr(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveNumberCommands#incrBy(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public <T extends Number> Flux<NumericResponse<IncrByCommand<T>, T>> incrBy(Publisher<IncrByCommand<T>> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value for INCRBY must not be null.");
|
||||
|
||||
T incrBy = command.getValue();
|
||||
|
||||
Observable<? extends Number> result = null;
|
||||
if (incrBy instanceof Double || incrBy instanceof Float) {
|
||||
result = cmd.incrbyfloat(command.getKey().array(), incrBy.doubleValue());
|
||||
} else {
|
||||
result = cmd.incrby(command.getKey().array(), incrBy.longValue());
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<T> monoConverter()
|
||||
.convert(result.map(val -> NumberUtils.convertNumberToTargetClass(val, incrBy.getClass())))
|
||||
.map(res -> new NumericResponse<>(command, res));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveNumberCommands#decr(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> decr(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.decr(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveNumberCommands#decrBy(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public <T extends Number> Flux<NumericResponse<DecrByCommand<T>, T>> decrBy(Publisher<DecrByCommand<T>> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value for DECRBY must not be null.");
|
||||
|
||||
T decrBy = command.getValue();
|
||||
|
||||
Observable<? extends Number> result = null;
|
||||
if (decrBy instanceof Double || decrBy instanceof Float) {
|
||||
result = cmd.incrbyfloat(command.getKey().array(), decrBy.doubleValue() * (-1.0D));
|
||||
} else {
|
||||
result = cmd.decrby(command.getKey().array(), decrBy.longValue());
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<T> monoConverter()
|
||||
.convert(result.map(val -> NumberUtils.convertNumberToTargetClass(val, decrBy.getClass())))
|
||||
.map(res -> new NumericResponse<>(command, res));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveNumberCommands#hIncrBy(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public <T extends Number> Flux<NumericResponse<HIncrByCommand<T>, T>> hIncrBy(Publisher<HIncrByCommand<T>> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
T incrBy = command.getValue();
|
||||
|
||||
Observable<? extends Number> result = null;
|
||||
|
||||
if (incrBy instanceof Double || incrBy instanceof Float) {
|
||||
result = cmd.hincrbyfloat(command.getKey().array(), command.getField().array(), incrBy.doubleValue());
|
||||
} else {
|
||||
result = cmd.hincrby(command.getKey().array(), command.getField().array(), incrBy.longValue());
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<T> monoConverter()
|
||||
.convert(result.map(val -> NumberUtils.convertNumberToTargetClass(val, incrBy.getClass())))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2016. the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import org.springframework.data.redis.connection.ReactiveRedisClusterConnection;
|
||||
import org.springframework.data.redis.connection.RedisNode;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.lambdaworks.redis.api.rx.RedisReactiveCommands;
|
||||
import com.lambdaworks.redis.cluster.RedisClusterClient;
|
||||
import com.lambdaworks.redis.cluster.api.StatefulRedisClusterConnection;
|
||||
import com.lambdaworks.redis.cluster.api.rx.RedisClusterReactiveCommands;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveRedisClusterConnection extends LettuceReactiveRedisConnection
|
||||
implements ReactiveRedisClusterConnection {
|
||||
|
||||
public LettuceReactiveRedisClusterConnection(RedisClusterClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterKeyCommands keyCommands() {
|
||||
return new LettuceReactiveClusterKeyCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterListCommands listCommands() {
|
||||
return new LettuceReactiveClusterListCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterSetCommands setCommands() {
|
||||
return new LettuceReactiveClusterSetCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterZSetCommands zSetCommands() {
|
||||
return new LettuceReactiveClusterZSetCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterHyperLogLogCommands hyperLogLogCommands() {
|
||||
return new LettuceReactiveClusterHyperLogLogCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterStringCommands stringCommands() {
|
||||
return new LettuceReactiveClusterStringCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterGeoCommands geoCommands() {
|
||||
return new LettuceReactiveClusterGeoCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterHashCommands hashCommands() {
|
||||
return new LettuceReactiveClusterHashCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LettuceReactiveClusterNumberCommands numberCommands() {
|
||||
return new LettuceReactiveClusterNumberCommands(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback
|
||||
* @return
|
||||
*/
|
||||
public <T> Flux<T> execute(RedisNode node, LettuceReactiveCallback<T> callback) {
|
||||
|
||||
try {
|
||||
Assert.notNull(callback, "ReactiveCallback must not be null!");
|
||||
Assert.notNull(node, "Node must not be null!");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Flux.error(e);
|
||||
}
|
||||
|
||||
return Flux.defer(() -> callback.doWithCommands(getCommands(node))).onErrorResumeWith(translateExeception());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StatefulRedisClusterConnection<byte[], byte[]> getConnection() {
|
||||
|
||||
Assert.isInstanceOf(StatefulRedisClusterConnection.class, super.getConnection(),
|
||||
"Connection needs to be instance of StatefulRedisClusterConnection");
|
||||
|
||||
return (StatefulRedisClusterConnection) super.getConnection();
|
||||
}
|
||||
|
||||
protected RedisClusterReactiveCommands<byte[], byte[]> getCommands() {
|
||||
return getConnection().reactive();
|
||||
}
|
||||
|
||||
protected RedisReactiveCommands<byte[], byte[]> getCommands(RedisNode node) {
|
||||
|
||||
if (!(getConnection() instanceof StatefulRedisClusterConnection)) {
|
||||
throw new IllegalArgumentException("o.O connection needs to be cluster compatible " + getConnection());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(node.getId())) {
|
||||
return ((StatefulRedisClusterConnection) getConnection()).getConnection(node.getId()).reactive();
|
||||
}
|
||||
|
||||
return ((StatefulRedisClusterConnection) getConnection()).getConnection(node.getHost(), node.getPort()).reactive();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.redis.connection.*;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.lambdaworks.redis.AbstractRedisClient;
|
||||
import com.lambdaworks.redis.RedisClient;
|
||||
import com.lambdaworks.redis.api.StatefulConnection;
|
||||
import com.lambdaworks.redis.api.StatefulRedisConnection;
|
||||
import com.lambdaworks.redis.cluster.RedisClusterClient;
|
||||
import com.lambdaworks.redis.cluster.api.StatefulRedisClusterConnection;
|
||||
import com.lambdaworks.redis.cluster.api.rx.RedisClusterReactiveCommands;
|
||||
import com.lambdaworks.redis.codec.ByteArrayCodec;
|
||||
import com.lambdaworks.redis.codec.RedisCodec;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveRedisConnection implements ReactiveRedisConnection {
|
||||
|
||||
private StatefulConnection<ByteBuffer, ByteBuffer> connection;
|
||||
|
||||
private static final RedisCodec<byte[], byte[]> CODEC = new ByteArrayCodec();
|
||||
|
||||
public LettuceReactiveRedisConnection(AbstractRedisClient client) {
|
||||
|
||||
Assert.notNull(client, "RedisClient must not be null!");
|
||||
|
||||
if (client instanceof RedisClient) {
|
||||
connection = ((RedisClient) client).connect(CODEC);
|
||||
} else if (client instanceof RedisClusterClient) {
|
||||
connection = ((RedisClusterClient) client).connect(CODEC);
|
||||
} else {
|
||||
throw new InvalidDataAccessResourceUsageException(
|
||||
String.format("Cannot use client of type %s", client.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection#keyCommands()
|
||||
*/
|
||||
@Override
|
||||
public ReactiveKeyCommands keyCommands() {
|
||||
return new LettuceReactiveKeyCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveStringCommands stringCommands() {
|
||||
return new LettuceReactiveStringCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveNumberCommands numberCommands() {
|
||||
return new LettuceReactiveNumberCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveListCommands listCommands() {
|
||||
return new LettuceReactiveListCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveSetCommands setCommands() {
|
||||
return new LettuceReactiveSetCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveZSetCommands zSetCommands() {
|
||||
return new LettuceReactiveZSetCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHashCommands hashCommands() {
|
||||
return new LettuceReactiveHashCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveGeoCommands geoCommands() {
|
||||
return new LettuceReactiveGeoCommands(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHyperLogLogCommands hyperLogLogCommands() {
|
||||
return new LettuceReactiveHyperLogLogCommands(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback
|
||||
* @return
|
||||
*/
|
||||
public <T> Flux<T> execute(LettuceReactiveCallback<T> callback) {
|
||||
return Flux.defer(() -> callback.doWithCommands(getCommands())).onErrorResumeWith(translateExeception());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
connection.close();
|
||||
}
|
||||
|
||||
protected StatefulConnection<byte[], byte[]> getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected RedisClusterReactiveCommands<byte[], byte[]> getCommands() {
|
||||
|
||||
if (connection instanceof StatefulRedisConnection) {
|
||||
return ((StatefulRedisConnection<byte[], byte[]>) connection).reactive();
|
||||
} else if (connection instanceof StatefulRedisClusterConnection) {
|
||||
return ((StatefulRedisClusterConnection<byte[], byte[]>) connection).reactive();
|
||||
}
|
||||
|
||||
throw new RuntimeException("o.O unknown connection type " + connection);
|
||||
}
|
||||
|
||||
<T> Function<Throwable, Publisher<? extends T>> translateExeception() {
|
||||
|
||||
return throwable -> {
|
||||
|
||||
if (throwable instanceof RuntimeException) {
|
||||
|
||||
DataAccessException convertedException = null;
|
||||
if (throwable instanceof RuntimeException) {
|
||||
convertedException = LettuceConverters.exceptionConverter().convert((RuntimeException) throwable);
|
||||
}
|
||||
return Flux.error(convertedException != null ? convertedException : throwable);
|
||||
}
|
||||
|
||||
return Flux.error(throwable);
|
||||
};
|
||||
}
|
||||
|
||||
interface LettuceReactiveCallback<T> {
|
||||
Publisher<T> doWithCommands(RedisClusterReactiveCommands<byte[], byte[]> cmd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveSetCommands;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveSetCommands implements ReactiveSetCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveSetCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveSetCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sAdd(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SAddCommand, Long>> sAdd(Publisher<SAddCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValues(), "Values must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.sadd(command.getKey().array(),
|
||||
command.getValues().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sRem(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SRemCommand, Long>> sRem(Publisher<SRemCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValues(), "Values must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.srem(command.getKey().array(),
|
||||
command.getValues().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sPop(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<KeyCommand>> sPop(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.spop(command.getKey().array()).map(ByteBuffer::wrap))
|
||||
.map(value -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sMove(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SMoveCommand>> sMove(Publisher<SMoveCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getDestination(), "Destination key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.smove(command.getKey().array(), command.getDestination().array(), command.getValue().array()))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sCard(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> sCard(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.scard(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sIsMember(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SIsMemberCommand>> sIsMember(Publisher<SIsMemberCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.sismember(command.getKey().array(), command.getValue().array()))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInter(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<SInterCommand, ByteBuffer>> sInter(Publisher<SInterCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd.sinter(command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInterStore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SInterStoreCommand, Long>> sInterStore(Publisher<SInterStoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.sinterstore(command.getKey().array(),
|
||||
command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInter(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<SUnionCommand, ByteBuffer>> sUnion(Publisher<SUnionCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd.sunion(command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInterStore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SUnionStoreCommand, Long>> sUnionStore(Publisher<SUnionStoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.sunionstore(command.getKey().array(),
|
||||
command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInter(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<SDiffCommand, ByteBuffer>> sDiff(Publisher<SDiffCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd.sdiff(command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]))
|
||||
.map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sInterStore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SDiffStoreCommand, Long>> sDiffStore(Publisher<SDiffStoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKeys(), "Keys must not be null!");
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.sdiffstore(command.getKey().array(),
|
||||
command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sMembers(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<KeyCommand, ByteBuffer>> sMembers(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(cmd.smembers(command.getKey().array()).map(ByteBuffer::wrap).toList())
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveSetCommands#sRandMembers(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<SRandMembersCommand, ByteBuffer>> sRandMember(
|
||||
Publisher<SRandMembersCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
boolean singleElement = !command.getCount().isPresent() || command.getCount().get().equals(1L);
|
||||
|
||||
Observable<List<ByteBuffer>> result = singleElement
|
||||
? cmd.srandmember(command.getKey().array()).map(ByteBuffer::wrap).map(Collections::singletonList)
|
||||
: cmd.srandmember(command.getKey().array(), command.getCount().get()).map(ByteBuffer::wrap).toList();
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.BooleanResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.ByteBufferResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveStringCommands;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.lambdaworks.redis.SetArgs;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveStringCommands implements ReactiveStringCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveStringCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveStringCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#mGet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<List<ByteBuffer>, ByteBuffer>> mGet(Publisher<List<ByteBuffer>> keyCollections) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(keyCollections).flatMap((keys) -> {
|
||||
|
||||
Assert.notNull(keys, "Keys must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<MultiValueResponse<List<ByteBuffer>, ByteBuffer>> monoConverter()
|
||||
.convert(cmd
|
||||
.mget(keys.stream().map(ByteBuffer::array).collect(Collectors.toList()).toArray(new byte[keys.size()][]))
|
||||
.map((value) -> value != null ? ByteBuffer.wrap(value) : ByteBuffer.allocate(0)).toList()
|
||||
.map((values) -> new MultiValueResponse<>(keys, values)));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#set(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SetCommand>> set(Publisher<SetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
SetArgs args = null;
|
||||
|
||||
if (command.getExpiration().isPresent() || command.getOption().isPresent()) {
|
||||
args = LettuceConverters.toSetArgs(command.getExpiration().isPresent() ? command.getExpiration().get() : null,
|
||||
command.getOption().isPresent() ? command.getOption().get() : null);
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(
|
||||
|
||||
args != null ? cmd.set(command.getKey().array(), command.getValue().array(), args)
|
||||
: cmd.set(command.getKey().array(), command.getValue().array()).map(LettuceConverters::stringToBoolean))
|
||||
.map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#getSet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<SetCommand>> getSet(Publisher<SetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
if (command.getExpiration().isPresent() || command.getOption().isPresent()) {
|
||||
throw new IllegalArgumentException("Command must not define exipiration nor option for GETSET.");
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBufferResponse<SetCommand>> monoConverter()
|
||||
.convert(cmd.getset(command.getKey().array(), command.getValue().array())
|
||||
.map((value) -> new ByteBufferResponse<>(command, ByteBuffer.wrap(value)))
|
||||
.defaultIfEmpty(new ByteBufferResponse<>(command, ByteBuffer.allocate(0))));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#get(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<KeyCommand>> get(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap((command) -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<ByteBuffer> monoConverter()
|
||||
.convert(cmd.get(command.getKey().array()).map(ByteBuffer::wrap))
|
||||
.map((value) -> new ByteBufferResponse<>(command, value))
|
||||
.defaultIfEmpty(new ByteBufferResponse<>(command, ByteBuffer.allocate(0)));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#setNX(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SetCommand>> setNX(Publisher<SetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.setnx(command.getKey().array(), command.getValue().array()))
|
||||
.map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#setEX(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SetCommand>> setEX(Publisher<SetCommand> commands) {
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.isTrue(command.getExpiration().isPresent(), "Expiration time must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<String> monoConverter()
|
||||
.convert(cmd.setex(command.getKey().array(), command.getExpiration().get().getExpirationTimeInSeconds(),
|
||||
command.getValue().array()))
|
||||
.map(LettuceConverters::stringToBoolean).map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#pSetEX(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SetCommand>> pSetEX(Publisher<SetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.isTrue(command.getExpiration().isPresent(), "Expiration time must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<String> monoConverter()
|
||||
.convert(cmd.psetex(command.getKey().array(), command.getExpiration().get().getExpirationTimeInMilliseconds(),
|
||||
command.getValue().array()))
|
||||
.map(LettuceConverters::stringToBoolean).map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#mSet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<MSetCommand>> mSet(Publisher<MSetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getKeyValuePairs(), "Pairs must not be null or empty!");
|
||||
|
||||
Map<byte[], byte[]> map = new LinkedHashMap<>();
|
||||
command.getKeyValuePairs().entrySet().forEach(entry -> map.put(entry.getKey().array(), entry.getValue().array()));
|
||||
|
||||
return LettuceReactiveRedisConnection.<String> monoConverter().convert(cmd.mset(map))
|
||||
.map(LettuceConverters::stringToBoolean).map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#mSet(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<MSetCommand>> mSetNX(Publisher<MSetCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notEmpty(command.getKeyValuePairs(), "Pairs must not be null or empty!");
|
||||
|
||||
Map<byte[], byte[]> map = new LinkedHashMap<>();
|
||||
command.getKeyValuePairs().entrySet().forEach(entry -> map.put(entry.getKey().array(), entry.getValue().array()));
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter().convert(cmd.msetnx(map))
|
||||
.map((value) -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#append(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<AppendCommand, Long>> append(Publisher<AppendCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.append(command.getKey().array(), command.getValue().array()))
|
||||
.map((value) -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#getRange(org.reactivestreams.Publisher, java.util.function.Supplier, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<ByteBufferResponse<RangeCommand>> getRange(Publisher<RangeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
Range<Long> range = command.getRange();
|
||||
|
||||
return LettuceReactiveRedisConnection
|
||||
.<ByteBuffer> monoConverter().convert(cmd
|
||||
.getrange(command.getKey().array(), range.getLowerBound(), range.getUpperBound()).map(ByteBuffer::wrap))
|
||||
.map((value) -> new ByteBufferResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#setRange(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<SetRangeCommand, Long>> setRange(Publisher<SetRangeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.notNull(command.getOffset(), "Offset must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.setrange(command.getKey().array(), command.getOffset(), command.getValue().array()))
|
||||
.map((value) -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#getBit(org.reactivestreams.Publisher, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<GetBitCommand>> getBit(Publisher<GetBitCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getOffset(), "Offset must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.getbit(command.getKey().array(), command.getOffset()).map(LettuceConverters::toBoolean))
|
||||
.map(value -> new BooleanResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#setBit(org.reactivestreams.Publisher, java.util.function.Supplier, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<BooleanResponse<SetBitCommand>> setBit(Publisher<SetBitCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
Assert.notNull(command.getOffset(), "Offset must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Boolean> monoConverter()
|
||||
.convert(cmd.setbit(command.getKey().array(), command.getOffset(), command.getValue() ? 1 : 0)
|
||||
.map(LettuceConverters::toBoolean))
|
||||
.map(respValue -> new BooleanResponse<>(command, respValue));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#bitCount(org.reactivestreams.Publisher, java.util.function.Supplier, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<BitCountCommand, Long>> bitCount(Publisher<BitCountCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
Range<Long> range = command.getRange();
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(range != null ? cmd.bitcount(command.getKey().array(), range.getLowerBound(), range.getUpperBound())
|
||||
: cmd.bitcount(command.getKey().array()))
|
||||
.map(responseValue -> new NumericResponse<>(command, responseValue));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#bitOp(org.reactivestreams.Publisher, java.util.function.Supplier, java.util.function.Supplier)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<BitOpCommand, Long>> bitOp(Publisher<BitOpCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getDestinationKey(), "DestinationKey must not be null!");
|
||||
Assert.notEmpty(command.getKeys(), "Keys must not be null or empty");
|
||||
|
||||
Observable<Long> result = null;
|
||||
byte[] destinationKey = command.getDestinationKey().array();
|
||||
byte[][] sourceKeys = command.getKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]);
|
||||
|
||||
switch (command.getBitOp()) {
|
||||
case AND:
|
||||
result = cmd.bitopAnd(destinationKey, sourceKeys);
|
||||
break;
|
||||
case OR:
|
||||
result = cmd.bitopOr(destinationKey, sourceKeys);
|
||||
break;
|
||||
case XOR:
|
||||
result = cmd.bitopXor(destinationKey, sourceKeys);
|
||||
break;
|
||||
case NOT:
|
||||
|
||||
Assert.isTrue(sourceKeys.length == 1, "BITOP NOT does not allow more than 1 source key.");
|
||||
|
||||
result = cmd.bitopNot(destinationKey, sourceKeys[0]);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Unknown BITOP '%s'.", command.getBitOp()));
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveStringCommands#strLen(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> strLen(Publisher<KeyCommand> commands) {
|
||||
return connection.execute(cmd -> {
|
||||
|
||||
return Flux.from(commands).flatMap(command -> {
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.strlen(command.getKey().array()))
|
||||
.map(respValue -> new NumericResponse<>(command, respValue));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.connection.lettuce;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.redis.connection.DefaultTuple;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.MultiValueResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
|
||||
import org.springframework.data.redis.connection.ReactiveZSetCommands;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
|
||||
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.lambdaworks.redis.ScoredValue;
|
||||
import com.lambdaworks.redis.ZAddArgs;
|
||||
import com.lambdaworks.redis.ZStoreArgs;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
|
||||
|
||||
private final LettuceReactiveRedisConnection connection;
|
||||
|
||||
/**
|
||||
* Create new {@link LettuceReactiveSetCommands}.
|
||||
*
|
||||
* @param connection must not be {@literal null}.
|
||||
*/
|
||||
public LettuceReactiveZSetCommands(LettuceReactiveRedisConnection connection) {
|
||||
|
||||
Assert.notNull(connection, "Connection must not be null!");
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zAdd(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Flux<NumericResponse<ZAddCommand, Number>> zAdd(Publisher<ZAddCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notEmpty(command.getTuples(), "Tuples must not be empty or null.");
|
||||
|
||||
ZAddArgs args = null;
|
||||
|
||||
if (command.getIncr().isPresent() || command.getUpsert().isPresent() || command.getReturnTotalChanged().isPresent()) {
|
||||
|
||||
if (command.getIncr().isPresent() && ObjectUtils.nullSafeEquals(command.getIncr().get(), Boolean.TRUE)) {
|
||||
|
||||
if (command.getTuples().size() > 1) {
|
||||
throw new IllegalArgumentException("ZADD INCR must not contain more than one tuple.");
|
||||
}
|
||||
|
||||
Tuple tuple = command.getTuples().iterator().next();
|
||||
|
||||
return LettuceReactiveRedisConnection.<Double> monoConverter()
|
||||
.convert(cmd.zaddincr(command.getKey().array(), tuple.getScore(), tuple.getValue()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}
|
||||
|
||||
if (command.getReturnTotalChanged().isPresent() && ObjectUtils.nullSafeEquals(command.getReturnTotalChanged().get(), Boolean.TRUE)) {
|
||||
args = ZAddArgs.Builder.ch();
|
||||
}
|
||||
|
||||
if (command.getUpsert().isPresent()) {
|
||||
|
||||
if (command.getUpsert().get().equals(Boolean.TRUE)) {
|
||||
args = ZAddArgs.Builder.nx();
|
||||
} else {
|
||||
args = ZAddArgs.Builder.xx();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScoredValue<byte[]>[] values = (ScoredValue<byte[]>[]) command.getTuples().stream()
|
||||
.map(tuple -> new ScoredValue<byte[]>(tuple.getScore(), tuple.getValue()))
|
||||
.toArray(size -> new ScoredValue[size]);
|
||||
|
||||
Observable<Long> result = args == null ? cmd.zadd(command.getKey().array(), values)
|
||||
: cmd.zadd(command.getKey().array(), args, values);
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRem(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZRemCommand, Long>> zRem(Publisher<ZRemCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notEmpty(command.getValues(), "Values must not be null or empty!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter()
|
||||
.convert(cmd.zrem(command.getKey().array(),
|
||||
command.getValues().stream().map(ByteBuffer::array).toArray(size -> new byte[size][])))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zIncrBy(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZIncrByCommand, Double>> zIncrBy(Publisher<ZIncrByCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Member must not be null!");
|
||||
Assert.notNull(command.getIncrement(), "Increment value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Double> monoConverter()
|
||||
.convert(
|
||||
cmd.zincrby(command.getKey().array(), command.getIncrement().doubleValue(), command.getValue().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRank(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZRankCommand, Long>> zRank(Publisher<ZRankCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
Observable<Long> result = ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)
|
||||
? cmd.zrank(command.getKey().array(), command.getValue().array())
|
||||
: cmd.zrevrank(command.getKey().array(), command.getValue().array());
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRange(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<ZRangeCommand, Tuple>> zRange(Publisher<ZRangeCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
Observable<List<Tuple>> result = Observable.empty();
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
|
||||
if (command.getWithScores().isPresent() && ObjectUtils.nullSafeEquals(command.getWithScores().get(), Boolean.TRUE)) {
|
||||
|
||||
result = cmd.zrangeWithScores(command.getKey().array(), command.getRange().getLowerBound(),
|
||||
command.getRange().getUpperBound()).map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
} else {
|
||||
|
||||
result = cmd
|
||||
.zrange(command.getKey().array(), command.getRange().getLowerBound(), command.getRange().getUpperBound())
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
}
|
||||
} else {
|
||||
if (command.getWithScores().isPresent() && ObjectUtils.nullSafeEquals(command.getWithScores().get(), Boolean.TRUE)) {
|
||||
result = cmd.zrevrangeWithScores(command.getKey().array(), command.getRange().getLowerBound(),
|
||||
command.getRange().getUpperBound()).map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
} else {
|
||||
|
||||
result = cmd
|
||||
.zrevrange(command.getKey().array(), command.getRange().getLowerBound(),
|
||||
command.getRange().getUpperBound())
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<Tuple>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRange(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<ZRangeByScoreCommand, Tuple>> zRangeByScore(Publisher<ZRangeByScoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
Object lowerBound = AgrumentConverters.lowerBoundArgOf(command.getRange());
|
||||
Object upperBound = AgrumentConverters.upperBoundArgOf(command.getRange());
|
||||
|
||||
boolean requiresStringConversion = lowerBound instanceof String || upperBound instanceof String;
|
||||
|
||||
boolean isLimited = command.getLimit().isPresent();
|
||||
|
||||
Observable<List<Tuple>> result = Observable.empty();
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
|
||||
if (command.getWithScores().isPresent() && ObjectUtils.nullSafeEquals(command.getWithScores().get(), Boolean.TRUE)) {
|
||||
|
||||
if (!isLimited) {
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrangebyscoreWithScores(command.getKey().array(), lowerBound.toString(), upperBound.toString())
|
||||
: cmd.zrangebyscoreWithScores(command.getKey().array(), (Double) lowerBound, (Double) upperBound))
|
||||
.map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
} else {
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrangebyscoreWithScores(command.getKey().array(), lowerBound.toString(), upperBound.toString(),
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount())
|
||||
: cmd.zrangebyscoreWithScores(command.getKey().array(), (Double) lowerBound, (Double) upperBound,
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount()))
|
||||
.map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!isLimited) {
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrangebyscore(command.getKey().array(), lowerBound.toString(), upperBound.toString())
|
||||
: cmd.zrangebyscore(command.getKey().array(), (Double) lowerBound, (Double) upperBound))
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
} else {
|
||||
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrangebyscore(command.getKey().array(), lowerBound.toString(), upperBound.toString(),
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount())
|
||||
: cmd.zrangebyscore(command.getKey().array(), (Double) lowerBound, (Double) upperBound,
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount()))
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (command.getWithScores().isPresent() && ObjectUtils.nullSafeEquals(command.getWithScores().get(), Boolean.TRUE)) {
|
||||
|
||||
if (!isLimited) {
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrevrangebyscoreWithScores(command.getKey().array(), lowerBound.toString(), upperBound.toString())
|
||||
: cmd.zrevrangebyscoreWithScores(command.getKey().array(), (Double) lowerBound, (Double) upperBound))
|
||||
.map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
} else {
|
||||
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrevrangebyscoreWithScores(command.getKey().array(), lowerBound.toString(), upperBound.toString(),
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount())
|
||||
: cmd.zrevrangebyscoreWithScores(command.getKey().array(), (Double) lowerBound, (Double) upperBound,
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount()))
|
||||
.map(sc -> (Tuple) new DefaultTuple(sc.value, sc.score)).toList();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!isLimited) {
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrevrangebyscore(command.getKey().array(), lowerBound.toString(), upperBound.toString())
|
||||
: cmd.zrevrangebyscore(command.getKey().array(), (Double) lowerBound, (Double) upperBound))
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
} else {
|
||||
|
||||
result = (requiresStringConversion
|
||||
? cmd.zrevrangebyscore(command.getKey().array(), lowerBound.toString(), upperBound.toString(),
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount())
|
||||
: cmd.zrevrangebyscore(command.getKey().array(), (Double) lowerBound, (Double) upperBound,
|
||||
command.getLimit().get().getOffset(), command.getLimit().get().getCount()))
|
||||
.map(value -> (Tuple) new DefaultTuple(value, Double.NaN)).toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<Tuple>> monoConverter().convert(result)
|
||||
.map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zCount(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZCountCommand, Long>> zCount(Publisher<ZCountCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
Object lowerBound = AgrumentConverters.lowerBoundArgOf(command.getRange());
|
||||
Object upperBound = AgrumentConverters.upperBoundArgOf(command.getRange());
|
||||
|
||||
Observable<Long> result = Observable.empty();
|
||||
|
||||
if (lowerBound instanceof String || upperBound instanceof String) {
|
||||
result = cmd.zcount(command.getKey().array(), lowerBound.toString(), upperBound.toString());
|
||||
} else {
|
||||
result = cmd.zcount(command.getKey().array(), (Double) lowerBound, (Double) upperBound);
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zCard(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<KeyCommand, Long>> zCard(Publisher<KeyCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(cmd.zcard(command.getKey().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zScore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZScoreCommand, Double>> zScore(Publisher<ZScoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getValue(), "Value must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection.<Double> monoConverter()
|
||||
.convert(cmd.zscore(command.getKey().array(), command.getValue().array()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRemRangeByRank(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZRemRangeByRankCommand, Long>> zRemRangeByRank(
|
||||
Publisher<ZRemRangeByRankCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
return LettuceReactiveRedisConnection
|
||||
.<Long> monoConverter().convert(cmd.zremrangebyrank(command.getKey().array(),
|
||||
command.getRange().getLowerBound(), command.getRange().getUpperBound()))
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRemRangeByRank(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZRemRangeByScoreCommand, Long>> zRemRangeByScore(
|
||||
Publisher<ZRemRangeByScoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Key must not be null!");
|
||||
Assert.notNull(command.getRange(), "Range must not be null!");
|
||||
|
||||
Object lowerBound = AgrumentConverters.lowerBoundArgOf(command.getRange());
|
||||
Object upperBound = AgrumentConverters.upperBoundArgOf(command.getRange());
|
||||
|
||||
Observable<Long> result = (lowerBound instanceof String || upperBound instanceof String)
|
||||
? cmd.zremrangebyscore(command.getKey().array(), lowerBound.toString(), upperBound.toString())
|
||||
: cmd.zremrangebyscore(command.getKey().array(), (Double) lowerBound, (Double) upperBound);
|
||||
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zUnionStore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUnionStoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
|
||||
|
||||
ZStoreArgs args = null;
|
||||
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
|
||||
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null, command.getWeights());
|
||||
}
|
||||
|
||||
byte[][] sourceKeys = command.getSourceKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]);
|
||||
Observable<Long> result = args != null ? cmd.zunionstore(command.getKey().array(), args, sourceKeys)
|
||||
: cmd.zunionstore(command.getKey().array(), sourceKeys);
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zInterStore(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<NumericResponse<ZInterStoreCommand, Long>> zInterStore(Publisher<ZInterStoreCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty!");
|
||||
|
||||
ZStoreArgs args = null;
|
||||
if (command.getAggregateFunction().isPresent() || !command.getWeights().isEmpty()) {
|
||||
args = zStoreArgs(command.getAggregateFunction().isPresent() ? command.getAggregateFunction().get() : null, command.getWeights());
|
||||
}
|
||||
|
||||
byte[][] sourceKeys = command.getSourceKeys().stream().map(ByteBuffer::array).toArray(size -> new byte[size][]);
|
||||
Observable<Long> result = args != null ? cmd.zinterstore(command.getKey().array(), args, sourceKeys)
|
||||
: cmd.zinterstore(command.getKey().array(), sourceKeys);
|
||||
return LettuceReactiveRedisConnection.<Long> monoConverter().convert(result)
|
||||
.map(value -> new NumericResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.ReactiveZSetCommands#zRangeByLex(org.reactivestreams.Publisher)
|
||||
*/
|
||||
@Override
|
||||
public Flux<MultiValueResponse<ZRangeByLexCommand, ByteBuffer>> zRangeByLex(Publisher<ZRangeByLexCommand> commands) {
|
||||
|
||||
return connection.execute(cmd -> Flux.from(commands).flatMap(command -> {
|
||||
|
||||
Assert.notNull(command.getKey(), "Destination key must not be null!");
|
||||
|
||||
Observable<byte[]> result = Observable.empty();
|
||||
|
||||
String lowerBound = AgrumentConverters.lowerBoundArgOf(command.getRange()).toString();
|
||||
String upperBound = AgrumentConverters.upperBoundArgOf(command.getRange()).toString();
|
||||
|
||||
if (command.getLimit() != null) {
|
||||
|
||||
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
|
||||
result = cmd.zrangebylex(command.getKey().array(), lowerBound, upperBound, command.getLimit().getOffset(),
|
||||
command.getLimit().getCount());
|
||||
} else {
|
||||
|
||||
// TODO: fix when https://github.com/mp911de/lettuce/issues/369 resolved
|
||||
throw new UnsupportedOperationException("Lettuce does not support ZREVRANGEBYLEX.");
|
||||
}
|
||||
} else {
|
||||
if (ObjectUtils.nullSafeEquals(command.getDirection(), Direction.ASC)) {
|
||||
result = cmd.zrangebylex(command.getKey().array(), lowerBound, upperBound);
|
||||
} else {
|
||||
|
||||
// TODO: fix when https://github.com/mp911de/lettuce/issues/369 resolved
|
||||
throw new UnsupportedOperationException("Lettuce does not support ZREVRANGEBYLEX.");
|
||||
}
|
||||
}
|
||||
|
||||
return LettuceReactiveRedisConnection.<List<ByteBuffer>> monoConverter()
|
||||
.convert(result.map(ByteBuffer::wrap).toList()).map(value -> new MultiValueResponse<>(command, value));
|
||||
}));
|
||||
}
|
||||
|
||||
private ZStoreArgs zStoreArgs(Aggregate aggregate, List<Double> weights) {
|
||||
|
||||
ZStoreArgs args = new ZStoreArgs();
|
||||
if (aggregate != null) {
|
||||
switch (aggregate) {
|
||||
case MIN:
|
||||
args.min();
|
||||
break;
|
||||
case MAX:
|
||||
args.max();
|
||||
break;
|
||||
default:
|
||||
args.sum();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix when https://github.com/mp911de/lettuce/issues/368 resolved
|
||||
if (weights != null) {
|
||||
long[] lg = new long[weights.size()];
|
||||
for (int i = 0; i < lg.length; i++) {
|
||||
lg[i] = weights.get(i).longValue();
|
||||
}
|
||||
args.weights(lg);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
protected LettuceReactiveRedisConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user