mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-07-13 21:18:50 +00:00
enh(issue-search): treat terms in query strings with no explicit operator as MUST (#13025)
The rationale for this decision is the apparent confusion regarding the number of issues that were presented. For a query such as `foo bar` the natural expectation would be results that contain both `foo` and `bar` not either `foo` or `bar`. However, for a query such as `+foo bar` the result MUST contain `foo`, while `bar` affects only the scoring of issues. A downside of such an approach is the inability to fully represent `foo OR bar` as the current implementation imposes that the query must contain atleast one MUST (or MUST NOT) term. closes #12935 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/13025 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
parent
7a3d9b486e
commit
14c5f0105f
2 changed files with 61 additions and 28 deletions
|
|
@ -6,6 +6,7 @@ package internal
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -208,7 +209,19 @@ func (o *SearchOptions) WithKeyword(ctx context.Context, keyword string) (err er
|
|||
return err
|
||||
}
|
||||
|
||||
o.Tokens = tokens
|
||||
// if the query only contains terms that are SHOULD the results returned are subpar.
|
||||
// convert them to MUST in this case.
|
||||
if slices.ContainsFunc(tokens, func(token Token) bool {
|
||||
return token.Kind != BoolOptShould
|
||||
}) {
|
||||
o.Tokens = tokens
|
||||
} else if len(tokens) != 0 {
|
||||
o.Tokens = make([]Token, 0, len(tokens))
|
||||
for _, token := range tokens {
|
||||
token.Kind = BoolOptMust
|
||||
o.Tokens = append(o.Tokens, token)
|
||||
}
|
||||
}
|
||||
|
||||
ids, err := user.GetUserIDsByNames(ctx, userNames, true)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -39,12 +39,12 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -54,12 +54,12 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -69,12 +69,12 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -123,24 +123,44 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "Hello -World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptNot,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+\"Hello World\"",
|
||||
Keyword: "+\"Hello World\" Hello",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -159,7 +179,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "+Hello -World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -169,7 +189,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "+Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -179,7 +199,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "\\Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -189,7 +209,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "\"Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -207,7 +227,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -221,7 +241,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: " World ",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -231,7 +251,7 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -241,29 +261,29 @@ var testOpts = []testIssueQueryStringOpt{
|
|||
{
|
||||
Term: "Best",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "Ever",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestIssueQueryString(t *testing.T) {
|
||||
var opt SearchOptions
|
||||
ctx := t.Context()
|
||||
for _, res := range testOpts {
|
||||
t.Run(res.Keyword, func(t *testing.T) {
|
||||
opt := &SearchOptions{}
|
||||
require.NoError(t, opt.WithKeyword(ctx, res.Keyword))
|
||||
assert.Equal(t, res.Results, opt.Tokens)
|
||||
assert.Equal(t, res.Results, opt.Tokens, "failed for keyword `%s`", res.Keyword)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -326,7 +346,7 @@ func TestIssueQueryStringWithFilters(t *testing.T) {
|
|||
Tokens: []Token{
|
||||
{
|
||||
Term: "hi",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -368,7 +388,7 @@ func TestIssueQueryStringWithFilters(t *testing.T) {
|
|||
Tokens: []Token{
|
||||
{
|
||||
Term: "test",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -386,7 +406,7 @@ func TestIssueQueryStringWithFilters(t *testing.T) {
|
|||
Tokens: []Token{
|
||||
{
|
||||
Term: "author:",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -402,12 +422,12 @@ func TestIssueQueryStringWithFilters(t *testing.T) {
|
|||
Tokens: []Token{
|
||||
{
|
||||
Term: "author:",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
{
|
||||
Term: "test",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -419,7 +439,7 @@ func TestIssueQueryStringWithFilters(t *testing.T) {
|
|||
Tokens: []Token{
|
||||
{
|
||||
Term: "modified:",
|
||||
Kind: BoolOptShould,
|
||||
Kind: BoolOptMust,
|
||||
Fuzzy: true,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue