redis的索引模式基本是基于sorted set
的,因sorted set
有数值,数值就是索引,你可以根据它找到其他值。
sorted set
sorted set是set和hash的混合结构。
它不能重复,这点像set。
它里面的每个元素都和一个浮点数映射(对应),这点像hash。
举例:
按照生年排序(全部)。
找到1950年前生的黑客。
删除1940年到1960年出生的黑客。
更多操作可以查询help @sorted_set
。
sorted set
的内部结构是跳跃表。跳跃表是一种链表,它的查询时间复杂度是
O
(
log
2
n
)
O(\log_2 n)
O(log2n)。
字典排序
如果score相等,那么就会按照字母的字典顺序来排序:
我们可以找出一定字母范围内的黑客:
这里的A和P是包含进来的。
这里的lex
是Lexicographical
的意思,就是字典。
如果你想得到以某个字母开始的所有黑客,可以这么写:
只要前两个的话加上分页:
地理信息
redis有经纬度的指令,很神奇。
我们有一个cars的集合,里面的一个成员是my-car,经纬度由一对数值给定。
关键是,怎么这些数就是经纬度了呢?
redis用geohash
把这些数转换成sorted set的score。因此地理信息功能还是基于sorted set的。
更新经纬度:
再来一辆车:
两辆车的距离:
这是以米为单位的。
以英尺为单位。
以某个点为圆心,半径100m以内的所有车子。
以robins-car为圆心,半径152m以内的所有车辆。
带上距离和坐标。
删除一辆车。
文本查询
redis有文本搜索的功能,我们将用set简单地实现一下,当然你也可以用sorted set,只要再携带score就行了。
比如我这里有三句话:
“Redis is very fast”
“Cheetahs are fast”
“Cheetahs have spots”
我们用set将它们分别存起来:
这告诉我们的是:哪一个文档有哪些词。
然后,反转:
这告诉我们:一个词在哪一个文档。
有very并且有fast的文档是:ex1、ex2。
有cheetahs或者有redis的文档是:ex1、ex2、ex3。
要删除一个文档,首先要找出它的所有单词,然后删除以该单词为索引的文档:
redis search
文本搜索redis有专门的模块。
下载使用:
git clone --recursive https://github.com/RediSearch/RediSearch.git
make build
make run
运行之后,我们可以看到,name是full text,版本是一堆9。
解释一下:
myIdx
是key。
SCHEMA
表示后面跟的东西是搜索索引的字段。
后面的title Text
等等的都是字段+类型
的模式。
添加文档:
我们往myIdx
上加一个文档doc1
,ID是1.0
。
FIELDS
表示后面都是为字段赋值的。
我们探究一下这个奇怪的数据结构:
redis本身不认识我们创建的index。
一共有10个key。
ft_index0
是RediSearch自己的数据类型。
doc1
是用hash来存储的。
ft_invidx
表示http
是一个文本索引。
搜索:
有“hello world”的文档就我们刚建的一个。
自动补全:
我们在autocomplete这个字典中增加了一个suggestion,后面的100是“hello world”的权重。
jredisearch
java也有操作redisearch的库。
pom:
<dependency>
<groupId>com.redislabs</groupId>
<artifactId>jredisearch</artifactId>
<version>0.21.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.1</version>
</dependency>
github给出的版本太高了,会报依赖冲突的错。
在server端启动redis server:
./redis-server /home/ocean/redis-5.0.7/redis.conf --loadmodule /home/ocean/redis_search/RediSearch/src/redisearch.so timeout 100
我带上了redis.conf
和redisearch.so
。
public class Redissearch {
public static void main(String[] args) {
//create a redis client
Client client = new Client("myIdx", "192.168.8.125", 6379,10000,10);
//create a schema
Schema sc = new Schema()
.addTextField("title", 5.0)
.addTextField("body", 1.0)
.addNumericField("price");
//create the index
client.createIndex(sc, Client.IndexOptions.Default());
//set the fields
Map<String, Object> fields = new HashMap<>();
fields.put("title", "hello world");
fields.put("state", "NY");
fields.put("body", "lorem ipsum");
fields.put("price", 1337);
//create a doc
client.addDocument("doc1", fields);
// Creating a complex query
Query q = new Query("hello world")
.addFilter(new Query.NumericFilter("price", 0, 2000))
.limit(0,5);
// actual search
SearchResult res = client.search(q);
List<Document> docs = res.docs;
docs.stream().forEach(System.out::println);
}
}
代码的语义很好懂。我们要查询有hello world
的文档并且价格在0到2000之间。