Browse Source

feat:搜索结构体

wangshan 3 years ago
parent
commit
93c1a98da5
3 changed files with 363 additions and 24 deletions
  1. 39 0
      jyBXCore/rpc/entity/entity.go
  2. 300 0
      jyBXCore/rpc/model/es/searchQuery.go
  3. 24 24
      jyBXSubscribe/rpc/model/push.go

+ 39 - 0
jyBXCore/rpc/entity/entity.go

@@ -0,0 +1,39 @@
+package entity
+
+// KeyWord /*筛选条件--关键词*/
+type KeyWord struct {
+	Keyword  string   `json:"keyword"`  //关键词
+	Appended []string `json:"appended"` //附加词
+	Exclude  []string `json:"exclude"`  //排除词
+}
+
+// BiddingSearchParams /*招标信息搜索筛选条件*/
+type BiddingSearchParams struct {
+	Id               string    `json:"id"`
+	PublishTime      string    `json:"publishtime"`        //发布时间
+	Province         []string  `json:"province"`           //地区-省份
+	City             []string  `json:"city"`               //地区-城市
+	Region           []string  `json:"region"`             //地区-省份+城市
+	Industry         []string  `json:"industry"`           //行业
+	Keyword          []KeyWord `json:"keywords"`           //关键词
+	Buyer            []string  `json:"buyer"`              //招标单位(采购单位)
+	BuyerClass       []string  `json:"buyerclass"`         //采购单位类型
+	HasBuyerTel      string    `json:"hasBuyertel"`        //是否有采购单位电话
+	Winner           []string  `json:"winner"`             //中标单位
+	HasWinnerTel     string    `json:"hasWinnertel"`       //是否有中标单位电话
+	ComeInTime       int64     `json:"comeintime"`         //入库时间(秒)
+	OpenId           string    `json:"openid"`             //用户openid
+	MinPrice         string    `json:"minprice"`           //金额——最少
+	MaxPrice         string    `json:"maxprice"`           //金额——最多
+	SelectType       string    `json:"selectType"`         //筛选(正文 or 标题)
+	Subtype          string    `json:"subtype"`            //信息类型
+	Toptype          string    `json:"toptype,omitempty"`  //信息类型-一级分类
+	SelectIds        []string  `json:"selectId"`           //选择信息导出
+	ComeInFrom       string    `json:"comeinfrom"`         //查询来源
+	FileExists       string    `json:"fileExists"`         //是否有附件
+	SearchTypeSwitch bool      `json:"searchTypeSwitch"`   //是否开启 正文 标题同时搜索只搜正文的开关
+	PageNum          int64     `json:"pageNum,omitempty"`  //当前页码
+	PageSize         int64     `json:"pageSize,omitempty"` //每页数量
+	Price            string    `json:"price,omitempty"`    //价格
+	UserId           string    `json:"userId,omitempty"`   //用户id
+}

+ 300 - 0
jyBXCore/rpc/model/es/searchQuery.go

