You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现PostgreSQL词表与商标表完整词匹配方案咨询

解决PostgreSQL词汇表与商标表的完整词/词组匹配问题

问题背景

现有两个PostgreSQL表:

CREATE TABLE IF NOT EXISTS words
(
    id bigint NOT NULL DEFAULT nextval('processed_words_id_seq'::regclass),
    keyword character varying(300) COLLATE pg_catalog."default",
);

INSERT INTO words (keyword)
VALUES ('while swam is interesting', 
        'ibm is a company like bmw');

CREATE TABLE IF NOT EXISTS trademarks
(
   id bigint NOT NULL DEFAULT nextval('trademarks_id_seq'::regclass),
   trademark character varying(300) COLLATE pg_catalog."default",
);

INSERT INTO trademarks (trademark)
VALUES ('while swam', 
        'ibm',
        'bmw');

需求是对比words.keyword字段内容,匹配trademarks.trademark中的完整词或词组,禁止部分匹配(例如aus不能匹配australia)。当前Java代码使用indexOf方法,会触发错误的部分匹配,需要修正。

解决方案

方案一:改进Java代码,正则匹配完整单元

核心是利用正则表达式的边界匹配逻辑,确保匹配的是独立的词/词组,而非字符串片段。同时要处理词组中的特殊字符,避免正则语法冲突。

修改findPhrasesInDocument方法:

List<String> findPhrasesInDocument(String doc, List<String> phrases) {
    List<String> foundPhrases = new ArrayList<>();
    for (String phrase : phrases) {
        // 转义词组中的正则特殊字符(如.、*等)
        String escapedPhrase = Pattern.quote(phrase);
        // 匹配规则:词组前后是空白字符或文档首尾,确保是完整单元
        String regex = String.format("(^|\\s)%s($|\\s)", escapedPhrase);
        // 可选忽略大小写,根据业务需求调整
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(doc);
        if (matcher.find()) {
            foundPhrases.add(phrase);
        }
    }
    return foundPhrases;
}

如果业务允许单词边界(如while可以匹配while.这类带标点的情况),可以把正则改成\\b%s\\b,但该规则对包含非单词字符(如连字符)的词组兼容性较差。

方案二:数据库层面匹配(适合大量商标场景)

当trademarks表有数千条数据时,把所有商标加载到Java内存循环匹配效率较低,建议直接在SQL中完成匹配,减少数据传输开销。

方式1:正则匹配SQL

编写原生SQL直接关联两张表,匹配完整词/词组:

SELECT t.trademark
FROM words w
JOIN trademarks t ON w.keyword ~* ('\m' || t.trademark || '\M')
WHERE w.id = ?; -- 传入待检查的words.id
  • ~* 是PostgreSQL不区分大小写的正则匹配运算符
  • \m 表示单词开头,\M 表示单词结尾,确保匹配完整单元

方式2:全文索引优化(高频查询场景)

如果查询频率高,给字段创建全文索引可以大幅提升匹配速度:

-- 创建全文索引
CREATE INDEX idx_words_keyword_tsv ON words USING GIN (to_tsvector('english', keyword));
CREATE INDEX idx_trademarks_trademark_tsv ON trademarks USING GIN (to_tsvector('english', trademark));

-- 查询匹配的商标
SELECT t.trademark
FROM words w
JOIN trademarks t ON to_tsvector('english', w.keyword) @@ to_tsquery('english', replace(t.trademark, ' ', ' & '))
WHERE w.id = ?;

replace(t.trademark, ' ', ' & ') 把多词词组转换成全文查询格式,确保整个词组被完整匹配。

代码整合示例(数据库查询方式)

修改Java业务逻辑,直接通过SQL获取匹配结果:

@Override
public void execute(JobExecutionContext context) {
    Optional<ProcessedWords> keywordOpt = processedWordsService.findRandomKeywordWhereTrademarkBlacklistedIsEmpty();
    if (keywordOpt.isPresent()) {
        ProcessedWords processedWords = keywordOpt.get();
        System.out.println("Checking keyword: " + processedWords.getKeyword());
        
        // 调用DAO查询匹配的商标
        List<String> matchedTrademarks = processedWordsService.findMatchedTrademarks(processedWords.getId());
        
        if (!matchedTrademarks.isEmpty()) {
            String joinedTrademarks = String.join(", ", matchedTrademarks);
            System.out.println("Found blacklisted word in keyword: " + joinedTrademarks);
            processedWordsService.updateTrademarkBlacklistedById(processedWords.getId(), joinedTrademarks);
        }
    }
}

// 在ProcessedWordsRepository中添加查询方法
@Query(value = "SELECT t.trademark FROM words w JOIN trademarks t ON w.keyword ~* ('\\m' || t.trademark || '\\M') WHERE w.id = :wordId", nativeQuery = true)
List<String> findMatchedTrademarks(@Param("wordId") long wordId);

内容的提问来源于stack exchange,提问作者Peter Penzov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 00:45:42