[分词] 基于 Lucene8 版本的 JSON 结构分词器 (属性值集合)
作者:alexgaoyh
- 2024-01-22 河南
本文字数:2749 字
阅读完需:约 9 分钟
介绍
在实际场景中,不排除将 JSON 字符串存储至 Database 中,并且现有的绝大部分数据库都支持了 JSON 结构,但是在将关系型数据库中的数据索引至 Lucene 的时候,针对 JSON 结构需要进行额外的处理。
解决方案
采用自定义分词器的思路,先将 JSON 字符串进行解析,获得所有属性值,并将属性值当做结果存储至 Lucene 中。
实现代码
详见如下测试代码,此代码调用自定义的 JSON 分词器。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {com.pap.lucene.LuceneAutoConfiguration.class})
@TestPropertySource("classpath:application.properties")
@FixMethodOrder(MethodSorters.JVM)
public class JSONTest {
@Autowired
private SearcherManager searcherManager;
@Autowired
private IndexWriter indexWriter;
@Test
public void init() throws Exception {
indexWriter.deleteAll();
indexDocument(indexWriter, "{\"name\":\"alexgaoyh\",\"sex\":\"male\",\"city\":\"Xu Chang\",\"phones\":[\"alexgaoyh@sina.com\",\"pap.net.cn\"],\"ext\":{\"skill\":\"programmer\",\"zip\":\"461000\",\"remark\":{\"language\":[\"chinese\",\"english\"],\"main-development\":\"java\"}}}");
indexWriter.commit();
// 分词结果: ['alexgaoyh','male','XuChang','alexgaoyh@sina.com','pap.net.cn','programmer','461000','chinese','english','java']
}
@Test
public void searchInOrder() throws Exception {
TermQuery termQuery = new TermQuery(new Term("json", "alexgaoyh"));
searcherManager.maybeRefresh();
IndexSearcher indexSearcher = searcherManager.acquire();
TopDocs search = indexSearcher.search(termQuery, 10);
}
private static void indexDocument(IndexWriter indexWriter, String json) throws IOException {
Document doc = new Document();
doc.add(new TextField("json", json, Field.Store.YES));
doc.add(new SortedDocValuesField("json", new BytesRef(json)));
indexWriter.addDocument(doc);
}
}
复制代码
public class JSONAnalyzer extends Analyzer {
@Override
protected TokenStreamComponents createComponents(String s) {
Tokenizer tokenizer = new JSONTokenizer();
return new TokenStreamComponents(tokenizer);
}
}
public final class JSONTokenizer extends Tokenizer {
private final CharTermAttribute charTermAttribute = addAttribute(CharTermAttribute.class);
private final OffsetAttribute offsetAttribute = addAttribute(OffsetAttribute.class);
private Iterator<Map<String, Object>> termIterator;
public JSONTokenizer() {
super();
}
@Override
public boolean incrementToken() throws IOException {
clearAttributes();
if (termIterator == null) {
int intValueOfChar;
String text = "";
while ((intValueOfChar = input.read()) != -1) {
text += (char) intValueOfChar;
}
List<Map<String, Object>> terms = JsonValueExtractor.segment(text);
termIterator = terms.iterator();
}
if (termIterator.hasNext()) {
Map<String, Object> termMap = termIterator.next();
charTermAttribute.append(termMap.get("word").toString());
charTermAttribute.setLength(termMap.get("word").toString().length());
offsetAttribute.setOffset(correctOffset(Integer.parseInt(termMap.get("offset").toString())), correctOffset(Integer.parseInt(termMap.get("offset").toString()) + termMap.get("word").toString().length()));
return true;
} else {
termIterator = null;
return false;
}
}
}
public class JsonValueExtractor {
public static List<Map<String, Object>> segment(String jsonString) {
List<Map<String, Object>> segmentMapList = new ArrayList<>();
List<String> values = extractValuesUsingJava(jsonString);
if (values != null && values.size() > 0) {
for (String term : values) {
Map<String, Object> segmentMap = new HashMap<>();
segmentMap.put("word", term);
segmentMap.put("offset", jsonString.indexOf(term));
segmentMapList.add(segmentMap);
}
}
return segmentMapList;
}
public static List<String> extractValuesUsingJava(String jsonString) {
List<String> values = new ArrayList<>();
try (JsonReader jsonReader = Json.createReader(new StringReader(jsonString))) {
JsonValue jsonValue = jsonReader.read();
traverseJsonValueUsingJava(jsonValue, values);
}
return values;
}
private static void traverseJsonValueUsingJava(JsonValue jsonValue, List<String> values) {
switch (jsonValue.getValueType()) {
case OBJECT:
JsonObject jsonObject = (JsonObject) jsonValue;
for (Map.Entry<String, JsonValue> entry : jsonObject.entrySet()) {
traverseJsonValueUsingJava(entry.getValue(), values);
}
break;
case ARRAY:
JsonArray jsonArray = (JsonArray) jsonValue;
for (JsonValue element : jsonArray) {
traverseJsonValueUsingJava(element, values);
}
break;
default:
values.add(jsonValue.toString().substring(1, jsonValue.toString().length() - 1));
break;
}
}
}
复制代码
总结
此代码在 Lucene8.6.2 版本下通过测试,采用自定义 JSON 分词器的方式,对 JSON 结构的数据提升支持力度。
参考
划线
评论
复制
发布于: 14 小时前阅读数: 14
版权声明: 本文为 InfoQ 作者【alexgaoyh】的原创文章。
原文链接:【http://xie.infoq.cn/article/99a5bb4bfa225fc77bcea1fa1】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
alexgaoyh
关注
DevOps 2013-12-08 加入
https://gitee.com/alexgaoyh
评论