mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-06-18 15:15:18 +00:00
feat(issue search): query string for boolean operators and phrase search (#6952)
closes #6909 related to forgejo/design#14 # Description Adds the following boolean operators for issues when using an indexer (with minor caveats) - `+term`: `term` MUST be present for any result - `-term`: negation; exclude results that contain `term` - `"this is a term"`: matches the exact phrase `this is a term` In all cases the special characters may be escaped by the prefix `\` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6952 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Otto <otto@codeberg.org> Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com> Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
This commit is contained in:
parent
eaa641c21e
commit
cddf608cb9
19 changed files with 451 additions and 192 deletions
171
modules/indexer/issues/internal/qstring_test.go
Normal file
171
modules/indexer/issues/internal/qstring_test.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testIssueQueryStringOpt struct {
|
||||
Keyword string
|
||||
Results []Token
|
||||
}
|
||||
|
||||
var testOpts = []testIssueQueryStringOpt{
|
||||
{
|
||||
Keyword: "Hello",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "Hello World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello +World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+Hello -World",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
{
|
||||
Term: "World",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptNot,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "+\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptMust,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "-\"Hello World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "Hello World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptNot,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\"+Hello -World\"",
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "+Hello -World",
|
||||
Fuzzy: false,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\+Hello", // \+Hello => +Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "+Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\\\Hello", // \\Hello => \Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "\\Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Keyword: "\\\"Hello", // \"Hello => "Hello
|
||||
Results: []Token{
|
||||
{
|
||||
Term: "\"Hello",
|
||||
Fuzzy: true,
|
||||
Kind: BoolOptShould,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestIssueQueryString(t *testing.T) {
|
||||
var opt SearchOptions
|
||||
for _, res := range testOpts {
|
||||
t.Run(opt.Keyword, func(t *testing.T) {
|
||||
opt.Keyword = res.Keyword
|
||||
tokens, err := opt.Tokens()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, res.Results, tokens)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue