当前位置:

多个关键字用or、and、包含、不包含动态拼接为正则表达式和SQL查询条件

访客 2024-01-05 425 0

前言

不知道大家有没有做过这种需求:在某字符串中,根据多个关键字去判断这串字符串是否满足条件。如下图:

亦或是

如果说要根据图二的关键字去数据库中查询符合条件的数据,要怎么做?是不是感觉头都大了?
暂且先不说包含、不包含,我们先来说说或、且。多个or、and是不能同时并列使用的,我们需要用括号将连续的、相同的or或者and用括号括起来。

比如图一,我们按顺序连起来就是:关键字1and关键字2or关键字3or关键字4and关键字5or关键字6
但是我们肯定不能直接就这么用,我们得捋一捋要把括号加在哪,哪些是并列的可以放在一个括号里的,哪些又是放在括号外面的。按照我的想法是,把连续and的关键字放在括号里,括号外面就是or。
所以得出来的条件就是:(关键字1and关键字2)or关键字3or(关键字4and关键字5)or关键字6

那么问题又来了,我们要怎么把这个条件转换成SQL的where条件?mysql中,包含指定字符串我们用locate函数,locate(‘关键字1’,字段1)>0就表示字段1的值包含关键字1,如果是<0则表示不包含。

于是,我们拼出来的where条件就是:

#(关键字1and关键字2)or关键字3or(关键字4and关键字5)or关键字6(locate('关键字1',字段名)>0andlocate('关键字2',字段名)>0)orlocate('关键字3',字段名)>0or(locate('关键字4',字段名)>0andlocate('关键字5',字段名)>0)orlocate('关键字6',字段名)>0

where条件拼出来了,那如果我觉得用locate拼的条件太长了,想用正则或者需要在java代码中,用正则表达式匹配呢?该怎么把这个条件转换成正则表达式?

包含的话,在正则中我们可以用.*,但这个是贪婪匹配,匹配任意次,我们只需要匹配一次就够了,所以可以用.*?,and的话,直接括号并列就行,or的话则用|表示。
于是得到得正则表达式就是:

((.*?关键字1.*?)(.*?关键字2.*?))|(.*?关键字3.*?)|((.*?关键字4.*?)(.*?关键字5.*?))|(.*?关键字6.*?)

校验

得到了两种查询方式的条件,那我们来验证一下,准备一张测试表:

DROPTABLEIFEXISTS`test1`;CREATETABLE`test1`(`id`bigintNOTNULLAUTO_INCREMENT,`content`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_general_ciNULLDEFAULTNULLCOMMENT'内容',PRIMARYKEY(`id`)USINGBTREE)ENGINE=InnoDBCHARACTERSET=utf8mb4COLLATE=utf8mb4_general_ciROW_FORMAT=Dynamic;INSERTINTO`test1`VALUES(1,'这里是测试内容关键字1内容,哈哈哈关键字2,内容内容、关键字3、测试关键字4测试;测试内容关键字5,关键字6。');INSERTINTO`test1`VALUES(2,'生死关键字1并非我能关键字4左右,善恶也非关键字4我能独断');INSERTINTO`test1`VALUES(3,'但我之关键字4本心将为我选择关键字5方向,我之使命将为关键字6我决断对错,纵使这世间混关键字1沌不堪,我也希望用关键字3这一身赤关键字3羽开辟曙光。');INSERTINTO`test1`VALUES(4,'神州关键字4之地,守护者关键字4沉眠已久。关键字1铭刻编年,关键字7此刻乃是关键字4苏醒之时。奔赴关键字7远方,重燃此世之炽。');INSERTINTO`test1`VALUES(5,'以精准完关键字5美的攻击关键字5,击退关键字6一切混沌与关键字7无序!关键字8');INSERTINTO`test1`VALUES(6,'身披关键字7秩序之衣,穷尽关键字7武道之关键字5极。');INSERTINTO`test1`VALUES(7,'漫漫关键字1白夜,划过天际的关键字2星辰不计关键字1其数,但旷关键字2古闪耀关键字3的明星,仅此一人。');INSERTINTO`test1`VALUES(8,'就昆仑山搭街坊卡拉,天热广泛的撒旦发射点。关键字5');INSERTINTO`test1`VALUES(9,'太艰苦拉关键字6萨大家发了肯定,解开了简历库进口量。');INSERTINTO`test1`VALUES(10,'这个是测试内关键字3容呀,测试测试测试,内容内容内容这个是测试内容呀。');INSERTINTO`test1`VALUES(11,'空手道解放昆仑山搭关键字4街坊,特我认为若i速关键字4冻i夫。解开了揭开角度来看,请问热关键字4望热望热热退热贴,烹饪例如可通过大幅高开。');INSERTINTO`test1`VALUES(12,'哈哈哈哈关键字5哈哈,请问请问,啦啦啦关键字5啦啦啦啦,了哈哈哈哈哈哈,钱关键字5钱钱钱钱钱钱,嘎嘎嘎嘎嘎嘎嘎反反复复烦烦烦。');

验证方式一:

