[分词] 基于 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







评论