条件拼接 - 根据入参生成where条件

news2024/12/24 9:17:54

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

条件拼接 - 根据入参生成where条件

  • 前言
  • 一、GraphConditionEnum 支持的条件
  • 二、ConditionServiceImpl 实现类
  • 三、DbDataTypeEnum 入参的数据类型
  • 四、GraphTypeEnum 图数据的类型
  • 五、工具类 & 静态参数类
  • 总结


前言

最近做了这个功能,根据不同的入参生产where条件。
值得一记,虽然有很多业务上的枚举,但是大体上思路没问题,就是拆解起来稍微麻烦点。偷懒了…随手记录下吧。
入参示例:

[
	{
		"rootName": "std_people",
		"fieldName": "name",
        "dbDataType":1,
		"option": "contain",
		"value": ["李"]
	},{
		"rootName": "",
		"fieldName": "",
        "dbDataType":null,
		"option": "or",
		"value": []
	},{
		"rootName": "std_people",
		"fieldName": "age",
        "dbDataType":3,
		"option": "betweenEnd",
		"value": [16,60]
	}
]

一、GraphConditionEnum 支持的条件

import org.apache.commons.lang3.StringUtils;

/**
 * @author lobster.long
 */
public enum GraphConditionEnum {
    /**
     * 条件
     */
    GREATER_THAN("greaterThan", ">", " %s > %s "),
    LESS_THAN("lessThan", "<", " %s < %s "),
    EQUAL("equal", "=", " %s = %s "),
    GREATER_EQUAL("greaterEqual", ">=", " %s >= %s "),
    LESS_EQUAL("lessEqual", "<=", " %s <= %s "),
    CONTAIN("contain", "CONTAINS", " %s CONTAINS %s "),
    NOT_EMPTY("notEmpty", "IS NOT NULL", " %s IS NOT NULL "),
    IS_EMPTY("isEmpty", "IS NULL", " %s IS NULL "),
    NOT_BETWEEN_END("notBetweenEnd", "", " (%s < %s OR %s > %s) "),
    BETWEEN_END("betweenEnd", "", " (%s >= %s and %s <= %s) "),
    START("start", "", " %s STARTS WITH %s "),
    END("end", "", " %s ENDS WITH %s "),
    BOTH_CONTAIN("bothContain", "", " %s CONTAINS '%s' "),
    NOT_BOTH_CONTAIN("notBothContain", "", " NOT %s CONTAINS '%s' "),
    NOT_CONTAIN("notContain", "", " NOT %s CONTAINS %s "),
    NOT_START("notStart", "", " NOT %s STARTS WITH %s "),
    NOT_END("notEnd", "", " NOT %s END WITH %s "),
    OR("or", "", " OR "),
    AND("and", "", " AND "),
    NO_EQUAL("noEqual", "", " %s <> %s "),
    MANY_VALUE_CONTAIN("manyValueContain", "", " %s IN [%s] "),
    LEFT_CONTAIN("leftContain", "", " %s STARTS WITH %s "),
    RIGHT_CONTAIN("rightContain", "", " %s END WITH %s "),
    FULL_EQUAL("full_equal", "", " %s = %s "),


    ;

    GraphConditionEnum(String sourceCondition, String targetCondition, String targetConditionFormat) {
        this.sourceCondition = sourceCondition;
        this.targetCondition = targetCondition;
        this.targetConditionFormat = targetConditionFormat;
    }

    private String sourceCondition;

    private String targetCondition;
    private String targetConditionFormat;

    public String getSourceCondition() {
        return sourceCondition;
    }

    public void setSourceCondition(String sourceCondition) {
        this.sourceCondition = sourceCondition;
    }

    public String getTargetCondition() {
        return targetCondition;
    }

    public void setTargetCondition(String targetCondition) {
        this.targetCondition = targetCondition;
    }

    public String getTargetConditionFormat() {
        return targetConditionFormat;
    }

    public void setTargetConditionFormat(String targetConditionFormat) {
        this.targetConditionFormat = targetConditionFormat;
    }

    public static GraphConditionEnum getEnumBySourceCondition(String sourceCondition) {
        for (GraphConditionEnum conditionEnum : GraphConditionEnum.values()) {
            if (conditionEnum.getSourceCondition().equalsIgnoreCase(sourceCondition)) {
                return conditionEnum;
            }
        }
        return null;
    }
    public static String getConditionFormat(String sourceCondition) {
        if(StringUtils.isBlank(sourceCondition)){
            return null;
        }
        for (GraphConditionEnum conditionEnum : GraphConditionEnum.values()) {
            if (conditionEnum.getSourceCondition().equals(sourceCondition)) {
                return conditionEnum.getTargetConditionFormat();
            }
        }
        return null;
    }
}

二、ConditionServiceImpl 实现类