SELECT*FROMtest1WHERE(locate('关键字1',content)>0andlocate('关键字2',content)>0)orlocate('关键字3',content)>0or(locate('关键字4',content)>0andlocate('关键字5',content)>0)orlocate('关键字6',content)>0


验证方式二:

SELECT*FROMtest1WHEREcontentREGEXP'((.*?关键字1.*?)(.*?关键字2.*?))|(.*?关键字3.*?)|((.*?关键字4.*?)(.*?关键字5.*?))|(.*?关键字6.*?)'


这么看,两种条件方式查出来的结果都是一样的。

既然这么做的思路没问题,那我们接下来要解决的就是怎么动态去拼接出这个where条件和正则表达式。

思路

1、存储方式

第一步我们先要考虑清楚,图一图二添加了多个关键字后,要以什么形式存储能更方便我们后续进行动态拼接。
我的想法是,直接把这些关键字用or、and连起来,后续用的时候也是用or、and进行分割,像这样:

#图一:or、and也可以用其他符号代替,比如or用|,and用&关键字1and关键字2or关键字3or关键字4and关键字5or关键字6#图二:包含、不包含则分别用is、no代替is关键字1orno关键字2andno关键字3andis关键字4oris关键字5andis关键字6orno关键字7

2、实现

图一实现

我们先用or进行分割(数组一),每一个or后面都拼接上表示“或”的字符:正则用|表示或,SQL用or表示。

遍历数组一,对每一个遍历的元素用and进行分割(数组二),如果数组二长度大于1,说明有连续多个and,这时我们要在这几个连续的and最外面加上括号,把它们括起来,表示这几个条件要同时满足。

即:关键字1and关键字2and关键字3——>((关键字1)and(关键字2)and(关键字3))

用代码实现:

/***根据关键字动态拼接正则表达式(没有包含、不包含)*@paramkeyWord关键字*/publicstaticStringregByKeyWord1(StringkeyWord){String[]orSplit=keyWord.split("or");StringBufferresult=newStringBuffer();for(inti=0;i<orSplit.length;i){String[]andSplit=orSplit[i].split("and");if(andSplit.length>1){result.append("(");for(intj=0;j<andSplit.length;j){result.append("(.*?"andSplit[j]".*?)");}result.append(")");}else{result.append("(.*?"orSplit[i]".*?)");}if(i<orSplit.length-1){result.append("|");}}System.out.println(result.toString());returnresult.toString();}/***根据关键字动态拼接SQL查询条件(没有包含、不包含)*@paramkeyWord关键字*@paramfield数据库条件字段*/publicstaticStringwhereByKeyWord1(StringkeyWord,Stringfield){String[]orSplit=keyWord.split("or");StringBufferresult=newStringBuffer();for(inti=0;i<orSplit.length;i){String[]andSplit=orSplit[i].split("and");if(andSplit.length>1){result.append("(");for(intj=0;j<andSplit.length;j){result.append("locate('"andSplit[j]"',"field")>0");if(j<andSplit.length-1){result.append("and");}}result.append(")");}else{result.append("locate('"orSplit[i]"',"field")>0");}if(i<orSplit.length-1){result.append("or");}}System.out.println(result.toString());returnresult.toString();}

得到的结果,和上面自己推算的条件、正则是一样的。

图二实现

图二有包含、不包含,就又要复杂一点了,特别是正则表达式!!!

因为本身对正则表达式也不熟,用的时候都是直接在网上找。单个包含或不包含就没什么难度,复杂的是多个包含不包含要一起使用,着实花了我好大一番心思。

我先把代码贴出来:

/***根据关键字动态拼接正则表达式(有包含、不包含)*@paramkeyWord关键字*/publicstaticStringregByKeyWord2(StringkeyWord){String[]orSplict=keyWord.split("or");StringBufferresult=newStringBuffer();for(inti=0;i<orSplict.length;i){String[]andSplit=orSplict[i].split("and");if(andSplit.length>1){StringBuffersb1=newStringBuffer();StringBuffersb2=newStringBuffer();result.append("(");intc1=0;for(intj=0;j<andSplit.length;j){if(andSplit[j].contains("no")){if(c1==0)sb1.append("((?!");if(c1>0)sb1.append("|");sb1.append(andSplit[j].replace("no",""));c1;}if(j==andSplit.length-1&&sb1.length()>0)sb1.append(").)*");}intc2=0;for(intj=0;j<andSplit.length;j){if(!andSplit[j].contains("no")){if(sb1.length()>0){if(c2==0)sb2.append(sb1.toString());sb2.append(andSplit[j].replace("is","")).append(sb1.toString());c2;}else{sb2.append("(.*?"andSplit[j].replace("is","")".*?)");}}}result.append(sb2.toString());result.append(")");}else{if(orSplict[i].contains("no")){result.append("((?!").append(orSplict[i].replace("no","")).append(").)*");}else{result.append("(.*?"orSplict[i].replace("is","")".*?)");}}if(i<orSplict.length-1){result.append("|");}}System.out.println(result.toString());returnresult.toString();}/***根据关键字动态拼接SQL查询条件(有包含、不包含)*@paramkeyWord关键字*@paramfield数据库条件字段*/publicstaticStringwhereByKeyWord2(StringkeyWord,Stringfield){String[]orSplit=keyWord.split("or");StringBufferresult=newStringBuffer();for(inti=0;i<orSplit.length;i){String[]andSplit=orSplit[i].split("and");if(andSplit.length>1){result.append("(");for(intj=0;j<andSplit.length;j){if(andSplit[j].contains("no")){result.append("locate('"andSplit[j].replace("no","")"',"field")<0");}else{result.append("locate('"andSplit[j].replace("is","")"',"field")>0");}if(j<andSplit.length-1){result.append("and");}}result.append(")");}else{if(orSplit[i].contains("no")){result.append("locate('"orSplit[i].replace("no","")"',"field")<0");}else{result.append("locate('"orSplit[i].replace("is","")"',"field")>0");}}if(i<orSplit.length-1){result.append("or");}}System.out.println(result.toString());returnresult.toString();}

