写点什么

[分词] 基于 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 结构的数据提升支持力度。

参考

  1. https://pap-docs.pap.net.cn/

  2. https://gitee.com/alexgaoyh/pap-lucene-spring-boot-starter


发布于: 14 小时前阅读数: 14
用户头像

alexgaoyh

关注

DevOps 2013-12-08 加入

https://gitee.com/alexgaoyh

评论

发布
暂无评论
[分词]基于Lucene8版本的JSON结构分词器(属性值集合)_Java_alexgaoyh_InfoQ写作社区