里面有些逻辑是业务上的,但是不涉及公司具体业务

iimport com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ConditionServiceImpl {
    /**
     * 构建查询条件
     *
     * @param conditions 帮我填条件
     * @return 构建查询条件
     * @throws Exception 找不到操作符合规则
     */
    public String buildCondition(List<AutoReqConditionDto> conditions) throws Exception {
        //非空检查-无条件
        StringBuilder fullConditionStr = new StringBuilder();
        if (CollectionUtils.isEmpty(conditions)) {
            return fullConditionStr.toString();
        }
        //有条件
        Map<String, String> shortStdNames = new HashMap<>();
        StringBuilder tempConditionStr = new StringBuilder();
        //循环解析\拼接每一个条件
        for (AutoReqConditionDto condition : conditions) {
            GraphConditionEnum graphConditionEnum = GraphConditionEnum.getEnumBySourceCondition(condition.getOption());
            if (Objects.isNull(graphConditionEnum)) {
                throw new Exception("条件: " + condition.getOption() + " 找不到对应规则");
            }
            //特殊处理OR AND
            if (GraphConditionEnum.OR == graphConditionEnum) {
                tempConditionStr.append(GraphConditionEnum.OR.name());
                continue;
            } else if (GraphConditionEnum.AND == graphConditionEnum) {
                if (tempConditionStr.toString().contains(GraphConditionEnum.OR.name())) {
                    tempConditionStr = new StringBuilder(StringConstant.LEFT_LITTLE_BRACKET + tempConditionStr + StringConstant.RIGHT_LITTLE_BRACKET);
                }
                fullConditionStr.append(tempConditionStr).append(GraphConditionEnum.AND.name());
                tempConditionStr = new StringBuilder();
                continue;
            } else if (StringUtils.isNotEmpty(tempConditionStr)) {
                String cypherAfterOr = RegexUtils.getCypherAfterRegex(tempConditionStr.toString(), GraphConditionEnum.OR.name());
                if (StringUtils.isNotEmpty(cypherAfterOr)) {
                    fullConditionStr.append(StringConstant.LEFT_LITTLE_BRACKET).append(tempConditionStr).append(StringConstant.RIGHT_LITTLE_BRACKET).append(GraphConditionEnum.AND.name());
                    tempConditionStr = new StringBuilder();
                }
            }
            //找到主体
            String rootName = condition.getRootName();
            if (!shortStdNames.containsKey(rootName)) {
                String shotStdName = RegexUtils.getCypherAfterRegex(rootName, StringConstant.UNDERLINE);
                shortStdNames.put(rootName, shotStdName);
            }
            //拼接条件 根据不同option组装不同的
            try {
                String conditionStr = buildConditionFormat(shortStdNames, condition, graphConditionEnum);
                tempConditionStr.append(conditionStr);
            } catch (Exception e) {
                log.error("condition转条件format异常:{},{}", JSONObject.toJSONString(condition), e.getMessage());
                throw new Exception("条件值转数字异常");
            }
        }
        if (tempConditionStr.toString().contains(GraphConditionEnum.OR.name())) {
            tempConditionStr = new StringBuilder(StringConstant.LEFT_LITTLE_BRACKET + tempConditionStr + StringConstant.RIGHT_LITTLE_BRACKET);
        }
        fullConditionStr.append(tempConditionStr);
        return StringConstant.WHERE + fullConditionStr;
    }

    /**
     * 构造条件
     *
     * @param shortStdNames      主体简称
     * @param condition          条件
     * @param graphConditionEnum 条件类型枚举
     * @return Between条件
     */
    private String buildConditionFormat(Map<String, String> shortStdNames, AutoReqConditionDto condition, GraphConditionEnum graphConditionEnum) throws Exception {
        String targetConditionFormat = graphConditionEnum.getTargetConditionFormat();
        GraphTypeEnum graphTypeEnum = GraphTypeEnum.returnGraphType(condition.getDbDataType());
        String shortStdName = shortStdNames.get(condition.getRootName());
        String key = String.format(StringConstant.KEY_FORMAT, shortStdName, condition.getFieldName());
        switch (graphConditionEnum) {
            case NOT_EMPTY:
            case IS_EMPTY:
                return String.format(targetConditionFormat, key);
            case NOT_BETWEEN_END:
            case BETWEEN_END:
                return buildBetweenCondition(key, condition.getValue(), targetConditionFormat, graphTypeEnum);
            case BOTH_CONTAIN:
            case NOT_BOTH_CONTAIN:
                return buildBothContainsCondition(key, condition.getValue(), targetConditionFormat);
            case MANY_VALUE_CONTAIN:
                return buildManyValueCondition(key, condition.getValue(), targetConditionFormat);
            default:
                return buildNormalCondition(key, condition.getValue(), targetConditionFormat, graphTypeEnum);
        }
    }

    /**
     * 构造IN条件
     *
     * @param key                   属性名
     * @param value                 属性执
     * @param targetConditionFormat 条件format
     * @return IN条件
     */
    private String buildManyValueCondition(String key, Object value, String targetConditionFormat) {
        List<String> strList = JSONArray.parseArray(JSONArray.toJSONString(value), String.class);
        String condition = buildUniqueKeyCondition(strList);
        return String.format(targetConditionFormat, key, condition);
    }

    /**
     * 构造BothContains条件
     *
     * @param key                   属性名
     * @param value                 属性执
     * @param targetConditionFormat 条件format
     * @return Between条件
     */
    private String buildBothContainsCondition(String key, Object value, String targetConditionFormat) {
        List<String> strList = JSONArray.parseArray(JSONArray.toJSONString(value), String.class);
        List<String> conditionList = strList.stream().map(str -> String.format(targetConditionFormat, key, str)).collect(Collectors.toList());
        String joinCondition = String.join(GraphConditionEnum.AND.name(), conditionList);
        return StringConstant.LEFT_LITTLE_BRACKET + joinCondition + StringConstant.RIGHT_LITTLE_BRACKET;
    }

    /**
     * 构造Between条件
     *
     * @param key                   属性名
     * @param value                 属性执
     * @param targetConditionFormat 条件format
     * @param graphTypeEnum         数据类型枚举
     * @return Between条件
     */
    private String buildBetweenCondition(String key, Object value, String targetConditionFormat, GraphTypeEnum graphTypeEnum) {
        switch (graphTypeEnum) {
            case INT32:
            case INT64:
                List<Integer> numList = JSONArray.parseArray(JSONArray.toJSONString(value), Integer.class);
                return String.format(targetConditionFormat, key, numList.get(NumberConstant.ZERO), key, numList.get(NumberConstant.ONE));
            default:
                List<String> strList = JSONArray.parseArray(JSONArray.toJSONString(value), String.class);
                return String.format(targetConditionFormat, key, strList.get(NumberConstant.ZERO), key, NumberConstant.ONE);
        }
    }

    /**
     * 根据条件format,拼接查询语句
     *
     * @param key                   属性key
     * @param value                 属性value
     * @param targetConditionFormat 条件类型对应的format
     * @param graphTypeEnum         对应图数据库类型
     * @return 条件语句
     * @throws ServiceException 不支持的数据类型,如新增了dbDataType
     */
    private String buildNormalCondition(String key, Object value, String targetConditionFormat, GraphTypeEnum graphTypeEnum) throws Exception
    {
        switch (graphTypeEnum) {
            case INT32:
            case INT64:
                try {
                    List<Integer> numList = JSONArray.parseArray(JSONArray.toJSONString(value), Integer.class);
                    return String.format(targetConditionFormat, key, numList.get(NumberConstant.ZERO));
                } catch (Exception e) {
                    log.error("条件值转数字异常:{},{},{}", key, value, e.getMessage());
                    throw new Exception("条件值转数字异常");
                }
            default:
                List<String> strList = JSONArray.parseArray(JSONArray.toJSONString(value), String.class);
                return String.format(targetConditionFormat, key, String.format(StringConstant.VALUE_FORMAT, strList.get(NumberConstant.ZERO)));
        }
    }

    /**
     * 构建唯一键条件
     * 用于复杂查询,biz_id in ('xxx','xxx','xxx','xxx','xxx','xxx')的场景)
     *
     * @param uniqueValues 唯一键的值
     * @return 'xxx','xxx','xxx','xxx','xxx','xxx',注意,字符串需要加上单引号,所以没有使用String.join()
     */
    private String buildUniqueKeyCondition(List<String> uniqueValues) {
        StringBuilder sb = new StringBuilder();
        for (String uniqueValue : uniqueValues) {
            if (sb.length() > 0) {
                sb.append(StringConstant.COMMA).append(String.format(StringConstant.VALUE_FORMAT, uniqueValue));
            } else {
                sb.append(String.format(StringConstant.VALUE_FORMAT, uniqueValue));
            }
        }
        return sb.toString();
    }

}

