From 26f069c87ba6a139a9b84e76286b73554b2edcb1 Mon Sep 17 00:00:00 2001 From: ras0q Date: Sat, 21 Dec 2024 17:18:43 +0900 Subject: [PATCH] =?UTF-8?q?feat(service/search):=20from/to=E3=81=AE?= =?UTF-8?q?=E8=A4=87=E6=95=B0=E6=8C=87=E5=AE=9A=E3=82=92=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related to #2526 - `?to={userID}&to={userID2}&to={groupID}`のような指定ができるようになる - これはechoのパースの方式に従った書き方だが、実際はORなのにANDのように見えてしまうことを懸念している - `?to={userID},{userID2},{groupID}`を実装するなら一旦生文字列で受け取って自前でスライスする必要がある - フロントエンド側では`to:me`や`to:@Ras`をuserIDとuserが所属するgroupIDsのORに変換することでグループメンションも受け取れる - 毎回グループメンションが必要かは議論の余地がありそうだが、そっちはフロントエンド側に任せます - 指定が1つの場合は従来通りの検索結果が返るのでAPIの形式自体に破壊的変更はない - OpenAPIから生成したAPI Clientの型がuuidから[]uuidに修正する必要は出てくるかも --- docs/v3-api.yaml | 12 ++++++--- service/search/engine.go | 4 +-- service/search/es.go | 58 +++++++++++++++++++++++++++------------- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/docs/v3-api.yaml b/docs/v3-api.yaml index b0820d563..4710073f6 100644 --- a/docs/v3-api.yaml +++ b/docs/v3-api.yaml @@ -119,14 +119,18 @@ paths: name: in description: メッセージが投稿されたチャンネル - schema: - type: string - format: uuid + type: array + items: + type: string + format: uuid in: query name: to description: メンションされたユーザー - schema: - type: string - format: uuid + type: array + items: + type: string + format: uuid in: query name: from description: メッセージを投稿したユーザー diff --git a/service/search/engine.go b/service/search/engine.go index 0183fea8b..cd22dc2a7 100644 --- a/service/search/engine.go +++ b/service/search/engine.go @@ -32,8 +32,8 @@ type Query struct { After optional.Of[time.Time] `query:"after"` // 以降(投稿日時) 2020-06-20T00:00:00Z Before optional.Of[time.Time] `query:"before"` // 以前(投稿日時) In optional.Of[uuid.UUID] `query:"in"` // 投稿チャンネル - To optional.Of[uuid.UUID] `query:"to"` // メンション先 - From optional.Of[uuid.UUID] `query:"from"` // 投稿者 + To []uuid.UUID `query:"to"` // メンション先 + From []uuid.UUID `query:"from"` // 投稿者 Citation optional.Of[uuid.UUID] `query:"citation"` // 引用しているメッセージ Bot optional.Of[bool] `query:"bot"` // 投稿者がBotか HasURL optional.Of[bool] `query:"hasURL"` // URLの存在 diff --git a/service/search/es.go b/service/search/es.go index 9368a13b0..9152cedd1 100644 --- a/service/search/es.go +++ b/service/search/es.go @@ -260,24 +260,17 @@ func NewESEngine(mm message.Manager, cm channel.Manager, repo repository.Reposit type searchQuery m type searchBody struct { - Query *struct { - Bool *struct { - Musts []searchQuery `json:"must,omitempty"` - } `json:"bool,omitempty"` - } `json:"query,omitempty"` + Query searchQuery `json:"query,omitempty"` } -func newSearchBody(sq []searchQuery) searchBody { - sb := searchBody{ - Query: &struct { - Bool *struct { - Musts []searchQuery `json:"must,omitempty"` - } `json:"bool,omitempty"` - }{Bool: &struct { - Musts []searchQuery `json:"must,omitempty"` - }{Musts: sq}}, +func newSearchBody(andQueries []searchQuery) searchBody { + return searchBody{ + Query: searchQuery{ + "bool": boolQuery{ + Must: andQueries, + }, + }, } - return sb } type simpleQueryString struct { @@ -286,6 +279,11 @@ type simpleQueryString struct { DefaultOperator string `json:"default_operator"` } +type boolQuery struct { + Must []searchQuery `json:"must,omitempty"` + Should []searchQuery `json:"should,omitempty"` +} + type rangeQuery map[string]rangeParameters type rangeParameters struct { @@ -338,12 +336,34 @@ func (e *esEngine) Do(q *Query) (Result, error) { musts = append(musts, searchQuery{"term": termQuery{"isPublic": termQueryParameter{Value: true}}}) } - if q.To.Valid { - musts = append(musts, searchQuery{"term": termQuery{"to": termQueryParameter{Value: q.To}}}) + if len(q.To) > 0 { + orQueries := make([]searchQuery, 0, len(q.To)) + for _, toID := range q.To { + orQueries = append(orQueries, searchQuery{"term": termQuery{"to": termQueryParameter{Value: toID}}}) + } + + sq := searchQuery{"bool": boolQuery{Should: orQueries}} + if len(q.To) == 1 { + // OR検索が不要 + sq = orQueries[0] + } + + musts = append(musts, sq) } - if q.From.Valid { - musts = append(musts, searchQuery{"term": termQuery{"userId": termQueryParameter{Value: q.From}}}) + if len(q.From) > 0 { + orQueries := make([]searchQuery, 0, len(q.From)) + for _, fromID := range q.From { + orQueries = append(orQueries, searchQuery{"term": termQuery{"userId": termQueryParameter{Value: fromID}}}) + } + + sq := searchQuery{"bool": boolQuery{Should: orQueries}} + if len(q.From) == 1 { + // OR検索が不要 + sq = orQueries[0] + } + + musts = append(musts, sq) } if q.Citation.Valid {