Reputation: 1
pom.xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.2.3</version>
</dependency>
Configuration
@Bean
public JedisCluster getRedisCluster() {
Set<HostAndPort> jedisClusterNode = new HashSet<>();
String[] nodes = {"xx.x.x.xxx:6379", "xx.x.x.xxx:6380", "xx.x.x.xxx:6381", "xx.x.x.xxx:6382", "xx.x.x.xxx:6383", "xx.x.x.xxx:6384"};
for (int i = 0; i < nodes.length; i++) {
String[] ipAndPort = nodes[i].split(":");
jedisClusterNode.add(new HostAndPort(ipAndPort[0], Integer.valueOf(ipAndPort[1])));
}
JedisCluster jc = new JedisCluster(jedisClusterNode, "default", "admin");
logger.debug("Redis(FT) connection Successfully.");
return jc;
}
BookDataSearchIndex.java
@Component
@Order(1)
public class BookDataSearchIndex implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(BookDataSearchIndex.class);
@Autowired
private UnifiedJedis jedis;
@Override
public void run(String... args) throws Exception {
try {
Schema schema = new Schema()
.addField(new Schema.Field(FieldName.of("$.bookId").as("BOOKID"), Schema.FieldType.TEXT, false,
false))
.addField(new Schema.Field(FieldName.of("$.title").as("TITLE"), Schema.FieldType.TEXT, false, false))
.addField(new Schema.Field(FieldName.of("$.price").as("PRICE"), Schema.FieldType.NUMERIC, true, false));
IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.JSON)
.setPrefixes("book:");
jedis.ftCreate("bookdata-idx", IndexOptions.defaultOptions().setDefinition(indexDefinition),
schema);
} catch (Exception e) {
logger.debug("Inside run in BookDataSearchIndex : {}", e.getMessage());
}
}
}
BookData.java
public class BookData {
private String bookId;
private String title;
private Long price;
// Setter & Getter
}
Page.java
public class Page<T> {
private List<T> data;
private Integer totalPage;
private Integer currentPage;
private Long total;
public Page(List<T> data, Integer totalPage, Integer currentPage, Long total) {
super();
this.data = data;
this.totalPage = totalPage;
this.currentPage = currentPage;
this.total = total;
}
// Setter & Getter
}
BookDataHelper.java
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.google.gson.Gson;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.search.Document;
import redis.clients.jedis.search.Query;
import redis.clients.jedis.search.SearchResult;
public class BookDataHelper {
private static final String ATTHERATE = "@";
private static final String LESS_THEN = "lt";
private static final String GREATER_THEN_EQUAL = "gte";
private static final String GREATER_THEN = "gt";
private static final String LESS_THEN_EQUAL = "lte";
private static final String BETWEEN = "between";
private static final Logger logger = LoggerFactory.getLogger(BookDataHelper.class);
@Autowired
private UnifiedJedis jedis;
public BookData save(BookData data) {
String memberKey = "book:"+ data.getBookId();
Gson gson = new Gson();
jedis.jsonSet(getKey(memberKey), gson.toJson(data));
jedis.sadd(getKey("bookdata"), getKey(memberKey));
return data;
}
public BookData findByKey(String index, String key, Object value, Class<BookData> dto) {
Map<String, Object> fields = new HashMap<>();
fields.put(key, value);
List<BookData> t = search(index, fields, dto);
if (!t.isEmpty()) {
return t.get(0);
}
return null;
}
public List<BookData> search(String index, Map<String, Object> fields, Class<BookData> dto) {
String queryCriteria = buildQuery(dto, fields, null);
return buildResponse(index, queryCriteria, dto);
}
public Page<BookData> search(String index, String queryCriteria, Integer offset, Integer limit, Class<BookData> dto) {
Query query = null;
if (queryCriteria.isEmpty()) {
query = new Query();
} else {
query = new Query(queryCriteria);
}
query.limit(offset, limit);
SearchResult searchResult = jedis.ftSearch(index, query);
Long total = searchResult.getTotalResults();
int totalPage = (int) Math.ceil((double) total / limit);
List<BookData> orderDataList = searchResult.getDocuments().stream()
.map(document -> convertDocumentToModel(document, dto)).collect(Collectors.toList());
return new Page<>(orderDataList, totalPage, offset, total);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//////// PRIVATE METHODS ////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
private List<BookData> buildResponse(String index, String queryCriteria, Class<BookData> dto) {
int offset = 0;
int limit = 10;
boolean done = false;
List<BookData> result = new ArrayList<>();
while (!done) {
Page<BookData> pageResult = search(index, queryCriteria,offset, limit, dto);
result.addAll(pageResult.getData());
// Check if there are more pages of results
if (pageResult.getData().isEmpty() || pageResult.getTotal() < limit) {
done = true;
} else {
offset += limit;
}
}
return result;
}
private static synchronized String buildQuery(Class<?> dto, Map<String, Object> fields, Map<String, String> operators) {
StringBuilder queryBuilder = new StringBuilder();
List<String> entityNumberTypeFields = getNumberFields(dto);
for (Entry<String, Object> entry : fields.entrySet()) {
String fieldName = entry.getKey().trim().toUpperCase();
Object fieldValue = toValue(entry.getValue());
if (null != operators && operators.containsKey(entry.getKey().trim())) {
String operator = operators.get(entry.getKey().trim()).trim();
buildOperatorsQuery(queryBuilder, operator, fieldName, fieldValue, entry.getValue());
} else {
if(entityNumberTypeFields.contains(fieldName)) {
queryBuilder.append(ATTHERATE).append(fieldName).append(":[").append(fieldValue).append(",")
.append(fieldValue).append("]").append(" ");
} else {
queryBuilder.append(ATTHERATE).append(fieldName).append(":").append(fieldValue).append(" ");
}
}
}
return queryBuilder.toString();
}
private static synchronized void buildOperatorsQuery(StringBuilder buildQuery, String operator, String fieldName, Object fieldValue, Object originFieldValue) {
if (GREATER_THEN.equalsIgnoreCase(operator)) {
buildQuery.append(ATTHERATE).append(fieldName).append(":[")
.append(getIncrementalVal(originFieldValue)).append(" > ").append(Integer.MAX_VALUE)
.append("]").append(" ");
} else if (LESS_THEN.equalsIgnoreCase(operator)) {
buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(Integer.MIN_VALUE)
.append(" < ").append(getDecrementalVal(originFieldValue)).append("]").append(" ");
} else if (GREATER_THEN_EQUAL.equalsIgnoreCase(operator)) {
buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(fieldValue).append(" > ")
.append(Integer.MAX_VALUE).append("]").append(" ");
} else if (LESS_THEN_EQUAL.equalsIgnoreCase(operator)) {
buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(Integer.MIN_VALUE)
.append(" < ").append(toValue(fieldValue)).append("]").append(" ");
} else if (BETWEEN.equalsIgnoreCase(operator) && originFieldValue instanceof List) {
List<?> range = (List<?>) originFieldValue;
if (range.size() == 2) {
buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(toValue(range.get(0))).append(",")
.append(toValue(range.get(1))).append("]").append(" ");
} else {
throw new IllegalArgumentException("Invalid range for 'between' operator");
}
} else {
buildQuery.append(ATTHERATE).append(fieldName).append(":").append(fieldValue).append(" ");
}
}
private static String getKey(String key) {
return key.replace("-", "").replace("_", "");
}
public static <T> T convertDocumentToModel(Document document, Class<T> model) {
Gson gson = new Gson();
String jsonDoc = document.getProperties().iterator().next().getValue().toString();
return gson.fromJson(jsonDoc, model);
}
public static List<String> getNumberFields(Class<?> obj) {
List<String> fieldList = new ArrayList<>();
try {
Class<?> clazz = obj.newInstance().getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (isNumberType(field.getType())) {
field.setAccessible(true); // Make the field accessible
fieldList.add(field.getName().toUpperCase());
}
}
} catch (InstantiationException | IllegalAccessException e) {
logger.error("Error while find number fields. {}", e);
}
return fieldList;
}
private static boolean isNumberType(Class<?> fieldType) {
return fieldType == int.class || fieldType == Integer.class || fieldType == long.class
|| fieldType == Long.class || fieldType == short.class || fieldType == Short.class
|| fieldType == byte.class || fieldType == Byte.class;
}
public static Object toValue(Object value) {
try {
if(value instanceof Date) {
Date date = (Date) value;
return date.getTime();
} else if(value instanceof String) {
return value.toString().trim().replace("-", "*").replace("_", "*");
}
} catch (Exception e) {
logger.error("Error in toValue while parsing value. {}", e);
}
return value;
}
public static Object getIncrementalVal(Object value) {
try {
if(value instanceof Integer || value instanceof Long || value instanceof String) {
long val = Long.parseLong((String) value.toString());
return val + 1;
} else if(value instanceof Double || value instanceof Float) {
double val = Double.parseDouble((String) value.toString());
return val + 0.1;
} else if(value instanceof Date) {
Date date = (Date) value;
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
// Add one day
calendar.add(Calendar.DAY_OF_YEAR, 1);
return calendar.getTime().getTime();
}
} catch (Exception e) {
logger.error("Error in getIncrementalVal while parsing value. {}", e);
}
return value;
}
public static Object getDecrementalVal(Object value) {
try {
if(value instanceof Integer || value instanceof Long || value instanceof String) {
long val = Long.parseLong((String) value.toString());
return val - 1;
} else if(value instanceof Double || value instanceof Float) {
double val = Double.parseDouble((String) value.toString());
return val - 0.1;
} else if(value instanceof Date) {
Date date = (Date) value;
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
// Subtract one day
calendar.add(Calendar.DAY_OF_YEAR, -1);
return calendar.getTime().getTime();
}
} catch (Exception e) {
logger.error("Error in getDecrementalVal while parsing value. {}", e);
}
return value;
}
}
Save below data by calling BookDataHelper.save method.
bookId = "HFDP-1"
title = "Head First Design Patterns"
price = 200
Retrieve data by calling BookDataHelper.findByKey method.
BookDataHelper.findByKey("bookdata-idx", "bookId", "HFDP-1", BookData.class);
Unfortunately getting no data result, Also tried with CLI by below command.
FT.SEARCH bookdata-idx @BOOKID:HFDP*1
Upvotes: 0
Views: 432
Reputation: 6267
You have mentioned not getting any result but have not mentioned getting any error. I am also assuming you are using regular RediSearch (installed properly in all of the nodes).
Based on these, I think you have to execute ftCreate
in all the nodes and fetch by ftSearch
from all the nodes.
Since Jedis 4.4.0+ (use the latest version possible):
ftCreate
is executed to all the nodes by default.ftSearchIteration
method instead of ftSearch
.Upvotes: 0