MeiliSearch
MeiliSearch是一个轻量级的搜索引擎,也是AI用来查询Memory实现混合搜索的轻量级实现,下面是一个简单的demo示例
docker run -it --rm \
--name meilisearch \
-d -p 7700:7700 \
-e MEILI_ENV='development' \
-e MEILI_MASTER_KEY='123456' \
-v /data/meili_data:/meili_data getmeili/meilisearch:v1.0.2
package com.scdq.env.water.patrol.transfer.controller;
import cn.hutool.json.JSONUtil;
import com.meilisearch.sdk.Client;
import com.meilisearch.sdk.Config;
import com.meilisearch.sdk.Index;
import com.meilisearch.sdk.exceptions.MeilisearchException;
import com.meilisearch.sdk.json.GsonJsonHandler;
import com.meilisearch.sdk.model.Results;
import com.meilisearch.sdk.model.SearchResult;
import com.meilisearch.sdk.model.TaskInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
/**
* Meilisearch 数据导入控制器
* 功能:创建索引、导入movies.json数据到Meilisearch
*/
@Slf4j
@RestController
@RequestMapping("/meiliSearch")
public class DemoController {
// Meilisearch 配置常量
private static final String MEILI_URL = "http://xx.xx.xx.xx:7700";
private static final String MEILI_API_KEY = "123456";
private static final String INDEX_NAME = "movies";
private static final String JSON_FILE_PATH = "D:\\tmp\\movies.json";
/**
* 初始化 Meilisearch 客户端(修复 Gson 字段冲突问题)
*/
private Client getMeilisearchClient() {
// 初始化配置(先设处理器,再设URL/API Key)
Config config = new Config(MEILI_URL, MEILI_API_KEY);
GsonJsonHandler gsonJsonHandler = new GsonJsonHandler();
config.setJsonHandler(gsonJsonHandler);
return new Client(config);
}
// ====================== 原有接口 ======================
@GetMapping("/initAll")
public ResponseEntity<String> initAll() {
Client client = getMeilisearchClient();
try {
log.info("Meilisearch客户端创建成功");
createIndex(client);
log.info("索引[{}]创建请求已提交", INDEX_NAME);
addDocuments(client);
log.info("JSON数据导入请求已提交,文件路径:{}", JSON_FILE_PATH);
return ResponseEntity.ok("操作成功:索引创建 + 数据导入请求已提交");
} catch (MeilisearchException e) {
log.error("Meilisearch操作失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Meilisearch操作失败:" + e.getMessage());
} catch (IOException e) {
log.error("读取JSON文件失败", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("读取JSON文件失败:" + e.getMessage());
} catch (Exception e) {
log.error("未知错误", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("未知错误:" + e.getMessage());
}
}
@GetMapping("/testConnection")
public ResponseEntity<String> testConnection() {
Client client = getMeilisearchClient();
try {
Results<Index> indexes = client.getIndexes();
for (Index index : indexes.getResults()) {
System.out.println("Index: " + index.getUid());
System.out.println("Primary Key: " + index.getConfig());
}
System.out.println();
// 执行测试逻辑
return ResponseEntity.ok("Meilisearch连接成功");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("连接失败:" + e.getMessage());
}
}
// ====================== 新增接口(改/删/查) ======================
/**
* 新增/更新文档接口
* 请求示例:POST /meiliSearch/document
* Body:{"id":"1","zn":"测试测试","en":"test"} 或 [{"id":"1"},{"id":"2"}]
*/
@PostMapping("/document")
public ResponseEntity<Map<String, Object>> updateDocument(
@RequestBody String documentJson, // 接收JSON格式的文档数据
@RequestParam(defaultValue = "movies") String indexName) {
Map<String, Object> result = new HashMap<>();
Client client = getMeilisearchClient();
try {
Index index = client.index(indexName);
TaskInfo taskInfo = index.updateDocuments(documentJson);
result.put("success", true);
result.put("message", "文档更新任务提交成功");
result.put("taskUid", taskInfo.getTaskUid());
result.put("status", taskInfo.getStatus());
return ResponseEntity.ok(result);
} catch (IllegalArgumentException e) {
log.error("参数错误", e);
result.put("success", false);
result.put("message", "参数错误:" + e.getMessage());
return ResponseEntity.badRequest().body(result);
} catch (MeilisearchException e) {
log.error("更新文档失败", e);
result.put("success", false);
result.put("message", "更新文档失败:" + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
/**
* 删除单个文档接口
* 请求示例:DELETE /meiliSearch/document?documentId=2&indexName=movies
*/
@DeleteMapping("/document")
public ResponseEntity<Map<String, Object>> deleteDocument(
@RequestParam String documentId, // 要删除的文档ID
@RequestParam(defaultValue = "movies") String indexName) {
Map<String, Object> result = new HashMap<>();
Client client = getMeilisearchClient();
try {
Index index = client.index(indexName);
TaskInfo taskInfo = index.deleteDocument(documentId);
result.put("success", true);
result.put("message", "文档删除任务提交成功");
result.put("documentId", documentId);
result.put("taskUid", taskInfo.getTaskUid());
result.put("status", taskInfo.getStatus());
return ResponseEntity.ok(result);
} catch (IllegalArgumentException e) {
log.error("参数错误", e);
result.put("success", false);
result.put("message", "参数错误:" + e.getMessage());
return ResponseEntity.badRequest().body(result);
} catch (MeilisearchException e) {
log.error("删除文档失败", e);
result.put("success", false);
result.put("message", "删除文档失败:" + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
/**
* 搜索文档接口
* 请求示例:GET /meiliSearch/search?keyword=Room&indexName=movies
*/
@GetMapping("/search")
public ResponseEntity<Map<String, Object>> searchDocument(
@RequestParam String keyword, // 搜索关键词
@RequestParam(defaultValue = "movies") String indexName) {
Map<String, Object> result = new HashMap<>();
Client client = getMeilisearchClient();
try {
Index index = client.index(indexName);
SearchResult searchResult = index.search(keyword);
result.put("success", true);
result.put("keyword", keyword);
result.put("totalHits", searchResult.getHits());
result.put("hits", searchResult.getHits());
result.put("rawResult", JSONUtil.toJsonStr(searchResult));
return ResponseEntity.ok(result);
} catch (IllegalArgumentException e) {
log.error("参数错误", e);
result.put("success", false);
result.put("message", "参数错误:" + e.getMessage());
return ResponseEntity.badRequest().body(result);
} catch (MeilisearchException e) {
log.error("搜索失败", e);
result.put("success", false);
result.put("message", "搜索失败:" + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);
}
}
// ====================== 私有工具方法 ======================
private void createIndex(Client client) throws MeilisearchException {
TaskInfo taskInfo = client.createIndex(INDEX_NAME, "id");
log.info("创建索引任务ID:{},状态:{}", taskInfo.getTaskUid(), taskInfo.getStatus());
}
private void addDocuments(Client client) throws MeilisearchException, IOException {
Path fileName = Path.of(JSON_FILE_PATH);
if (!Files.exists(fileName)) {
throw new IOException("JSON文件不存在:" + JSON_FILE_PATH);
}
String moviesJson = Files.readString(fileName);
log.info("JSON文件读取成功,文件大小:{} 字节", moviesJson.getBytes().length);
Index index = client.index(INDEX_NAME);
TaskInfo taskInfo = index.addDocuments(moviesJson);
log.info("导入数据任务ID:{},状态:{}", taskInfo.getTaskUid(), taskInfo.getStatus());
}
}
<dependency>
<groupId>com.meilisearch.sdk</groupId>
<artifactId>meilisearch-java</artifactId>
<version>0.15.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
注意:meilisearch-java的maven依赖选择0.15.0版本,这个版本没有gson的相关报错
浙公网安备 33010602011771号