三、DbDataTypeEnum 入参的数据类型

import lombok.Getter;

/**
 * 数据类型枚举
 */
@Getter
public enum DbDataTypeEnum {
    /**
     * 1 - 字符型
     */
    STRING(1),
    /**
     * 2 - 加密字符
     */
    ENCRYPT_STRING(2),
    /**
     * 3 - 整型
     */
    Integer(3),
    /**
     * 4 - 字符数组
     */
    STRING_ARRAY(4),
    /**
     * 5 - 加密字符数组
     */
    ENCRYPT_STRING_ARRAY(5),
    /**
     * 6 - 标签对象数组
     */
    LABEL_ARRAY(6),
    /**
     * 7 - 字典对象
     */
    DICT(7),
    /**
     * 8 - 地址对象
     */
    ADDRESS(8),
    /**
     * 9 - 地址对象数组
     */
    ADDRESS_ARRAY(9),
    /**
     * 10 - 网格身份对象数组
     */
    GRID_ARRAY(10),
    /**
     * 11 - 社区职务对象数组
     */
    GRID_IDENTIFY(11),
    /**
     * 12 - 微网格身份对象数组
     */
    MICROGRID__ARRAY(12),
    /**
     * 13 - 特殊字段类型
     */
    SPECIAL_FILED(13),

