学习使用 Jedis 在 JSON 文档中存储、读取和搜索数据

上次更新 2024 年 4 月 26 日

目标

Redis 可以管理 JSON 文档,除了索引 Redis 哈希表之外,JSON 文档还可以被索引和搜索。本文档提供了一个使用 Jedis 存储、索引和搜索会话数据的完整示例。

解决方案

在本文件中详细介绍的示例中,我们将继续以以下格式存储会话数据

[
   {
      "lastAccessedTime":1713903362,
      "creationTime":1713903362,
      "location":"34.638,31.79",
      "visited":[
         "www.redis.io",
         "www.wikipedia.com",
         "www.mortensi.com"
      ],
      "cart":[
         {
            "quantity":1,
            "price":1990.99,
            "id":"hp-2341"
         },
         {
            "quantity":2,
            "price":19.99,
            "id":"case-9993"
         }
      ]
   }
]

我们还将探讨如何创建索引来索引会话的不同部分。让我们开始并连接到 Redis,如下所示

HostAndPort node = HostAndPort.from("localhost:6379");
JedisClientConfig clientConfig = DefaultJedisClientConfig.builder()
                                        .resp3() 
                                        .build();

UnifiedJedis client = new UnifiedJedis(node, clientConfig);

学习如何使用 Jedis 客户端库在使用 Maven 构建的 Java 项目中连接到 Redis

我们正在创建一个索引;让我们删除最终的索引和所有相关文档。

client.ftDropIndexDD("session_idx");

如果你想保留数据并删除索引,请使用命令 ftDropIndex

现在,我们可以通过选择需要索引的字段来定义索引。

Schema schema = new Schema()
                        .addGeoField("$.location").as("location")
                        .addTagField("$.cart[*].id").as("item_id")
                        .addNumericField("$.cart[*].price").as("price")
                        .addTagField("$.visited[*]").as("visited")
                        .addNumericField("$.lastAccessedTime").as("updated")
                        .addNumericField("$.creationTime").as("created");

// Defining the index, it could be HASH too
IndexDefinition def = new IndexDefinition(Type.JSON).setPrefixes(new String[] {"session:"});

// Creating the index 
client.ftCreate("session_idx", IndexOptions.defaultOptions().setDefinition(def), schema);

现在,让我们添加一些元数据,如会话创建和更新时间戳,以及用户的位置,以 (经度,纬度) 的形式表示。了解有关 Redis 中索引的更多信息。

JSONObject jsonObject = new JSONObject();
jsonObject.put("lastAccessedTime", System.currentTimeMillis() / 1000L);
jsonObject.put("creationTime", System.currentTimeMillis() / 1000L);
jsonObject.put("location", "34.638,31.79");
client.jsonSet("session:1", jsonObject);

我们还可以存储已访问 URL 的数组

client.jsonSet("session:1", new Path2("visited"), new ArrayList<>());
List<String> visited = new ArrayList<String>();
visited.add("www.redis.io");
visited.add("www.wikipedia.com");
visited.add("www.mortensi.com");
client.jsonSetWithEscape("session:1", new Path2("visited"), visited);

现在,我们为该用户存储一个包含几个商品的购物车。

// Shopping cart item
JSONObject laptop = new JSONObject();
laptop.put("id", "hp-2341");
laptop.put("price", 1990.99);
laptop.put("quantity", 1);

// Another shopping cart item
JSONObject laptopCase = new JSONObject();
laptopCase.put("id", "case-9993");
laptopCase.put("price", 19.99);
laptopCase.put("quantity", 2);

// Storing items in the shopping cart
client.jsonSet("session:1", new Path2("cart"), new ArrayList<>());
client.jsonArrAppend("session:1", new Path2("cart"), laptop);
client.jsonArrAppend("session:1", new Path2("cart"), laptopCase);

让我们创建第二个会话来演示如何执行跨会话搜索操作。

// Creating another session
JSONObject jsonObject2 = new JSONObject();
jsonObject2.put("lastAccessedTime", System.currentTimeMillis() / 1000L);
jsonObject2.put("creationTime", System.currentTimeMillis() / 1000L);
jsonObject2.put("location", "34.638,31.79");
client.jsonSet("session:2", jsonObject2);

// Shopping cart item
JSONObject book = new JSONObject();
book.put("id", "sking-2435");
book.put("price", 14.90);
book.put("quantity", 1);

client.jsonSet("session:2", new Path2("cart"), new ArrayList<>());
client.jsonArrAppend("session:2", new Path2("cart"), book);

现在检索我们的数据。在屏幕上打印整个会话

System.out.println(client.jsonGet("session:1"));

读取会话的一部分,在本例中仅为购物车

System.out.println(client.jsonGet("session:1", new Path2("$.cart")));

你也可以在会话中搜索特定商品(通过 ID)并返回价格。搜索语法遵循 JSONPath 语法。

System.out.println(client.jsonGet("session:1", new Path2("$.cart[?(@.id==\"hp-2341\")].price")));

最后,让我们使用我们创建的 session_idx 索引并执行跨会话搜索以检索那些商品价格在 10 到 30 个“单位”之间的会话。

Query q = new Query().addFilter(new Query.NumericFilter("price", 10, 30)).setSortBy("price", true).setNoContent();
SearchResult res = client.ftSearch("session_idx", q);
System.out.println("Number of results: " + res.getTotalResults());
List<Document> docs = res.getDocuments();

for (Document doc : docs) {
        System.out.println("Found session: " + doc.getId()); 
}

搜索将检索以下结果,因为两个会话都包含价格范围内的商品。

Number of results: 2
Found session: session:2
Found session: session:1

参考资料