@@ -0,0 +1,300 @@
+package es
+
+import (
+	MC "app.yhyue.com/moapp/jybase/common"
+	elastic "app.yhyue.com/moapp/jybase/esv1"
+	"fmt"
+	"jyBXCore/rpc/entity"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	queryByIds        = `{"query":{"bool":{"must":[{"terms":{"_id":[%s]}}]}}}`
+	multiMatch        = `{"multi_match": {"query": %s,"type": "phrase", "fields": [%s]}}`
+	query             = `{"query":{"bool":{"must":[%s],"must_not":[%s],"should":[%s],"minimum_should_match": %d}}}`
+	queryBoolShould   = `{"bool":{"should":[%s],"minimum_should_match": 1}}`
+	queryPrice        = `{"bool":{"must":[{"range":{"bidamount":{%s}}}]}},{"bool":{"must":[{"range":{"budget":{%s}}}],"must_not":[{"range":{"bidamount":{"gte":-1}}}]}}`
+	queryBoolMust     = `{"terms":{"%s":[%s]}}`
+	queryBoolMustAnd  = `{"bool":{"must":[%s]%s}}`
+	queryMissing      = `{"constant_score":{"filter":{"missing":{"field":"%s"}}}}`
+	queryBoolMustTerm = `{"bool": {"must": [{ "term": {"isValidFile": %d }}]}}`
+	gte               = `"gte": %s`
+	lte               = `"lte": %s`
+	queryPublishTime  = `{"range":{"publishtime":{%s}}}`
+)
+
+var topType = map[string]string{
+	"招标预告":   "预告",
+	"招标公告":   "招标",
+	"招标结果":   "结果",
+	"招标信用信息": "其它",
+	"拟建项目":   "拟建",
+	"采购意向":   "采购意向",
+}
+
+type SearchQuery struct{}
+
+func (s *SearchQuery) BiddingSearchQuery(bsp *entity.BiddingSearchParams) (qstr string) {
+	if len(bsp.SelectIds) > 0 {
+		return fmt.Sprintf(queryByIds, `"`+strings.Join(bsp.SelectIds, `","`)+`"`)
+	}
+
+	var bools []string
+	var musts = []string{fmt.Sprintf(`{"range":{"comeintime":{"lt":%d}}}`, bsp.ComeInTime)}
+	var mustNot []string
+	//地区
+	var area []string
+	//省份
+	if len(bsp.Province) > 0 {
+		areaquery := `{"terms":{"area":[`
+		for k, v := range bsp.Province {
+			if k > 0 {
+				areaquery += `,`
+			}
+			areaquery += `"` + v + `"`
+		}
+		areaquery += `]}}`
+		area = append(area, areaquery)
+	}
+	//城市
+	if len(bsp.City) > 0 {
+		areaquery := `{"terms":{"city":[`
+		for k, v := range bsp.City {
+			if k > 0 {
+				areaquery += `,`
+			}
+			areaquery += `"` + v + `"`
+		}
+		areaquery += `]}}`
+		area = append(area, areaquery)
+	}
+	if len(area) > 0 {
+		musts = append(musts, fmt.Sprintf(queryBoolShould, strings.Join(area, ",")))
+	}
+	//检索日期
+	//发布时间
+	startTime := ""
+	now := time.Unix(bsp.ComeInTime, 0)
+	endTime := fmt.Sprintf("%d", now.Unix())
+	if strings.Contains(bsp.PublishTime, "_") { //设置检索日期
+		timeQuery := ``
+		startTime = strings.Split(bsp.PublishTime, "_")[0]
+		endTimeTmp := now
+		if etime := strings.Split(bsp.PublishTime, "_")[1]; etime != "" {
+			etTime := time.Unix(MC.Int64All(etime), 0)
+			endTimeTmp = time.Date(etTime.Year(), etTime.Month(), etTime.Day()+1, 0, 0, 0, 0, time.Local)
+		}
+		//结束时间必须小于筛选时间
+		if endTimeTmp.After(now) {
+			endTimeTmp = now
+		}
+		endTime = fmt.Sprintf("%d", endTimeTmp.Unix())
+		if startTime != "" {
+			timeQuery += fmt.Sprintf(gte, startTime)
+		}
+		if startTime != "" && endTime != "" {
+			timeQuery += `,`
+		}
+		if endTime != "" {
+			timeQuery += fmt.Sprintf(lte, endTime)
+		}
+		musts = append(musts, fmt.Sprintf(queryPublishTime, timeQuery))
+	}
+	//信息类型 toptype 一级;subtype 二级;
+	if bsp.Subtype != "" || bsp.Toptype != "" {
+		var subQuery string
+		var topTypes = strings.Split(bsp.Toptype, ",")
+		var subTypes = strings.Split(bsp.Subtype, ",")
+		for _, v := range strings.Split(bsp.Subtype, ",") {
+			if v1, ok := topType[v]; ok {
+				topTypes = append(topTypes, fmt.Sprintf(`"%s"`, v1))
+			} else {
+				subTypes = append(subTypes, fmt.Sprintf(`"%s"`, v))
+			}
+		}
+		if len(subTypes) > 0 && len(topTypes) > 0 {
+			subQuery = fmt.Sprintf(`{"bool": {"should": [{"terms": {"subtype": [%s]}},{"terms": {"toptype": [%s]}}]}}`, strings.Join(subTypes, ","), strings.Join(topTypes, ","))
+		} else if len(subTypes) > 0 {
+			subQuery = fmt.Sprintf(`{"terms":{"subtype":[%s]}}`, strings.Join(subTypes, ","))
+		} else if len(topTypes) > 0 {
+			subQuery = fmt.Sprintf(`{"terms":{"toptype":[%s]}}`, strings.Join(topTypes, ","))
+		}
+		musts = append(musts, subQuery)
+	}
+	//行业
+	if len(bsp.Industry) > 0 {
+		musts = append(musts, fmt.Sprintf(queryBoolMust, "s_subscopeclass", `"`+strings.Join(bsp.Industry, `","`)+`"`))
+	}
+	//采购单位
+	if len(bsp.Buyer) > 0 {
+		musts = append(musts, fmt.Sprintf(queryBoolMust, "buyer", `"`+strings.Join(bsp.Buyer, `","`)+`"`))
+	}
+	//采购单位类型
+	if len(bsp.BuyerClass) > 0 {
+		musts = append(musts, fmt.Sprintf(queryBoolMust, "buyerclass", `"`+strings.Join(bsp.BuyerClass, `","`)+`"`))
+	}
+	//中标单位
+	if len(bsp.Winner) > 0 {
+		musts = append(musts, fmt.Sprintf(queryBoolMust, "s_winner", `"`+strings.Join(bsp.Winner, `","`)+`"`))
+	}
+	//价格区间
+	if bsp.MinPrice != "" || bsp.MaxPrice != "" {
+		_minPrice := ""
+		_maxPrice := ""
+		sq := ``
+		if bsp.MinPrice != "" {
+			min, _ := strconv.ParseFloat(bsp.MinPrice, 64)
+			_minPrice = fmt.Sprintf("%.0f", min*10000)
+			if _minPrice == "0" {
+				_minPrice = ""
+			}
+		}
+		if bsp.MaxPrice != "" {
+			max, _ := strconv.ParseFloat(bsp.MaxPrice, 64)
+			_maxPrice = fmt.Sprintf("%.0f", max*10000)
+			if _maxPrice == "0" {
+				_maxPrice = ""
+			}
+		}
+		if _minPrice != "" {
+			sq += fmt.Sprintf(gte, _minPrice)
+		}
+		if _minPrice != "" && _maxPrice != "" {
+			sq += `,`
+		}
+		if _maxPrice != "" {
+			sq += fmt.Sprintf(lte, _maxPrice)
+		}
+		if _minPrice != "" || _maxPrice != "" {
+			musts = append(musts, fmt.Sprintf(queryBoolShould, fmt.Sprintf(queryPrice, sq, sq)))
+		}
+	}
+	boolsNum := 0
+	selectType := bsp.SelectType
+	//关键词
+	if len(bsp.Keyword) > 0 {
+		boolsNum = 1
+		queryItem := ""
+		if selectType == "" {
+			queryItem = "title"
+		} else if selectType == "all" {
+			queryItem = "detail\", \"title"
+		} else {
+			//搜索开关打开  包含标题和正文  只匹配正文
+			if bsp.SearchTypeSwitch && s.DetailANDTitle(selectType) {
+				if strings.Contains(selectType, "title,") {
+					selectType = strings.Replace(selectType, "title,", "", -1)
+				} else if strings.Contains(selectType, ",title") {
+					selectType = strings.Replace(selectType, ",title", "", -1)
+				}
+			}
+			queryItem = strings.ReplaceAll(selectType, ",", "\",\"")
+		}
+		multiMatchNew := fmt.Sprintf(multiMatch, "%s", "\""+queryItem+"\"")
+		for _, v := range bsp.Keyword {
+			var should []string
+			var mustNot []string
+			if v.Keyword != "" {
+				if strings.Contains(v.Keyword, "+") {
+					for _, vk := range strings.Split(v.Keyword, "+") {
+						//单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail  搜索的时候加上标题
+						if bsp.ComeInFrom == "supersearchPage" && s.DetailFileORTitle(selectType) && len([]rune(elastic.ReplaceYH(vk))) == 1 {
+							queryItem = strings.ReplaceAll(selectType+",title", ",", "\",\"")
+							shouldKeys := fmt.Sprintf(multiMatch, "\""+vk+"\"", "\""+queryItem+"\"")
+							should = append(should, shouldKeys)
+						} else {
+							should = append(should, fmt.Sprintf(multiMatchNew, "\""+vk+"\""))
+						}
+					}
+				} else if strings.Contains(v.Keyword, " ") {
+					for _, vk := range strings.Split(v.Keyword, " ") {
+						//单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail  搜索的时候加上标题
+						if bsp.ComeInFrom == "supersearchPage" && s.DetailFileORTitle(selectType) && len([]rune(elastic.ReplaceYH(vk))) == 1 {
+							queryItem = strings.ReplaceAll(selectType+",title", ",", "\",\"")
+							shouldKeys := fmt.Sprintf(multiMatch, "\""+vk+"\"", "\""+queryItem+"\"")
+							should = append(should, shouldKeys)
+						} else {
+							should = append(should, fmt.Sprintf(multiMatchNew, "\""+vk+"\""))
+						}
+					}
+				} else {
+					//单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail  搜索的时候加上标题
+					if bsp.ComeInFrom == "supersearchPage" && s.DetailFileORTitle(selectType) && len([]rune(elastic.ReplaceYH(v.Keyword))) == 1 {
+						queryItem = strings.ReplaceAll(selectType+",title", ",", "\",\"")
+						shouldKeys := fmt.Sprintf(multiMatch, "\""+v.Keyword+"\"", "\""+queryItem+"\"")
+						should = append(should, shouldKeys)
+					} else {
+						should = append(should, fmt.Sprintf(multiMatchNew, "\""+v.Keyword+"\""))
+					}
+				}
+			}
+
+			//附加词
+			for _, vv := range v.Appended {
+				should = append(should, fmt.Sprintf(multiMatchNew, "\""+vv+"\""))
+			}
+
+			//排除词
+			for _, vv := range v.Exclude {
+				//单个字 搜索范围 有全文或者附件 无标题 例如:学 虚拟机 detail  搜索的时候加上标题
+				if bsp.ComeInFrom == "supersearchPage" && s.DetailFileORTitle(selectType) && len([]rune(elastic.ReplaceYH(vv))) == 1 {
+					queryItem = strings.ReplaceAll(selectType+",title", ",", "\",\"")
+					shouldKeys := fmt.Sprintf(multiMatch, "\""+vv+"\"", "\""+queryItem+"\"")
+					should = append(should, shouldKeys)
+				} else {
+					mustNot = append(mustNot, fmt.Sprintf(multiMatchNew, "\""+vv+"\""))
+				}
+			}
+
+			//添加
+			if len(should) > 0 {
+				notStr := ""
+				if len(mustNot) > 0 {
+					notStr = fmt.Sprintf(`,"must_not":[%s]`, strings.Join(mustNot, ","))
+				}
+				bools = append(bools, fmt.Sprintf(queryBoolMustAnd, strings.Join(should, ","), notStr))
+			}
+		}
+	}
+	//采购单位联系方式
+	if bsp.HasBuyerTel != "" {
+		if bsp.HasBuyerTel == "y" {
+			mustNot = append(mustNot, fmt.Sprintf(queryMissing, "buyertel"))
+		} else {
+			musts = append(musts, fmt.Sprintf(queryMissing, "buyertel"))
+		}
+	}
+	//中标企业联系方式
+	if bsp.HasWinnerTel != "" {
+		if bsp.HasWinnerTel == "y" {
+			mustNot = append(mustNot, fmt.Sprintf(queryMissing, "winnertel"))
+		} else {
+			musts = append(musts, fmt.Sprintf(queryMissing, "winnertel"))
+		}
+	}
+	//搜索范围是否只有附件
+	//搜索范围只选择附件,是否有附件条件无效;
+	var isFileSearch = strings.ReplaceAll(selectType, ",", "\",\"") == "filetext"
+	if !isFileSearch && bsp.FileExists != "" {
+		if bsp.FileExists == "1" { //有附件
+			mustNot = append(mustNot, fmt.Sprintf(queryMissing, "isValidFile"))
+			musts = append(musts, fmt.Sprintf(queryBoolMustTerm, 1))
+		} else if bsp.FileExists == "-1" { //无附件
+			musts = append(musts, fmt.Sprintf(queryMissing, "isValidFile"))
+		}
+	}
+	qstr = fmt.Sprintf(query, strings.Join(musts, ","), strings.Join(mustNot, ","), strings.Join(bools, ","), boolsNum)
+	return
+}
+
+// DetailFileORTitle 包含正文或 附件 不包含标题
+func (s *SearchQuery) DetailFileORTitle(findFields string) bool {
+	return (strings.Contains(findFields, "detail") || strings.Contains(findFields, "filetext")) && !strings.Contains(findFields, "title")
+}
+
+// DetailANDTitle 包含正文包含标题
+func (s *SearchQuery) DetailANDTitle(findFields string) bool {
+	return strings.Contains(findFields, "detail") && strings.Contains(findFields, "title")
+}

