DATAKV-36

add current sort-and-get draft
This commit is contained in:
Costin Leau
2011-03-07 19:34:10 +02:00
parent df5a4e3cfc
commit ea8806aa61
8 changed files with 470 additions and 34 deletions

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2011 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.keyvalue.redis.core;
import java.util.Iterator;
import java.util.List;
/**
* Wrapper class allowing for stream-like access across a list of values.
*
* @author Costin Leau
*/
class BulkIterable<T> implements Iterable<T> {
private final List<T> list;
private volatile int index = 0;
public BulkIterable(List<T> list) {
this.list = list;
}
public boolean hasMore() {
throw new UnsupportedOperationException();
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public T next() {
return list.get(index++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2011 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.keyvalue.redis.core;
import java.util.Iterator;
/**
* Mapper translating Redis bulk value responses (typically returned by a sort query) to actual objects. Implementations of this interface do not have to worry
* about exception or connection handling.
* <p/>
* Typically used by {@link RedisTemplate} <tt>sortAndGet</tt> methods.
*
* @author Costin Leau
*/
public interface BulkMapper<T> {
T mapBulk(Iterator<byte[]> valueStream);
}

View File

@@ -32,10 +32,12 @@ import java.util.concurrent.TimeUnit;
import org.springframework.dao.DataAccessException;
import org.springframework.data.keyvalue.redis.connection.DataType;
import org.springframework.data.keyvalue.redis.connection.DefaultSortParameters;
import org.springframework.data.keyvalue.redis.connection.RedisConnection;
import org.springframework.data.keyvalue.redis.connection.RedisConnectionFactory;
import org.springframework.data.keyvalue.redis.connection.SortParameters;
import org.springframework.data.keyvalue.redis.connection.RedisListCommands.Position;
import org.springframework.data.keyvalue.redis.core.query.SortQuery;
import org.springframework.data.keyvalue.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.keyvalue.redis.serializer.RedisSerializer;
import org.springframework.data.keyvalue.redis.serializer.StringRedisSerializer;
@@ -145,7 +147,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
* @return object returned by the action
*/
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
return execute(action, exposeConnection, valueSerializer);
return execute(action, exposeConnection, false);
}
/**
@@ -158,35 +160,6 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
* @return object returned by the action
*/
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
return execute(action, exposeConnection, pipeline, valueSerializer);
}
/**
* Executes the given action object within a connection, which can be exposed or not. Allows a custom serializer
* to be specified for the returned object.
*
* @param <T> return type
* @param action action callback object that specifies the Redis action
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
* @param returnSerializer serializer used for converting the binary data to the custom return type
* @return returned by the action
*/
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, RedisSerializer<?> returnSerializer) {
return execute(action, exposeConnection, false, returnSerializer);
}
/**
* Executes the given action object within a connection, which can be exposed or not. Allows a custom serializer
* to be specified for the returned object.
*
* @param <T> return type
* @param action action callback object that specifies the Redis action
* @param exposeConnection whether to enforce exposure of the native Redis Connection to callback code
* @param pipeline whether to pipeline or not the connection for the execution duration
* @param returnSerializer serializer used for converting the binary data to the custom return type
* @return returned by the action
*/
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline, RedisSerializer<?> returnSerializer) {
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = getConnectionFactory();
@@ -203,7 +176,7 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
try {
RedisConnection connToExpose = (exposeConnection ? conn : createRedisConnectionProxy(conn));
T result = action.doInRedis(connToExpose);
// TODO: should do flush?
// TODO: any other connection processing?
return postProcessResult(result, conn, existingConnection);
} finally {
try {
@@ -450,11 +423,15 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
@SuppressWarnings("unchecked")
private <T extends Collection<V>> T deserializeValues(Collection<byte[]> rawValues, Class<? extends Collection> type) {
Collection<V> values = (List.class.isAssignableFrom(type) ? new ArrayList<V>(rawValues.size())
: new LinkedHashSet<V>(rawValues.size()));
return deserializeValues(rawValues, type, valueSerializer);
}
private <X, T extends Collection<X>> T deserializeValues(Collection<byte[]> rawValues, Class<? extends Collection> type, RedisSerializer<?> redisSerializer) {
Collection<X> values = (List.class.isAssignableFrom(type) ? new ArrayList<X>(rawValues.size())
: new LinkedHashSet<X>(rawValues.size()));
for (byte[] bs : rawValues) {
if (bs != null) {
values.add((V) valueSerializer.deserialize(bs));
values.add((X) redisSerializer.deserialize(bs));
}
}
@@ -1975,4 +1952,96 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return deserializeHashMap(entries);
}
}
// Sort operations
public List<V> sort(SortQuery<K> query) {
return sort(query, null);
}
public List<V> sort(SortQuery<K> query, String getKeyPattern) {
return sort(query, getKeyPattern, valueSerializer);
}
@SuppressWarnings("unchecked")
public <T> List<T> sort(SortQuery<K> query, String getKeyPattern, RedisSerializer<T> resultSerializer) {
final byte[] rawKey = rawKey(query.getKey());
final SortParameters params = convertQuery(query,
(getKeyPattern != null ? Collections.singletonList(getKeyPattern) : null), stringSerializer);
List<byte[]> vals = execute(new RedisCallback<List<byte[]>>() {
@Override
public List<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
return connection.sort(rawKey, params);
}
}, true);
return (List<T>) deserializeValues(vals, List.class, resultSerializer);
}
public <T> List<T> sort(SortQuery<K> query, List<String> getKeyPattern, BulkMapper<T> bulkMapper) {
final byte[] rawKey = rawKey(query.getKey());
final SortParameters params = convertQuery(query, getKeyPattern, stringSerializer);
List<byte[]> vals = execute(new RedisCallback<List<byte[]>>() {
@Override
public List<byte[]> doInRedis(RedisConnection connection) throws DataAccessException {
return connection.sort(rawKey, params);
}
}, true);
int bulkSize = getKeyPattern.size();
List<T> result = new ArrayList<T>(vals.size() / bulkSize + 1);
final List<byte[]> bulk = new ArrayList<byte[]>(bulkSize);
final List<byte[]> listView = Collections.unmodifiableList(bulk);
for (byte[] bs : vals) {
bulk.add(bs);
if (bulk.size() == bulkSize) {
bulkMapper.mapBulk(listView.iterator());
bulk.clear();
}
}
return result;
}
public void sortAndStore(SortQuery<K> query, K storeKey) {
sortAndStore(query, null, storeKey);
}
public void sortAndStore(SortQuery<K> query, List<String> getKeyPattern, K storeKey) {
final byte[] rawStoreKey = rawKey(storeKey);
final byte[] rawKey = rawKey(query.getKey());
final SortParameters params = convertQuery(query, getKeyPattern, stringSerializer);
execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.sort(rawKey, params, rawStoreKey);
return null;
}
}, true);
}
private static <K> SortParameters convertQuery(SortQuery<K> query, List<String> getKeyPattern, RedisSerializer<String> stringSerializer) {
return new DefaultSortParameters(stringSerializer.serialize(query.getBy()), query.getLimit(), serialize(
getKeyPattern, stringSerializer), query.getOrder(), query.isAlphabetic());
}
private static byte[][] serialize(List<String> strings, RedisSerializer<String> stringSerializer) {
List<byte[]> raw = null;
if (strings == null) {
raw = Collections.emptyList();
}
else {
raw = new ArrayList<byte[]>(strings.size());
for (String key : strings) {
raw.add(stringSerializer.serialize(key));
}
}
return raw.toArray(new byte[raw.size()][]);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2011 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.keyvalue.redis.core.query;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
/**
* @author Costin Leau
*/
class DefaultSortCriterion<K> implements SortCriterion<K> {
private final K key;
private String by;
private Range limit;
private Order order;
private Boolean alpha;
DefaultSortCriterion(K key) {
this.key = key;
}
@Override
public SortCriterion<K> alphabetical(boolean alpha) {
this.alpha = Boolean.valueOf(alpha);
return this;
}
@Override
public SortQuery<K> build() {
return new DefaultSortQuery<K>(key, by, limit, order, alpha);
}
@Override
public SortCriterion<K> limit(long offset, long count) {
this.limit = new Range(offset, count);
return this;
}
@Override
public SortCriterion<K> limit(Range range) {
this.limit = range;
return this;
}
@Override
public SortCriterion<K> order(Order order) {
this.order = order;
return this;
}
SortCriterion<K> addBy(String keyPattern) {
this.by = keyPattern;
return this;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2011 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.keyvalue.redis.core.query;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
/**
* Default SortQuery implementation.
*
* @author Costin Leau
*/
class DefaultSortQuery<K> implements SortQuery<K> {
private final K key;
private final Boolean alpha;
private final Order order;
private final Range limit;
private final String by;
DefaultSortQuery(K key, String by, Range limit, Order order, Boolean alpha) {
this.key = key;
this.by = by;
this.limit = limit;
this.order = order;
this.alpha = alpha;
}
@Override
public String getBy() {
return by;
}
@Override
public Range getLimit() {
return limit;
}
@Override
public Order getOrder() {
return order;
}
@Override
public Boolean isAlphabetic() {
return alpha;
}
@Override
public K getKey() {
return key;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2011 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.keyvalue.redis.core.query;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
/**
* @author Costin Leau
*/
public interface SortCriterion<K> {
SortCriterion<K> limit(long offset, long count);
SortCriterion<K> limit(Range range);
SortCriterion<K> order(Order order);
SortCriterion<K> alphabetical(boolean alpha);
SortQuery<K> build();
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2011 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.keyvalue.redis.core.query;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Order;
import org.springframework.data.keyvalue.redis.connection.SortParameters.Range;
/**
* @author Costin Leau
*/
public interface SortQuery<K> {
/**
* Returns the sorting order. Can be null if nothing is specified.
*
* @return sorting order
*/
Order getOrder();
/**
* Indicates if the sorting is numeric (default) or alphabetical (lexicographical).
* Can be null if nothing is specified.
*
* @return the type of sorting
*/
Boolean isAlphabetic();
/**
* Returns the sorting limit (range or pagination).
* Can be null if nothing is specified.
*
* @return sorting limit/range
*/
Range getLimit();
/**
* Target key for sorting.
*
* @return
*/
K getKey();
/**
* Pattern of external key used for sorting.
*
* @return
*/
String getBy();
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2011 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.keyvalue.redis.core.query;
/**
* Builder class for constructing {@link SortQuery}.
*
* @author Costin Leau
*/
public class SortQueryBuilder<K> extends DefaultSortCriterion<K> {
private static final String NO_SORT_KEY = "~";
private SortQueryBuilder(K key) {
super(key);
}
public static <K> SortQueryBuilder<K> sort(K key) {
return new SortQueryBuilder<K>(key);
}
public SortCriterion<K> by(String keyPattern) {
return addBy(keyPattern);
}
public SortCriterion<K> noSort() {
return by(NO_SORT_KEY);
}
}