java FT. sql无法使用基于集群的Redis服务器配置

mmvthczy  于 2023-11-15  发布在  Java
关注(0)|答案(1)|浏览(160)
  • Redis版本:7.2.1
  • 安装的模块:RediSearch v.2.4.15,RedisJSON v.99.99.99
  • 节点数量:xx.x.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
    pom.xml
  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>4.2.3</version>
  5. </dependency>

字符串

配置

  1. @Bean
  2. public JedisCluster getRedisCluster() {
  3. Set<HostAndPort> jedisClusterNode = new HashSet<>();
  4. 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"};
  5. for (int i = 0; i < nodes.length; i++) {
  6. String[] ipAndPort = nodes[i].split(":");
  7. jedisClusterNode.add(new HostAndPort(ipAndPort[0], Integer.valueOf(ipAndPort[1])));
  8. }
  9. JedisCluster jc = new JedisCluster(jedisClusterNode, "default", "admin");
  10. logger.debug("Redis(FT) connection Successfully.");
  11. return jc;
  12. }

BookDataSearchIndex.java

  1. @Component
  2. @Order(1)
  3. public class BookDataSearchIndex implements CommandLineRunner {
  4. private static final Logger logger = LoggerFactory.getLogger(BookDataSearchIndex.class);
  5. @Autowired
  6. private UnifiedJedis jedis;
  7. @Override
  8. public void run(String... args) throws Exception {
  9. try {
  10. Schema schema = new Schema()
  11. .addField(new Schema.Field(FieldName.of("$.bookId").as("BOOKID"), Schema.FieldType.TEXT, false,
  12. false))
  13. .addField(new Schema.Field(FieldName.of("$.title").as("TITLE"), Schema.FieldType.TEXT, false, false))
  14. .addField(new Schema.Field(FieldName.of("$.price").as("PRICE"), Schema.FieldType.NUMERIC, true, false));
  15. IndexDefinition indexDefinition = new IndexDefinition(IndexDefinition.Type.JSON)
  16. .setPrefixes("book:");
  17. jedis.ftCreate("bookdata-idx", IndexOptions.defaultOptions().setDefinition(indexDefinition),
  18. schema);
  19. } catch (Exception e) {
  20. logger.debug("Inside run in BookDataSearchIndex : {}", e.getMessage());
  21. }
  22. }
  23. }

BookData.java

  1. public class BookData {
  2. private String bookId;
  3. private String title;
  4. private Long price;
  5. // Setter & Getter
  6. }

Page.java

  1. public class Page<T> {
  2. private List<T> data;
  3. private Integer totalPage;
  4. private Integer currentPage;
  5. private Long total;
  6. public Page(List<T> data, Integer totalPage, Integer currentPage, Long total) {
  7. super();
  8. this.data = data;
  9. this.totalPage = totalPage;
  10. this.currentPage = currentPage;
  11. this.total = total;
  12. }
  13. // Setter & Getter
  14. }