+ 24 - 24
jyBXSubscribe/rpc/model/push.go

@@ -729,14 +729,14 @@ func (s *subscribePush) DefaultDatas(spqp *SubPushQueryParam) (hasNextPage bool,
 	t1 := time.Now()
 	log.Println("userId:", spqp.UserId)
 	//用户信息
-	scd := s.getUserInfo(spqp)
+	bsp := s.getUserInfo(spqp)
 	logx.Info("获取用户信息耗时:", time.Since(t1))
 	t2 := time.Now()
-	if len(scd.Keyword) > 0 {
-		logx.Info(time.Since(t1), "--scd:", scd)
+	if len(bsp.Keyword) > 0 {
+		logx.Info(time.Since(t1), "--bsp:", bsp)
 		//获取查询语句
-		qstr := s.getDefaultDatasSQL(scd)
-		list := elastic.GetAllByNgram(INDEX, TYPE, qstr, findfields, bidSort, bidField, 0, scd.Size, 0, false)
+		qstr := s.getDefaultDatasSQL(bsp)
+		list := elastic.GetAllByNgram(INDEX, TYPE, qstr, findfields, bidSort, bidField, 0, bsp.Size, 0, false)
 		logx.Info(time.Since(t1), "count:", len(*list))
 		if list != nil && len(*list) > 0 {
 			total = int64(len(*list))
@@ -745,11 +745,11 @@ func (s *subscribePush) DefaultDatas(spqp *SubPushQueryParam) (hasNextPage bool,
 			if len(*list) > pageSize {
 				listOne = (*list)[:pageSize]
 			}
-			result = s.listManager(spqp, listOne, scd.Keyword, (len(listOne)+pageSize)/pageSize)
+			result = s.listManager(spqp, listOne, bsp.Keyword, (len(listOne)+pageSize)/pageSize)
 			if len(*list) > pageSize {
 				hasNextPage = true
 				listOther := (*list)[pageSize:]
-				go s.listManager(spqp, listOther, scd.Keyword, (len(listOther)+pageSize)/pageSize)
+				go s.listManager(spqp, listOther, bsp.Keyword, (len(listOther)+pageSize)/pageSize)
 			}
 		}
 	}
@@ -865,7 +865,7 @@ func getKeys(title string, keywords []ViewKeyWord) (str []string) {
 }
 
 //获取查询语句
-func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
+func (s *subscribePush) getDefaultDatasSQL(bsp *ViewCondition) (str string) {
 	query := `{"query":{"bool":{"must":[%s],"should":[%s],"minimum_should_match": %d}}}`
 	query_bool_should := `{"bool":{"should":[%s],"minimum_should_match": 1}}`
 	multi_match := `{"multi_match": {"query": %s,"type": "phrase", "fields": [%s]}}`
@@ -873,13 +873,13 @@ func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
 
 	bools := []string{}
 	musts := []string{}
-	//发布时间最新三个月
-	musts = append(musts, fmt.Sprintf(bidTime, time.Now().AddDate(0, -3, 0).Unix()))
+	//发布时间最新7天
+	musts = append(musts, fmt.Sprintf(bidTime, time.Now().AddDate(0, 0, -7).Unix()))
 	//省份
 	areaCity := []string{}
-	if len(scd.Area) > 0 {
+	if len(bsp.Area) > 0 {
 		areaquery := `{"terms":{"area":[`
-		for k, v := range scd.Area {
+		for k, v := range bsp.Area {
 			if k > 0 {
 				areaquery += `,`
 			}
@@ -890,9 +890,9 @@ func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
 	}
 
 	//城市
-	if len(scd.City) > 0 {
+	if len(bsp.City) > 0 {
 		areaquery := `{"terms":{"city":[`
-		for k, v := range scd.City {
+		for k, v := range bsp.City {
 			if k > 0 {
 				areaquery += `,`
 			}
@@ -904,9 +904,9 @@ func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
 	if len(areaCity) > 0 {
 		musts = append(musts, fmt.Sprintf(query_bool_should, strings.Join(areaCity, ",")))
 	}
-	if len(scd.Subtype) > 0 {
+	if len(bsp.Subtype) > 0 {
 		subquery := `{"terms":{"subtype":[`
-		for k, v := range scd.Subtype {
+		for k, v := range bsp.Subtype {
 			if k > 0 {
 				subquery += `,`
 			}
@@ -915,9 +915,9 @@ func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
 		subquery += `]}}`
 		musts = append(musts, subquery)
 	}
-	if len(scd.Buyerclass) > 0 {
+	if len(bsp.Buyerclass) > 0 {
 		Buyerclass := `{"terms":{"buyerclass":[`
-		for k, v := range scd.Buyerclass {
+		for k, v := range bsp.Buyerclass {
 			if k > 0 {
 				Buyerclass += `,`
 			}
@@ -927,16 +927,16 @@ func (s *subscribePush) getDefaultDatasSQL(scd *ViewCondition) (str string) {
 		musts = append(musts, Buyerclass)
 	}
 	boolsNum := 0 //should
-	if len(scd.Keyword) > 0 {
+	if len(bsp.Keyword) > 0 {
 		boolsNum = 1
-		if scd.SelectType == "" || scd.SelectType == "2" {
-			scd.SelectType = "detail\", \"title"
+		if bsp.SelectType == "" || bsp.SelectType == "2" {
+			bsp.SelectType = "detail\", \"title"
 		} else {
-			scd.SelectType = "title"
+			bsp.SelectType = "title"
 		}
-		multi_match = fmt.Sprintf(multi_match, "%s", "\""+scd.SelectType+"\"")
+		multi_match = fmt.Sprintf(multi_match, "%s", "\""+bsp.SelectType+"\"")
 
-		for _, v := range scd.Keyword {
+		for _, v := range bsp.Keyword {
 			shoulds := []string{}
 			must_not := []string{}
 			//附加词