    ;

    private Integer key;

    DbDataTypeEnum(Integer key) {
        this.key = key;
    }

    public static DbDataTypeEnum returnDbDataType(int key){
        for (DbDataTypeEnum type:DbDataTypeEnum.values()){
            if(type.key == key){
                return type;
            }
        }
        return null;
    }
}

四、GraphTypeEnum 图数据的类型

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author: zbx
 * @CreateTime: 2024-08-19
 * @Description: 图数据库基本类型
 */
@Slf4j
@Getter
public enum GraphTypeEnum {
    /**
     * tuGraph数据库对应的数据类型
     */
    INT32,

    INT64,

    STRING,

    DATE,

    DATETIME,

    BOOL;

    public static GraphTypeEnum returnGraphType(int dbDataType) throws Exception {
        if (dbDataType == DbDataTypeEnum.STRING.getKey()
                || dbDataType == DbDataTypeEnum.ENCRYPT_STRING.getKey()
                || dbDataType == DbDataTypeEnum.STRING_ARRAY.getKey()
                || dbDataType == DbDataTypeEnum.ENCRYPT_STRING_ARRAY.getKey()
                || dbDataType == DbDataTypeEnum.LABEL_ARRAY.getKey()
                || dbDataType == DbDataTypeEnum.DICT.getKey()
                || dbDataType == DbDataTypeEnum.ADDRESS.getKey()
                || dbDataType == DbDataTypeEnum.ADDRESS_ARRAY.getKey()
                || dbDataType == DbDataTypeEnum.SPECIAL_FILED.getKey()) {
            return GraphTypeEnum.STRING;
        } else if (dbDataType == DbDataTypeEnum.Integer.getKey()) {
            return GraphTypeEnum.INT32;
        } else {
            log.error("数据类型转换,数据类型不支持:{}", dbDataType);
            throw new Exception("数据类型不支持");
        }
    }

    /**
     * 返回tuGraph数据库对应的数据类型枚举
     *
     * @param graphType 数据类型
     * @return 数据类型枚举
     * @throws Exception 暂不支持的图数据类型
     */
    public static GraphTypeEnum getGraphType(String graphType) throws Exception {
        for (GraphTypeEnum value : GraphTypeEnum.values()) {
            if (value.name().equals(graphType)) {
                return value;
            }
        }
        throw new Exception("暂不支持的图数据类型:" + graphType);
    }
}

五、工具类 & 静态参数类

NumberConstant
RegexConstant
RegexUtils
StringConstant

public interface NumberConstant {

    Integer MINUS_ONE = -1;

    Integer ZERO = 0;

    Integer ONE = 1;

    Integer TWO = 2;

    Integer THREE = 3;

    Integer FOUR = 4;

    Integer FIVE = 5;

    Integer TEN = 10;

    Integer TWELVE = 12;

    Integer THIRTY = 30;

    Integer FIFTY = 50;

    Integer ONE_HUNDRED = 100;
}

public interface RegexConstant {
    //---------------------------------------------------------------- 用于字符串匹配 ------------------------------------------------------------------------
    /**
     * 匹配()内的内容
     */
    String STD_LITTLE_BRACKET_REGEX = "\\((.*?)\\)";
    /**
     * 匹配[]内的内容
     */
    String STD_SQUARE_BRACKET_REGEX = "\\[(.*?)\\]";

    /**
     * 匹配是否数字
     */
    String IS_NUMERIC = "^[-+]?\\d*(\\.\\d+)?$";
    /**
     * 匹配主体名
     */
    String STD_REGEX = "std_\\w+";
    /**
     * 匹配属性名,属性名通常是:英文+下划线,如:id_card
     */
    String PROPERTY_REGEX = "^[a-zA-Z0-9._]*$";
    /**
     * 匹配运算符:加、减、乘、除,大于、小于、不等于、等于
     */
    String OPERATORS = "[+\\-*/>\\<!=\\=]";
    /**
     * 匹配不区分大小写的聚合函数名
     */
    String AGGREGATE_FUNCTION = "(?i)(SUM|AVG|MAX|MIN|COUNT)";
    /**
     * 匹配不区分大小写的聚合函数名
     */
    String AGGREGATE_FUNCTION_NO_COUNT = "(?i)(SUM|AVG|MAX|MIN)";
    /**
     * 判断字符串中是否含有一个COUNT(
     */
    String COUNT_REGEX = "(?i)COUNT\\(";
    /**
     * 匹配示例:(p:std_people {name:'张三'}) 或 (p:std_people)
     */
    String NODE_AND_CONDITIONS = "(\\(\\s*[a-zA-Z0-9_]*:[a-zA-Z0-9_]*+(?:\\s+\\{[^\\}]*\\})?\\s*\\))";
    /**
     * 做大括号
     */
    String LEFT_BIG_BRACE = "\\{";
    /**
     * 右小括号
     */
    String RIGHT_LITTLE_BRACKET = "\\)";
    /**
     * 匹配label = 'xxx'的模式
     */
    String LABEL_EQUAL_REGEX = "(\\w+)\\.label\\s*=\\s*'([^']+)'";
    /**
     * 替换为label CONTAINS 'xxx'
     */
    String LABEL_CONTAINS_REPLACE_REGEX = "$1.label CONTAINS '$2'";
    /**
     * 匹配label = 'xxx'的模式
     */
    String NAME_EQUAL_REGEX = "(\\w+)\\.name\\s*=\\s*'([^']+)'";
    /**
     * 替换为label CONTAINS 'xxx'
     */
    String NAME_EQUAL_REPLACE_REGEX = "($1.name = '$2·$4' OR $1.name = '$2.$4')";
    /**
     * 匹配label = 'xxx'的模式
     */
    String NAME_CONTAINS_REGEX = "(\\w+)\\.name\\s*CONTAINS\\s*'([^']+)'";
    /**
     * 替换为label CONTAINS 'xxx'
     */
    String NAME_CONTAINS_REPLACE_REGEX = "($1.name CONTAINS '$2·$4' OR $1.name CONTAINS '$2.$4')";

    String DIRECTION_REGEX = "(<-\\[:[^\\]]*\\]-)|(-\\[:[^\\]]*\\]->)";


    //---------------------------------------------------------------- 用于字符串分割 ------------------------------------------------------------------------
    /**
     * 空格
     */
    String STRING_SPILT_BLANK = " ";
    /**
     * 英文句号分隔符
     */
    String STRING_SPILT_POINT = "\\.";
    /**
     * 冒号分隔符
     */
    String STRING_SPILT_COLO = "\\:";
    /**
     * RETURN分隔符
     */
    String STRING_SPILT_RETURN = "RETURN";
    /**
     * 拼接统计函数
     */
    String STRING_COUNT = "COUNT()";
    /**
     * 分页分隔符
     */
    String STRING_LIMIT = "LIMIT";
    /**
     * 分页偏移量
     */
    String STRING_SKIP = "SKIP";
    /**
     * 拼接返回统计函数
     */
    String STRING_RETURN_COUNT = STRING_SPILT_RETURN + STRING_SPILT_BLANK + STRING_COUNT;

//    boolean containsFunction = matcher.find();
    /**
     *
     */
}
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 正则工具类
 *
 * @author lobster.long
 */
public class RegexUtils {

