// 配备并重新组合查询语句, 给出QCnd对象 func (qb *QWBuilder) evalQCnd(fld string) *QCnd { for _, rule := range qb.Rules { if rule.Regex.MatchString(fld) { z.DebugPrintf("fld [%s] match regex [%s]\n", fld, rule.Regex.String()) groups := rule.Regex.FindStringSubmatch(fld) for _, grp := range groups { z.DebugPrintf(" g_%s\n", grp) } extractStr := z.Trim(findMatchStr(rule.Seg, groups)) // 生成QCnd qc := new(QCnd) qc.Key = rule.Key qc.Origin = fld qc.Plain = extractStr qc.Type = rule.Type switch qc.Type { case String: qc.Value = extractStr case Regex: qc.Value = regexp.MustCompile(extractStr) case IntRegion, LongRegion, DateRegion: qc.Value = z.MakeRegion(extractStr) case StringEnum: senum := extractStr[1 : len(extractStr)-1] qc.Value = z.SplitIgnoreBlank(senum, ",") case IntEnum: ienum := extractStr[1 : len(extractStr)-1] iarray := make([]int, 0, 5) for _, ie := range z.SplitIgnoreBlank(ienum, ",") { ione, _ := strconv.Atoi(ie) iarray = append(iarray, ione) } qc.Value = iarray case Json: jmap := new(map[string]interface{}) jerr := z.JsonFromString(extractStr, jmap) if jerr != nil { qc.Value = jmap } default: // unsupport qc.Value = nil } return qc } } z.DebugPrintf("fld [%s] miss match\n", fld) return nil }
func Test_QWord_Each(t *testing.T) { qb := query.QWordBuilder(strings.NewReader(` $user : ^(@)(.*)$ ${2} = String $test :: S $(2) = StringEnum `)) qword := qb.Parse("@zozoh , S(abc,hha, erer, jdgg gdg)") qword.Each(func(index int, qc *query.QCnd, prevIsAnd bool) { z.DebugPrintf("no.%d prevIsAnd.%v \n", index, prevIsAnd) z.DebugPrintln(qc.String()) }) }
func Test_QWord_Region(t *testing.T) { qb := query.QWordBuilder(strings.NewReader(` $ct :: C ${2} = IntRegion $st :: S ${2} = IntRegion $usr : ^(@)([a-zA-Z]+)$ ${2} = String `)) qword := qb.Parse("C[10,15], S(12,) , @pw") qword.Each(func(index int, qc *query.QCnd, prevIsAnd bool) { z.DebugPrintf("no.%d prevIsAnd.%v \n", index, prevIsAnd) z.DebugPrintln(qc.String()) }) qc := qword.Get("usr") if qc == nil { t.Error("can't find qcnd by key") } }
// 提取查询语句与连接符 func (qb *QWBuilder) extractFldsAndSeps(kwd string) (isGOr, isGAnd bool, flds []string, seps []byte) { // 判断全局参数 (先强制认为 'xxx:') // FIXME 后面改成正则匹配, 可以多加空格 isGOr = strings.HasPrefix(kwd, qb.GOr+":") isGAnd = strings.HasPrefix(kwd, qb.GAnd+":") if isGOr || isGAnd { kwd = kwd[strings.Index(kwd, ":")+1:] } // 分割查询字符串 flds = make([]string, 0, 5) seps = make([]byte, 0, 5) // FIXME 这个地方需要在好好想想 // 保证一个特殊情况下的正确性, 比如或是" "与是"," kwd中有类似"xxx , yyy"这样的语句,需要把处理 // 这个特例是一搬在" "当做或的情况下 if z.IsInStrings(qb.SepOr, " ") { for _, sAnd := range qb.SepAnd { ss := " " + sAnd + " " kwd = strings.Replace(kwd, ss, sAnd, -1) } } // 开始解析 kszie := len(kwd) fld := z.StringBuilder() for i := 0; i < kszie; i++ { c := kwd[i] z.DebugPrintf("extract kwd %3d -> [%s]\n", i, string(c)) // 如果是引用 if pos := z.IndexOfStrings(qb.QuoteBegin, string(c)); pos > 0 { // 这里面的空格啥的就不会被跳过了, 会一直取到引用结束, 逃逸字符会调整一下 for i++; i < kszie; i++ { c = kwd[i] if string(c) == qb.QuoteEnd[pos] { break } else if c == '\\' { // FIXME 这里没有做防守 i++ fld.Append(kwd[i]) } else { fld.Append(c) } } continue } // 如果是括弧 if z.IsInStrings(qb.BracketBegin, string(c)) { fld.Append(c) for i++; i < kszie; i++ { c = kwd[i] fld.Append(c) if z.IsInStrings(qb.BracketEnd, string(c)) { break } } continue } // 如果是分隔符 if z.IsInStrings(qb.SepAnd, string(c)) || z.IsInStrings(qb.SepOr, string(c)) { cfld := z.Trim(fld.String()) if len(cfld) > 0 { flds = append(flds, cfld) seps = append(seps, c) fld = z.StringBuilder() } continue } // 没什么特殊情况,那就加入到sb fld.Append(c) } // 加入最后一个条件 lfld := z.Trim(fld.String()) if len(lfld) > 0 { flds = append(flds, lfld) } // 打印一下 if z.IsDebugOn() && len(flds) > 0 { z.DebugPrintf("kwd : %s\n", kwd) for _, f := range flds { z.DebugPrintf("fld : %s\n", f) } for _, s := range seps { z.DebugPrintf("sep : '%s'\n", string(s)) } } return isGOr, isGAnd, flds, seps }
// 读取解析规则, 传入一个实现了io.Reader的对象即可 func (qb *QWBuilder) LoadRules(reader io.Reader) *QWBuilder { bufreader := bufio.NewReader(reader) var line string var lnum int var err error var rules []*QWordRule = make([]*QWordRule, 0) for true { line, err = bufreader.ReadString('\n') if err == io.EOF { err = nil break } else if err != nil { break } lnum++ z.DebugPrintf("rule %2d : %s", lnum, line) // 忽略空行与注释行 line = strings.TrimSpace(line) if z.IsBlank(line) || strings.HasPrefix(line, "#") { continue } // 解析行信息 if strings.HasPrefix(line, "$") { qwRule := new(QWordRule) // 获得key var fpos int = strings.Index(line, ":") if fpos < 0 { err = errors.New(fmt.Sprintf("invalid rule line %d : %s", lnum, line)) break } qwRule.Key = strings.TrimSpace(line[1:fpos]) // 获得regex var expr string if line[fpos+1] == ':' { // 简要模式 expr = "^(" + strings.TrimSpace(line[fpos+2:]) + ")(.*)$" } else { // 普通模式 expr = strings.TrimSpace(line[fpos+1:]) } regex, regerr := regexp.Compile(expr) if regerr != nil { err = regerr break } qwRule.Regex = regex // 读取下一行,必须存在哟 line, err = bufreader.ReadString('\n') if err == io.EOF { err = errors.New(fmt.Sprintf("rule line(%d) miss next line", lnum)) break } else if err != nil { break } lnum++ z.DebugPrintf("rule %2d : %s", lnum, line) line = strings.TrimSpace(line) spos := strings.Index(line, "=") if z.IsBlank(line) || !strings.HasPrefix(line, "$") || spos < 0 { err = errors.New(fmt.Sprintf("invalid rule line %d : %s", lnum, line)) break } // 获得seg qwRule.Seg = z.Trim(line[:spos]) // 获得type qcType, qcterr := QCType(strings.TrimSpace(line[spos+1:])) if qcterr != nil { err = qcterr break } qwRule.Type = qcType // 加入到rules rules = append(rules, qwRule) } } // 是否有错误 if err != nil { panic(err) } qb.Rules = rules return qb }