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

PostgreSQL多词匹配:Java实现商标黑名单检测方案问询

问题描述

我有如下用于存储词汇的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 ('swam', 
        'ibm',
        'bmw');

注:修正了原插入语句的表名错误,原insert into words (trademarks)应为insert into trademarks (trademark)

trademarks表中存储数千个已注册的商标名称,需要检测words表的keyword字段内容是否包含单个词汇或词汇组形式的商标。比如words.keyword中的while swam is interesting要能匹配到trademarks里的swam,ibm is a company like bmw要能匹配到ibm和bmw。

现有Java代码只能匹配完整字符串,无法实现单词/短语的匹配,代码如下:

Optional<ProcessedWords> keywords = processedWordsService.findRandomKeywordWhereTrademarkBlacklistedIsEmpty();

if(keywords.isPresent())
{
    List<BlacklistedWords> blacklistedWords = blacklistedWordsService.findAll();
    List<String> list = new ArrayList<>();
    for(BlacklistedWords item:  blacklistedWords){
        list.add(item.getKeyword());
    }

    ProcessedWords processedWords = keywords.get();
    String keyword = processedWords.getKeyword();

    if(list.contains(keyword))
    {
        System.out.println("Found blacklisted word in keyword: " + keyword);
    }
}

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Entity
@Table(name = "trademarks")
public class BlacklistedWords implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, updatable = false, nullable = false)
    private long id;

    @Column(name = "trademark", length = 200, unique = true)
    private String keyword;
}
解决方案

方法一:Java端实现单词/短语匹配

思路

  1. 将商标列表转换为正则表达式,为每个商标添加单词边界规则,避免部分字符匹配(比如防止swam匹配swamp)。
  2. 用正则匹配目标字符串,提取所有匹配的商标。

修改后的代码

Optional<ProcessedWords> keywords = processedWordsService.findRandomKeywordWhereTrademarkBlacklistedIsEmpty();

if (keywords.isPresent()) {
    List<BlacklistedWords> blacklistedWords = blacklistedWordsService.findAll();
    
    // 构建正则表达式:匹配完整单词/短语,转义特殊字符避免正则语法冲突
    String regexPattern = blacklistedWords.stream()
            .map(item -> "\\b" + Pattern.quote(item.getKeyword()) + "\\b")
            .collect(Collectors.joining("|"));
    Pattern pattern = Pattern.compile(regexPattern, Pattern.CASE_INSENSITIVE); // 可选忽略大小写

    ProcessedWords processedWords = keywords.get();
    String keywordContent = processedWords.getKeyword();
    Matcher matcher = pattern.matcher(keywordContent);

    // 遍历所有匹配到的商标
    while (matcher.find()) {
        String matchedTrademark = matcher.group();
        System.out.println("Found blacklisted trademark: " + matchedTrademark + " in content: " + keywordContent);
        // 可添加后续处理逻辑,比如标记该词汇为包含黑名单商标
    }
}

说明

  • \\b代表单词边界,确保匹配的是完整的单词或短语。
  • Pattern.quote()用于转义商标中的特殊字符(如., *等正则符号),避免匹配逻辑出错。
  • Pattern.CASE_INSENSITIVE为可选配置,可实现大小写不敏感匹配,根据业务需求调整。

方法二:数据库端直接查询(更高效)

思路

如果trademarks表数据量较大(数千条),将所有数据拉取到Java端处理会影响性能,直接在数据库层做关联查询效率更高。

实现方式

方式1:使用PostgreSQL的LIKE查询(适合简单单词匹配)

修改processedWordsService的查询方法,直接在SQL中关联两张表:

SELECT w.*, t.trademark 
FROM words w
JOIN trademarks t ON w.keyword LIKE '% ' || t.trademark || ' %' 
                   OR w.keyword LIKE t.trademark || ' %' 
                   OR w.keyword LIKE '% ' || t.trademark
                   OR w.keyword = t.trademark
WHERE w.trademark_blacklisted IS NULL;

注:trademark_blacklisted对应原代码中findRandomKeywordWhereTrademarkBlacklistedIsEmpty()的过滤字段,需根据实际表结构调整

方式2:使用PostgreSQL的正则表达式查询

SELECT w.*, t.trademark
FROM words w
JOIN trademarks t ON w.keyword ~* ('\m' || t.trademark || '\M')
WHERE w.trademark_blacklisted IS NULL;
  • ~*表示大小写不敏感的正则匹配
  • \m是PostgreSQL的单词起始边界,\M是单词结束边界,作用等同于Java中的\\b

Java端直接获取匹配结果即可:

List<ProcessedWordsWithTrademark> matchedRecords = processedWordsService.findKeywordsWithBlacklistedTrademarks();
for (var record : matchedRecords) {
    System.out.println("Found trademark: " + record.getTrademark() + " in keyword: " + record.getKeyword());
}

性能优化建议

  • 若trademarks表数据持续增长,建议给trademarks.trademark字段添加索引,或使用PostgreSQL的全文检索功能(创建tsvector索引)提升查询效率。
  • 采用Java端处理时,建议缓存商标列表(如使用Guava Cache或Spring Cache),避免每次查询都从数据库拉取数据。

内容的提问来源于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.30 23:18:33