BookDataHelper.java

  1. import java.lang.reflect.Field;
  2. import java.util.ArrayList;
  3. import java.util.Calendar;
  4. import java.util.Date;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.stream.Collectors;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import com.google.gson.Gson;
  14. import redis.clients.jedis.UnifiedJedis;
  15. import redis.clients.jedis.search.Document;
  16. import redis.clients.jedis.search.Query;
  17. import redis.clients.jedis.search.SearchResult;
  18. public class BookDataHelper {
  19. private static final String ATTHERATE = "@";
  20. private static final String LESS_THEN = "lt";
  21. private static final String GREATER_THEN_EQUAL = "gte";
  22. private static final String GREATER_THEN = "gt";
  23. private static final String LESS_THEN_EQUAL = "lte";
  24. private static final String BETWEEN = "between";
  25. private static final Logger logger = LoggerFactory.getLogger(BookDataHelper.class);
  26. @Autowired
  27. private UnifiedJedis jedis;
  28. public BookData save(BookData data) {
  29. String memberKey = "book:"+ data.getBookId();
  30. Gson gson = new Gson();
  31. jedis.jsonSet(getKey(memberKey), gson.toJson(data));
  32. jedis.sadd(getKey("bookdata"), getKey(memberKey));
  33. return data;
  34. }
  35. public BookData findByKey(String index, String key, Object value, Class<BookData> dto) {
  36. Map<String, Object> fields = new HashMap<>();
  37. fields.put(key, value);
  38. List<BookData> t = search(index, fields, dto);
  39. if (!t.isEmpty()) {
  40. return t.get(0);
  41. }
  42. return null;
  43. }
  44. public List<BookData> search(String index, Map<String, Object> fields, Class<BookData> dto) {
  45. String queryCriteria = buildQuery(dto, fields, null);
  46. return buildResponse(index, queryCriteria, dto);
  47. }
  48. public Page<BookData> search(String index, String queryCriteria, Integer offset, Integer limit, Class<BookData> dto) {
  49. Query query = null;
  50. if (queryCriteria.isEmpty()) {
  51. query = new Query();
  52. } else {
  53. query = new Query(queryCriteria);
  54. }
  55. query.limit(offset, limit);
  56. SearchResult searchResult = jedis.ftSearch(index, query);
  57. Long total = searchResult.getTotalResults();
  58. int totalPage = (int) Math.ceil((double) total / limit);
  59. List<BookData> orderDataList = searchResult.getDocuments().stream()
  60. .map(document -> convertDocumentToModel(document, dto)).collect(Collectors.toList());
  61. return new Page<>(orderDataList, totalPage, offset, total);
  62. }
  63. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  64. //////// PRIVATE METHODS ////////
  65. ////////////////////////////////////////////////////////////////////////////////////////////////////////
  66. private List<BookData> buildResponse(String index, String queryCriteria, Class<BookData> dto) {
  67. int offset = 0;
  68. int limit = 10;
  69. boolean done = false;
  70. List<BookData> result = new ArrayList<>();
  71. while (!done) {
  72. Page<BookData> pageResult = search(index, queryCriteria,offset, limit, dto);
  73. result.addAll(pageResult.getData());
  74. // Check if there are more pages of results
  75. if (pageResult.getData().isEmpty() || pageResult.getTotal() < limit) {
  76. done = true;
  77. } else {
  78. offset += limit;
  79. }
  80. }
  81. return result;
  82. }
  83. private static synchronized String buildQuery(Class<?> dto, Map<String, Object> fields, Map<String, String> operators) {
  84. StringBuilder queryBuilder = new StringBuilder();
  85. List<String> entityNumberTypeFields = getNumberFields(dto);
  86. for (Entry<String, Object> entry : fields.entrySet()) {
  87. String fieldName = entry.getKey().trim().toUpperCase();
  88. Object fieldValue = toValue(entry.getValue());
  89. if (null != operators && operators.containsKey(entry.getKey().trim())) {
  90. String operator = operators.get(entry.getKey().trim()).trim();
  91. buildOperatorsQuery(queryBuilder, operator, fieldName, fieldValue, entry.getValue());
  92. } else {
  93. if(entityNumberTypeFields.contains(fieldName)) {
  94. queryBuilder.append(ATTHERATE).append(fieldName).append(":[").append(fieldValue).append(",")
  95. .append(fieldValue).append("]").append(" ");
  96. } else {
  97. queryBuilder.append(ATTHERATE).append(fieldName).append(":").append(fieldValue).append(" ");
  98. }
  99. }
  100. }
  101. return queryBuilder.toString();
  102. }
  103. private static synchronized void buildOperatorsQuery(StringBuilder buildQuery, String operator, String fieldName, Object fieldValue, Object originFieldValue) {
  104. if (GREATER_THEN.equalsIgnoreCase(operator)) {
  105. buildQuery.append(ATTHERATE).append(fieldName).append(":[")
  106. .append(getIncrementalVal(originFieldValue)).append(" > ").append(Integer.MAX_VALUE)
  107. .append("]").append(" ");
  108. } else if (LESS_THEN.equalsIgnoreCase(operator)) {
  109. buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(Integer.MIN_VALUE)
  110. .append(" < ").append(getDecrementalVal(originFieldValue)).append("]").append(" ");
  111. } else if (GREATER_THEN_EQUAL.equalsIgnoreCase(operator)) {
  112. buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(fieldValue).append(" > ")
  113. .append(Integer.MAX_VALUE).append("]").append(" ");
  114. } else if (LESS_THEN_EQUAL.equalsIgnoreCase(operator)) {
  115. buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(Integer.MIN_VALUE)
  116. .append(" < ").append(toValue(fieldValue)).append("]").append(" ");
  117. } else if (BETWEEN.equalsIgnoreCase(operator) && originFieldValue instanceof List) {
  118. List<?> range = (List<?>) originFieldValue;
  119. if (range.size() == 2) {
  120. buildQuery.append(ATTHERATE).append(fieldName).append(":[").append(toValue(range.get(0))).append(",")
  121. .append(toValue(range.get(1))).append("]").append(" ");
  122. } else {
  123. throw new IllegalArgumentException("Invalid range for 'between' operator");
  124. }
  125. } else {
  126. buildQuery.append(ATTHERATE).append(fieldName).append(":").append(fieldValue).append(" ");
  127. }
  128. }
  129. private static String getKey(String key) {
  130. return key.replace("-", "").replace("_", "");
  131. }
  132. public static <T> T convertDocumentToModel(Document document, Class<T> model) {
  133. Gson gson = new Gson();
  134. String jsonDoc = document.getProperties().iterator().next().getValue().toString();
  135. return gson.fromJson(jsonDoc, model);
  136. }
  137. public static List<String> getNumberFields(Class<?> obj) {
  138. List<String> fieldList = new ArrayList<>();
  139. try {
  140. Class<?> clazz = obj.newInstance().getClass();
  141. Field[] fields = clazz.getDeclaredFields();
  142. for (Field field : fields) {
  143. if (isNumberType(field.getType())) {
  144. field.setAccessible(true); // Make the field accessible
  145. fieldList.add(field.getName().toUpperCase());
  146. }
  147. }
  148. } catch (InstantiationException | IllegalAccessException e) {
  149. logger.error("Error while find number fields. {}", e);
  150. }
  151. return fieldList;
  152. }
  153. private static boolean isNumberType(Class<?> fieldType) {
  154. return fieldType == int.class || fieldType == Integer.class || fieldType == long.class
  155. || fieldType == Long.class || fieldType == short.class || fieldType == Short.class
  156. || fieldType == byte.class || fieldType == Byte.class;
  157. }
  158. public static Object toValue(Object value) {
  159. try {
  160. if(value instanceof Date) {
  161. Date date = (Date) value;
  162. return date.getTime();
  163. } else if(value instanceof String) {
  164. return value.toString().trim().replace("-", "*").replace("_", "*");
  165. }
  166. } catch (Exception e) {
  167. logger.error("Error in toValue while parsing value. {}", e);
  168. }
  169. return value;
  170. }
  171. public static Object getIncrementalVal(Object value) {
  172. try {
  173. if(value instanceof Integer || value instanceof Long || value instanceof String) {
  174. long val = Long.parseLong((String) value.toString());
  175. return val + 1;
  176. } else if(value instanceof Double || value instanceof Float) {
  177. double val = Double.parseDouble((String) value.toString());
  178. return val + 0.1;
  179. } else if(value instanceof Date) {
  180. Date date = (Date) value;
  181. Calendar calendar = Calendar.getInstance();
  182. calendar.setTime(date);
  183. // Add one day
  184. calendar.add(Calendar.DAY_OF_YEAR, 1);
  185. return calendar.getTime().getTime();
  186. }
  187. } catch (Exception e) {
  188. logger.error("Error in getIncrementalVal while parsing value. {}", e);
  189. }
  190. return value;
  191. }
  192. public static Object getDecrementalVal(Object value) {
  193. try {
  194. if(value instanceof Integer || value instanceof Long || value instanceof String) {
  195. long val = Long.parseLong((String) value.toString());
  196. return val - 1;
  197. } else if(value instanceof Double || value instanceof Float) {
  198. double val = Double.parseDouble((String) value.toString());
  199. return val - 0.1;
  200. } else if(value instanceof Date) {
  201. Date date = (Date) value;
  202. Calendar calendar = Calendar.getInstance();
  203. calendar.setTime(date);
  204. // Subtract one day
  205. calendar.add(Calendar.DAY_OF_YEAR, -1);
  206. return calendar.getTime().getTime();
  207. }
  208. } catch (Exception e) {
  209. logger.error("Error in getDecrementalVal while parsing value. {}", e);
  210. }
  211. return value;
  212. }
  213. }

调用BookDataHelper.save方法保存以下数据。

  1. bookId = "HFDP-1"
  2. title = "Head First Design Patterns"
  3. price = 200

调用BookDataHelper.findByKey方法查询数据。

  1. BookDataHelper.findByKey("bookdata-idx", "bookId", "HFDP-1", BookData.class);


不幸的是没有得到数据结果,也尝试了以下命令与CLI。

  1. FT.SEARCH bookdata-idx @BOOKID:HFDP*1

ryoqjall

ryoqjall1#

你提到没有得到任何结果,但没有提到得到任何错误。我还假设你使用的是常规的RediSearch(在所有节点中正确安装)。
基于这些,我认为你必须在所有节点上执行ftCreate,并从所有节点上提取ftSearch
自Jedis 4.4.0+起(尽可能使用最新版本):

  • ftCreate默认对所有节点执行。
  • 通过使用ftSearchIteration方法而不是ftSearch方法可以轻松地从所有节点中获取。

相关问题