RediSearch-创建多个索引

87 阅读4分钟

问题

首先,RediSearch是可以创建多个索引的。

但是,创建多个索引之后,有个问题,就是可以搜到对方的数据。

这是为什么呢?

原因

原因就是因为,写数据的时候,没有指定索引名字。

那为什么没有指定索引名字?因为旧版本的RediSearch是可以指定索引名字的,但是新版本不能指定索引名字。

那新版本,怎么区分不同的数据?根据key的前缀。

为什么新版本要这么设计呢?因为不同索引是可能共享同一部分数据的。

说白了,这个设计思想的背后目的是,粒度更细了。以前是索引粒度,现在是前缀粒度。

优点是:粒度更细了。内存更小了,因为不同索引可以共享同一部分数据。粒度更细了之后,也更灵活了。

缺点是:容易混淆。特别是初学者,搞晕了——而且还容易出bug,导致生产故障。

解决方法

比如,创建两个不同的索引:

1、第一个索引名字
idx:name1

2、第二个索引名字
idx:name2


然后,接下来,开始往索引里面写数据:

1、第一个索引
hset(keyPrefix1+id,map);

2、第二个索引
hset(keyPrefix2+id,map);


注意,写数据的时候,是没有指定索引名字的。那不同索引,怎么区分是不是自己的数据?根据前缀来区分。

写数据的时候,使用了不同的前缀。但是,不同的索引,怎么和不同的前缀关联上呢?创建索引的时候,也要指定前缀。意思是,只搜索匹配前缀的key。具体代码和细节,见下文。

到此为止,答案一目了然,就是旧版本写数据的时候,是根据指定索引名字来区分写到不同的索引。但是新版本是根据key前缀来匹配和搜索数据。

key前缀

作用

告诉索引应该索引哪些键。您可以向索引添加多个前缀。由于参数是可选的,因此默认值为 * (所有键)。 redis.io/commands/ft…

默认是匹配所有数据,这也是创建多个索引之后,如果没有指定前缀,就会导致搜索结果混乱的原因。其实原因就是,虽然创建了多个索引,但是每个索引都是会搜索所有的数据。怎么过滤自己的数据?基于key前缀。

创建索引的时候,前缀可以指定一个,或多个。但是,写数据的时候,只能指定一个key。

也就是说,一个索引可以匹配一个前缀,多个前缀,所有前缀。而一个前缀key,也可以属于多个索引。索引和key,变成了多对多的关系。

创建索引

直接看真实的代码示例

protected IndexStatus doInitIndex() throws Exception {
  logger.warn("will init index!");
  return executeSearch(commands -> {
    try {
      CreateOptions.Builder<String, String> cob = CreateOptions.builder();
      cob.defaultLanguage(Language.valueOf(this.defaultLanguage));
      cob.prefix(KEY_PREFIX); //只搜索匹配指定前缀的key
      String result = commands.create(REDISEARCH_INDEX_NAME, cob.build(),
          // fields:
          Field.numeric("id").noIndex().build(),
          Field.numeric("publishAt").build(),
          Field.tag("type").build(),
          Field.text("name").weight(10).build(),
          Field.text("description").weight(5).build(),
          Field.text("content").weight(1).build(),
          Field.text("url").noIndex().build(),
          Field.text("url2").noIndex().build());
      logger.info("FT.CREATE language {}, index {}: {}", REDISEARCH_INDEX_NAME,
          this.defaultLanguage, result);
      return IndexStatus.CREATED;
    } catch (RedisCommandExecutionException e) {
      if ("Index already exists".equals(e.getMessage())) {
        logger.warn("FT: index already exists.");
        return IndexStatus.EXIST;
      } else {
        logger.error("init index error: {}", e.getMessage());
      }
    } catch (Exception e) {
      logger.error("init index error.", e);
    }
    return IndexStatus.FAILED;
  });
}

key前缀

static final String REDISEARCH_INDEX_NAME = "idx:doc"; //索引名字

public static final String KEY_PREFIX = "doc:"; //key前缀

写数据

前缀和前面保持一致

protected void indexSearchableDocuments(List<SearchableDocument> documents) throws Exception {
  logger.info("add searchable documents...");
  executeSearch(commands -> {
    for (SearchableDocument doc : documents) {
      //Map.of方法的key/value不能为null,所以需要转换一下
      if (doc.tags == null) {
        doc.tags = "";
      }

      commands.hset(KEY_PREFIX + doc.id,
          Map.of("id", String.valueOf(doc.id), "type", doc.type, "name", doc.name, "content",
              doc.content, "publishAt", String.valueOf(doc.publishAt), "url", doc.url,
              "url2", doc.url2, "tags", doc.tags));
    }
    logger.info("{} docs indexed.", documents.size());
    return documents.size();
  });
}

官方文档

从历史上看,RediSearch 使用 FT.ADD 命令,该命令在文档和索引之间建立连接。然后,FT.DROP,也是一个历史命令,默认删除文档。在2.x版本中,RediSearch索引哈希和JSON,索引和文档之间的依赖关系不再存在。 redis.io/commands/ft…

旧版本,写数据的add命令,是可以指定索引名字的。但是新版本,已经不能指定名字了。只能通过key前缀来匹配不同的数据。


问chatgpt

在RediSearch 2.x版本中,确实进行了一些重要的变更。在1.x版本中,FT.ADD用于将文档添加到索引,并且存在文档和索引之间的连接。而在2.x版本中,这种连接不再存在,因为索引现在直接与哈希和JSON相关联。

此外,FT.DROP也经历了变化。在1.x版本中,FT.DROP用于删除整个索引,包括相关的文档。在2.x版本中,FT.DROP仍然存在,但它不再删除与索引相关联的文档,只删除索引本身。

这些变化反映了RediSearch在版本之间的演变和改进,以更好地满足用户需求。

删除数据

同上

public boolean unindexSearchableDocument(long id) throws Exception {
  logger.info("remove searchable document {}...", id);
  executeSearch(commands -> {
    long n = commands.del(KEY_PREFIX + id);
    return n == 1;
  });
  return true;
}

总结

增删查改的时候,包括创建索引的时候,必须全部使用同一个key前缀,搜索结果才准确。

否则,搜到的是所有数据。

参考

官方文档 redis.io/commands/ft…

mp.weixin.qq.com/s/Lp8KLgOj6…

mp.weixin.qq.com/s/FgcvoRGkV…