    /**
     * 返回字符串中满足正则的第一个结果
     *
     * @param input 字符串
     * @param regex 正则
     * @return 满足正则的所有结果
     */
    public static String getFirst(String input, String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            return matcher.group();
        }
        return null;
    }

    /**
     * 返回字符串中满足正则的最后一个结果
     *
     * @param input 字符串
     * @param regex 正则
     * @return 满足正则的所有结果
     */
    public static String getLast(String input, String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        String result = null;
        while (matcher.find()) {
            result = matcher.group();
        }
        return result;
    }

    /**
     * 返回字符串中满足正则的所有结果
     *
     * @param input 字符串
     * @param regex 正则
     * @return 满足正则的所有结果
     */
    public static List<String> getList(String input, String regex) {
        List<String> result = new ArrayList<>();
        Pattern lastPattern = Pattern.compile(regex);
        Matcher lastmatcher = lastPattern.matcher(input);
        while (lastmatcher.find()) {
            result.add(lastmatcher.group(NumberConstant.ONE));
        }
        return result;
    }

    /**
     * 使用正则表达式匹配,返回匹配结果:true,false
     *
     * @param input 字符串
     * @param regex 正则
     * @return 返回匹配结果:true,false
     */
    public static boolean regexBool(String input, String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        return matcher.find();
    }

    /**
     * 截取cypher中,最后一个return 之前的内容
     */
    public static String getCypherBeforeRegex(String cypher, String regex) {
//        // 将原始字符串转换为小写,以便不区分大小写地搜索"return"
//        String lowerCaseString = cypher.toUpperCase();
//        // 查找"return"(不区分大小写)在字符串中最后一次出现的位置
//        int lastIndex = lowerCaseString.lastIndexOf(regex);
        int lastIndex = cypher.lastIndexOf(regex);
        // 如果找到了"return",则截取它之前的所有内容
        // 注意:lastIndexOf返回的是小写字符串中的索引,但我们要在原始字符串上截取,所以索引是有效的
        if (lastIndex != -1) {
            return cypher.substring(0, lastIndex);
        }
        return null;
    }

    /**
     * 返回字符串中,最后一个regex后面的内容
     *
     * @param cypher 待执行语句
     * @param regex  指定字符串
     * @return 指定字符串后面的内容
     */
    public static String getCypherAfterRegex(String cypher, String regex) {
//        // 将原始字符串转换为小写,以便不区分大小写地搜索"return"
//        String lowerCaseString = cypher.toUpperCase();
//        // 查找"return"(不区分大小写)在字符串中最后一次出现的位置
//        int lastIndex = lowerCaseString.lastIndexOf(regex);
        int lastIndex = cypher.lastIndexOf(regex);
        // 如果找到了"return",则截取它之后的所有内容
        // 注意:lastIndexOf返回的是小写字符串中的索引,但我们要在原始字符串上截取,所以索引是有效的
        if (lastIndex != -1) {
            return cypher.substring(lastIndex + regex.length());
        }
        return null;
    }

    public static String appendCount(String cypher) {
        return cypher + RegexConstant.STRING_RETURN_COUNT;
    }

    /**
     * 匹配字符串中的(p:std_people) 或 (h:std_house {building_no:1}) 将社区编码加入到条件中
     *
     * @param cypher                待修正语句
     * @param nodeAndConditionRegex 正则匹配
     * @param communityCode         社区编码
     * @return 增加社区编码后的查询语句
     */
    public static String addCommunityCodeCondition(String cypher, String nodeAndConditionRegex, String communityCode) {
        Pattern pattern = Pattern.compile(nodeAndConditionRegex);
        Matcher matcher = pattern.matcher(cypher);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            // 获取匹配的节点定义
            String nodeDef = matcher.group(0);
            if (nodeDef.contains(StringConstant.BRACE)) {
                // 将community_code:'5101'添加到节点定义的属性列表中
                String modifiedNodeDef = nodeDef.replaceFirst(RegexConstant.LEFT_BIG_BRACE, String.format(StringConstant.COMMUNITY_CODE_AND_CONDITION, communityCode));
                matcher.appendReplacement(sb, modifiedNodeDef);
            } else {
                // 将community_code:'5101'添加到节点定义的属性列表中
                String modifiedNodeDef = nodeDef.replaceFirst(RegexConstant.RIGHT_LITTLE_BRACKET, String.format(StringConstant.COMMUNITY_CODE_NO_CONDITION, communityCode));
                matcher.appendReplacement(sb, modifiedNodeDef);
            }
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 将字符串中满足正则的部分,全部替换为指定字符串
     * 如:将 label = 'sss' 替换为 label CONTAINS 'sss'
     *
     * @param str         字符串
     * @param regex       正则
     * @param replacement 替换为指定字符串
     * @return 替换后的字符串
     */
    public static String replaceAll(String str, String regex, String replacement) {
        // 正则表达式匹配regex,如:匹配label = 'abc''的模式
        Pattern pattern = Pattern.compile(regex);
        // 使用Matcher来替换所有匹配的字符串
        return pattern.matcher(str).replaceAll(replacement);
    }

    /**
     * 提取字符串中符合正则的所有字符串
     *
     * @param str       字符串
     * @param regexList 正则匹配
     * @return 符合正则的所有字符串
     */
    public static List<String> getMatchStrByPatterns(String str, List<String> regexList) {
        List<String> matches = new ArrayList<>();
        for (String regex : regexList) {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(str);
            while (matcher.find()) {
                // 只提取符合特定格式的匹配项
                matches.add(matcher.group(0));
            }
        }
        return matches;
    }

    /**
     * 检查是否有匹配,并且只匹配一次
     *
     * @param str   字符串
     * @param regex 正则
     * @return true - 只有一个,false - 有多个
     */
    public static Boolean onlyOne(String str, String regex) {
        // 编译正则表达式
        Pattern pattern = Pattern.compile(regex);
        // 创建matcher对象
        Matcher matcher = pattern.matcher(str);
        // 检查是否有匹配,并且只匹配一次
        return matcher.find() && !matcher.find();
    }

}

