使用solr实现pinyin分词,针对短词搜索,比如电影搜索

简介: 1,增加solr拼音查询原理: pinyin4j-2.5.0.jar  下载地址:  http://sourceforge.net/projects/pinyin4j/ solr环境使用3.6.2。 2,Token代码: package com.freewebsys.index.analysis;import net.sourceforge.pinyin4j

1,增加solr拼音查询原理:


pinyin4j-2.5.0.jar 
下载地址:
 http://sourceforge.net/projects/pinyin4j/

solr环境使用3.6.2。

2,Token代码:

package com.freewebsys.index.analysis;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.ngram.NGramTokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

public class PinyinTokenizer extends Tokenizer {

	private static final int DEFAULT_BUFFER_SIZE = 512;

	private boolean done = false;
	private int finalOffset;
	private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
	private OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);
	private HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
	// 链接字符串.
	private String padding_char = " ";

	// 构造函数.
	public PinyinTokenizer(Reader reader) {
		this(reader, DEFAULT_BUFFER_SIZE);
	}

	public PinyinTokenizer(Reader input, int bufferSize) {
		super(input);
		termAtt.resizeBuffer(bufferSize);
		format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		format.setVCharType(HanyuPinyinVCharType.WITH_V);
	}

	@Override
	public final boolean incrementToken() throws IOException {
		if (!done) {
			clearAttributes();
			done = true;
			int upto = 0;
			char[] buffer = termAtt.buffer();
			System.out.println(String.valueOf(buffer));
			while (true) {
				final int length = input.read(buffer, upto, buffer.length
						- upto);
				if (length == -1)
					break;
				upto += length;
				if (upto == buffer.length)
					buffer = termAtt.resizeBuffer(1 + buffer.length);
			}
			termAtt.setLength(upto);
			String str = termAtt.toString();
			termAtt.setEmpty();
			StringBuilder stringBuilder = new StringBuilder();
			StringBuilder firstLetters = new StringBuilder();
			StringBuilder cnLetters = new StringBuilder();
			StringBuilder allPinYinLetters = new StringBuilder();

			for (int i = 0; i < str.length(); i++) {
				char c = str.charAt(i);
				if (c < 128) {
					stringBuilder.append(c);
				} else {
					try {
						String[] strs = PinyinHelper.toHanyuPinyinStringArray(
								c, format);
						if (strs != null) {
							// get first result by default
							String first_value = strs[0];
							// TODO more than one pinyin
							// 拼接中文字符.
							cnLetters.append(c);
							cnLetters.append(this.padding_char);
							// 全部拼音字符.
							allPinYinLetters.append(first_value);
							// 拼接拼音字符.
							stringBuilder.append(first_value);
							stringBuilder.append(this.padding_char);
							// 拼接首字母字符.
							firstLetters.append(first_value.charAt(0));

						}
					} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
						badHanyuPinyinOutputFormatCombination.printStackTrace();
					}
				}
			}

			// let's join them

			termAtt.append(stringBuilder.toString());
			termAtt.append(this.padding_char);
			termAtt.append(cnLetters.toString());
			termAtt.append(this.padding_char);
			termAtt.append(firstLetters.toString());
			termAtt.append(this.padding_char);
			// 将全部拼音分词成一个一个输入数据索引。
			termAtt.append(mergeNGramPinYin(allPinYinLetters.toString()));

			finalOffset = correctOffset(upto);
			offsetAtt.setOffset(correctOffset(0), finalOffset);
			return true;
		}
		return false;
	}

	@Override
	public final void end() {
		// set final offset
		offsetAtt.setOffset(finalOffset, finalOffset);
	}

	@Override
	public void reset(Reader input) throws IOException {
		super.reset(input);
		this.done = false;
	}

	public static String mergeNGramPinYin(String allPinYin) {
		// 读取字符串
		StringReader reader = new StringReader(allPinYin);
		// 设置临时变量
		String[] pinYinBuffer = null;
		if (StringUtils.isNotBlank(allPinYin)) {
			// 设置数组长度
			pinYinBuffer = new String[allPinYin.length()];
			StringBuffer tmpAppendPinYin = new StringBuffer();
			NGramTokenizer nGramTokenizer = new NGramTokenizer(reader);
			for (int i = 0; i < allPinYin.length(); i++) {
				try {
					// 循环递增
					nGramTokenizer.incrementToken();
					// 取得解析后的字符串
					CharTermAttribute charTermAttribute = nGramTokenizer
							.getAttribute(CharTermAttribute.class);
					tmpAppendPinYin.append(String.valueOf(
							charTermAttribute.buffer()).trim());
					// 每次都给数据赋值.
					pinYinBuffer[i] = tmpAppendPinYin.toString();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		// 将结果按照空格合并.
		return StringUtils.join(pinYinBuffer, " ");

	}

}

PinyinTokenizerFactory:
package com.freewebsys.index.analysis;

import org.apache.lucene.analysis.Tokenizer;
import org.apache.solr.analysis.BaseTokenizerFactory;
import java.io.Reader;

/**
 */
public class PinyinTokenizerFactory extends BaseTokenizerFactory {

	@Override
	public Tokenizer create(Reader input) {
		return new PinyinTokenizer(input);
	}

}


3,solr 的schema.xml配置:

对于 index和query分开配置:
		<!-- standard_text.标准分词.创建使用pinyin分词,搜索不使用. -->
		<fieldType name="standard_text" class="solr.TextField"
			positionIncrementGap="100">
			<analyzer type="index">
				<tokenizer class="com.freewebsys.index.analysis.PinyinTokenizerFactory" />
				<filter class="solr.LowerCaseFilterFactory" />
				<filter class="solr.WordDelimiterFilterFactory"
					generateWordParts="1" generateNumberParts="1" catenateWords="0"
					catenateNumbers="1" catenateAll="0" splitOnCaseChange="1" />
			</analyzer>
			<analyzer type="query">
				<tokenizer class="solr.StandardTokenizerFactory" />
			</analyzer>
		</fieldType>

4,使用分词效果:





如果在管理后台测试可以看到上面的分词结果,就说明pinyin分词配置好了。

5,不足:

目前看在搜索完成后,高亮显示还有点问题,因为索引里面增加了很多拼音。

局限性,只能简单对比较段的内容进行分词,比如商品,电影,图书等,要是文字太长使用NGramTokenizer 分词就会增加一堆没用的pinyin。



目录
相关文章
|
关系型数据库 MySQL 数据库
n8n自动化工具部署与使用
n8n是一款开源的工作流自动化工具,类似于IFTTT。它的优点是开源、可以自托管、下载安装方便、易于使用,可以互联上百种服务。n8n基于节点能够将任何工具连接在一起,轻松部署不同类型的任务。它可以做很多事情,比如:从数据库中获取数据后下载为excel然后通过邮件发送给其他人。
9789 1
|
自然语言处理 Go 索引
startOffset must be non-negative, and endOffset must be >= startOffset, and offsets must not go backwards startOffset=615,endOffset=617,lastStartOffset=616 for field 'convContent.content'
【7月更文挑战第4天】startOffset must be non-negative, and endOffset must be >= startOffset, and offsets must not go backwards startOffset=615,endOffset=617,lastStartOffset=616 for field 'convContent.content'
|
网络协议 Java
elasticsearch7.1 安装启动报错
elasticsearch7.1 安装启动报错
263 1
|
自然语言处理
Elasticsearch+IK+pinyin自定义分词器
Elasticsearch+IK+pinyin自定义分词器
195 0
|
Android开发 开发者 Windows
|
缓存 自然语言处理 Java
|
4天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
3天前
|
云安全 人工智能 自然语言处理
阿里云x硅基流动:AI安全护栏助力构建可信模型生态
阿里云AI安全护栏:大模型的“智能过滤系统”。
|
3天前
|
人工智能 自然语言处理 自动驾驶
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知