where条件拼接没什么好说的,它和whereByKeyWord1一样。主要讲讲正则表达式该怎么写。

不包含用正则表示:((?!关键字1).)*//不包含关键字1和关键字2多个并列的不包含用正则表示:((?!关键字1|关键字2).)*//包含关键字3,但不包含关键字1和关键字2包含、不包含同时使用:((?!关键字1|关键字2).)*关键字3((?!关键字1|关键字2).)*//包含关键字3和关键字4,但不包含关键字1和关键字2多个包含、不包含同时使用:(((?!关键字1|关键字2).)*关键字3((?!关键字1|关键字2).)*关键字4((?!关键字1|关键字2).)*)

是不是感觉很复杂,我也觉得很复杂。

包含不包含同时用时,要先写不包含,在不包含最后的星号后面接上包含的关键字。

只是我不明白为啥我前面明明有写不包含的表达式,为啥包含后面还要再加上不包含的表达式?

如果有多个包含、不包含同时使用,那就会像第四条表达式一样,开头要先写不包含,然后接着第一个包含,再写不包含后,接着第二个包含,然后又要写不包含。如此循环接下去。那这表达式不可谓不长。

不知道有没有更简单的表达式。

结果

//图一StringkeyWord="关键字1and关键字2or关键字3or关键字4and关键字5or关键字6";Stringreg=DynamicWhereUtil.regByKeyWord1(keyWord);Stringwhere=DynamicWhereUtil.whereByKeyWord1(keyWord,"content");//图一输出(换行是为了方便查看,实际输出结果没有换行)((.*?关键字1.*?)(.*?关键字2.*?))|(.*?关键字3.*?)|((.*?关键字4.*?)(.*?关键字5.*?))|(.*?关键字6.*?)(locate('关键字1',content)>0andlocate('关键字2',content)>0)orlocate('关键字3',content)>0or(locate('关键字4',content)>0andlocate('关键字5',content)>0)orlocate('关键字6',content)>0//图二StringkeyWord="is关键字1orno关键字2andno关键字3andis关键字4andis关键字5oris关键字6andis关键字7orno关键字8";Stringreg=DynamicWhereUtil.regByKeyWord2(keyWord);Stringwhere=DynamicWhereUtil.whereByKeyWord2(keyWord,"content");//图二输出(换行是为了方便查看,实际输出结果没有换行)(.*?关键字1.*?)|(((?!关键字2|关键字3).)*关键字4((?!关键字2|关键字3).)*关键字5((?!关键字2|关键字3).)*)|((.*?关键字6.*?)(.*?关键字7.*?))|((?!关键字8).)*locate('关键字1',content)>0or(locate('关键字2',content)<0andlocate('关键字3',content)<0andlocate('关键字4',content)>0andlocate('关键字5',content)>0)or(locate('关键字6',content)>0andlocate('关键字7',content)>0)orlocate('关键字8',content)<0

最后

终于是实现了开头的那两个需求,以下是校验正则的测试代码:

publicstaticvoidmain(String[]args){//1、只有and、orStringkeyWord1="关键字1and关键字2or关键字3or关键字4and关键字5or关键字6";Stringreg1=DynamicWhereUtil.regByKeyWord1(keyWord1);//Stringwhere1=DynamicWhereUtil.whereByKeyWord1(keyWord1,"content");Stringcontent1="110kV湾上甲线1721开关、变低、变低、CT高压试验";System.out.println(content1.matches(reg1));//2、有and、or和包含、不包含//is关键字1or(no关键字2andno关键字3andis关键字4andis关键字5)or(is关键字6andis关键字7)orno关键字8StringkeyWord2="is关键字1orno关键字2andno关键字3andis关键字4andis关键字5oris关键字6andis关键字7orno关键字8";Stringreg2=DynamicWhereUtil.regByKeyWord2(keyWord2);//Stringwhere2=DynamicWhereUtil.whereByKeyWord2(keyWord2,"content");Stringcontent2="这里是关键字4hhh、哈哈哈关键字5666、关键字5好好好、关键字4;关键字5";System.out.println(content2.matches(reg2));}

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~