public interface StringConstant {
    /**
     * -------------------------------------- format --------------------------------
     */
    String VERTEX_SCHEME_FORMAT = "CALL db.getVertexSchema('%s')";
    String KEY_FORMAT = " %s.%s ";
    String VALUE_FORMAT = " '%s' ";
    String UNIQUE_KEY_CONDITION_FORMAT = " %s.%s IN [%s] ";
    String COUNT_FORMAT = " WITH DISTINCT %s.%s AS 主键 RETURN COUNT(主键) as 总数 ";
    String PAGE_DATA_FORMAT = " RETURN DISTINCT %s.%s ";
    String SKIP_LIMIT_FORMAT = " SKIP %s LIMIT %s ";
    String OR_FORMAT = "( %s OR %s )";
    String COMMUNITY_CODE_AND_CONDITION = "{community_code:'%s',";
    String COMMUNITY_CODE_NO_CONDITION = " {community_code:'%s'})";
    String STD_MATCH_FORMAT = " (%s:%s)";
    /**
     * 生成MATCH语句
     */
    String SIMPLE_MATCH_FORMAT = "MATCH %s %s RETURN %s ";
    /**
     * 反方向
     */
    String REVERSE_DIRECTION_FORMAT = "<-[%s]-";


    /**
     * -------------------------------------- 常量 --------------------------------
     */
    /**
     * 字符分隔符    ·
     */
    String CH_POINT = "·";
    /**
     * 字符分隔符    .
     */
    String ES_POINT = ".";
    /**
     * 字符分隔符    .
     */
    String EQUAL = "=";
    /**
     * 字符分隔符    .
     */
    String CONTAINS = "CONTAINS";
    String STD_OTHER = "other";
    String BRACE = "}";
    String COMMA = ",";
    String UNDERLINE = "_";
    String LEFT_LITTLE_BRACKET = " (";
    String RIGHT_LITTLE_BRACKET = ") ";
    String TOTAL = "总数";
    String WHERE = "WHERE";
    String AND = "AND";
    String COUNT = "COUNT";
    String BLANK = " ";
    String[] KEYWORDS = {"CREATE", "DELETE", "SET", "REMOVE", "DROP"};
    String STD_COMMON = "std_common";


}

总结

执行结果:
在这里插入图片描述

虽然不是分组条件,但是将就用吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2080014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C/C++】int类型32位有符号整数的含义

在C语言中&#xff0c;int类型的取值范围是有符号的32位整数&#xff0c;即-2,147,483,648到2,147,483,647。‌ 这个范围是由int类型在32位系统上的存储大小决定的。具体来说&#xff0c;int类型在C语言中占4个字节&#xff0c;即32个二进制位。当表示正数时&#xff0c;最高位…

从永远到永远-日语学习-动词三分类及ます变形

动词三分类 0.前置知识1.动词三分类3.判断方法4.变形 0.前置知识 讨论的都是基于动词原形动词变ます形&#xff0c;是因为小日子骨子里的假客套&#xff0c;ます形比较礼貌。日语所有动词原型都是う段结尾五段动词联想う段动2、动3原型都是る结尾上一段动词&#xff08;い段る…

PS制作曲线背景

新建图层->设置颜色 钢笔->画个路径 画笔->柔边缘5像素 描边路径->工具->模拟压力 图层->Ctrl->缩略图 编辑->定义画笔预设 F5->画笔设置->间距5%->形状动态->角度抖动300

【突出百分比的图表创意】保姆级教程(Excel 图表呈现)

包学包会 效果一览&#xff1a;思路&#xff1a;圆形百分比图表 保姆级详细过程先试着做一个吧接下来完成剩下的其他图标探索 效果一览&#xff1a; 思路&#xff1a; 先准备好两个形状一致的图形&#xff0c;中间填充不同颜色&#xff0c;一深一浅。 一个用来表示百分百&…

Git项目的文件泄露分析和漏洞修复

说明:本文仅是用于学习分析自己搭建的Git漏洞内容和原理,请勿用在非法途径上,违者后果自负,与笔者无关;本文开始前请认真详细学习《‌中华人民共和国网络安全法》‌及其相关法规内容【学法时习之丨网络安全在身边一图了解网络安全法_中央网络安全和信息化委员会办公室】 …

C语言典型例题53

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 例题4.5 用π/41-1/31/5-1/7……公式来计算π的近似值&#xff0c;知道某一项的绝对值小于10的六次方为止 代码&#xff1a; //《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 //例题4.5 用π/41-1/3…

移动支付背后的信息安全与防控策略

你是否有过以下担心&#xff1f; 每次支付后&#xff0c;担心金额扣除多了&#xff1b; 账号不幸被盗后&#xff0c;银行卡(绑卡)是否会被恶意刷爆&#xff1b; 存移动钱包的钱&#xff0c;哪天不翼而飞; 本文讨论了移动支付背后的安全防控策略&#xff0c;强调了支付安全的…

安达发|企业实施APS计划排单系统会有哪些效益?

在竞争日益激烈的商业环境中&#xff0c;企业如何保持竞争力并实现可持续发展&#xff1f;答案可能就隐藏在先进的生产管理工具中。今天&#xff0c;我们将深入探讨企业实施APS&#xff08;高级计划排程&#xff09;计划排单系统所带来的五大惊人效益&#xff0c;这些效益不仅能…

【Python学习手册(第四版)】学习笔记22-模块代码编写基础

个人总结难免疏漏&#xff0c;请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。 本文主要介绍模块编码工具的基础知识&#xff1a;import和from语句&#xff0c;以及reload调用&#xff0c;介绍了模块命名空间&#xff08;这个概念可能因为翻译…

数据结构;顺序表;链式表

1.衡量一个程序是否优秀&#xff1a; 1.时间复杂度&#xff1a; 数据量增长与程序运行时间的比例关系以函数描述称为时间渐进复杂度函数,简称时间复杂度 O(c) > O(logn) > O(n) > O(nlogn) > O(n^2) > O(n^3) > O(2^n) 2.空间复杂度…

echarts倾斜横向堆叠柱状图

代码如下 option: {backgroundColor: "transparent",tooltip: {trigger: "axis",axisPointer: {lineStyle: {color: {type: "linear",x: 0,y: 0,x2: 0,y2: 1,colorStops: [{offset: 0,color: "rgba(126,199,255,0)", // 0% 处的颜色}…

Sentinel-1 Level 1数据处理的详细算法定义(七)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程&#xff0c;以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下&…

模型 SPIN销售法

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。探需挖痛&#xff0c;引导成交。 1 SPIN销售法的应用 1.1 提升工作效率的软件销售应用SPIN模型 一家制造企业正在寻求提升工作效率的解决方案。他们注意到员工在处理文件和任务时存在效率问题&#…

SX_Git Graph图形化案例详解_20

这里写目录标题 1、前言&#xff1a;2、需求&#xff1a;3、需求满足&#xff1a;1.克隆到本地操作&#xff1a;2.创建自己的本地分支并连接远程分支:3.修改代码推送代码&#xff1a;4.分支的rebase和merge操作&#xff1a;5.拉取操作&#xff1a; 1、前言&#xff1a; git gra…

排队辅助功能二手车,全速自适应巡航

沃尔沃 xc90 2015款 acc 3秒后会停止 pilot 15公里以内 停止场景少 ‌沃尔沃全新XC90确实新增了排队辅助功能‌&#xff0c;这一功能旨在缓解驾驶者在低速排队行驶中的疲劳&#xff0c;通过自动控制加速、刹车和转向来跟随前方车辆&#xff0c;从而让驾乘体验更加安全舒适。这…

python办公自动化:PPT工具之`Python-PPTX`简介

第1章 简介 1.1 什么是Python-PPTX库&#xff1f; Python-PPTX库是一个用于创建和修改Microsoft PowerPoint (.pptx) 文件的Python库。它提供了一个易于使用的API&#xff0c;使得开发者能够以编程方式操纵PowerPoint文件&#xff0c;包括创建新文件、修改现有文件、添加文本…

【人工智能 | 机器学习 | 理论篇】决策树(decision tree)

文章目录 1. 基本流程2. 划分选择2.1 信息增益2.2 增益率2.3 基尼系数 3. 剪枝处理3.1 预剪枝3.2 后剪枝 4. 连续与缺失值4.1 连续值处理4.2 缺失值处理 5. 多变量决策树 1. 基本流程 二分类任务决策树流程&#xff1a; 决策树&#xff1a;包含 1个根结点、若干个内部结点、若…

云计算实训37——Dockerfile的应用+私有仓库的创建与管理

一、文件联合系统 文件联合系统&#xff08;Overlay Filesystem&#xff09;&#xff1a; 是一种允许多个文件系统层叠在一起的文件系统。它通常用于将只读文件系统&#xff08;如基础镜像&#xff09;与可写文件系统&#xff08;如用户的修改&#xff09;结合&#xff0c;形…

2k1000LA 调试触摸+调试8723bu

原材料&#xff1a; 硬件&#xff1a; 2k1000LA 开发板 软件&#xff1a; 使用网盘上的 pmon 源码 linux源码 逻辑&#xff1a; 设备树的修改需要在 pmon 源码里面修改 驱动的修改需要在 linux 的源码里面修改。 下载 loongnix 的 pmon 源码 linux 源码 &#xff0c;并放入虚…

雷达图概述以及实例

目录 一.雷达图概述1.何为雷达图2.雷达图的构成要素 二.实例&#xff08;以Excel、Python为例 &#xff09;1.Excel&#xff08;2021版&#xff09;2.Python 一.雷达图概述 1.何为雷达图 雷达图&#xff0c;是一种展现多维度数据间相对重要性或程度的可视化图形。以中心点为起…