Skip to content

JQL Builder

The root gojira package provides a JQL builder for constructing Jira Query Language queries programmatically.

Basic Usage

import "github.com/grokify/gojira"

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    StatusesIncl: [][]string{{"Open", "In Progress"}},
}

query := jql.String()
// Result: "project = 'FOO' AND status IN ('Open', 'In Progress')"

JQL Structure

The JQL struct supports include and exclude conditions for various fields:

type JQL struct {
    // Date conditions
    CreatedGT       *time.Time
    CreatedGTE      *time.Time
    CreatedLT       *time.Time
    CreatedLTE      *time.Time
    DueGT           *time.Time
    DueGTE          *time.Time
    DueLT           *time.Time
    DueLTE          *time.Time
    UpdatedGT       *time.Time
    UpdatedGTE      *time.Time
    UpdatedLT       *time.Time
    UpdatedLTE      *time.Time

    // String field conditions (outer = AND, inner = IN)
    FiltersIncl     [][]string
    FiltersExcl     [][]string
    IssuesIncl      [][]string
    IssuesExcl      [][]string
    KeysIncl        [][]string
    KeysExcl        [][]string
    LabelsIncl      [][]string
    LabelsExcl      [][]string
    ParentsIncl     [][]string
    ParentsExcl     [][]string
    ProjectsIncl    [][]string
    ProjectsExcl    [][]string
    ResolutionIncl  [][]string
    ResolutionExcl  [][]string
    StatusesIncl    [][]string
    StatusesExcl    [][]string
    TypesIncl       [][]string
    TypesExcl       [][]string

    // Text search
    SummaryLike     []string
    SummaryNotLike  []string
    TextLike        []string
    TextNotLike     []string

    // Custom fields
    CustomFieldIncl map[string][]string
    CustomFieldExcl map[string][]string

    // Raw JQL fragments
    Raw             []string
}

Examples

Single Project, Single Status

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    StatusesIncl: [][]string{{"Open"}},
}
// project = 'FOO' AND status = 'Open'

Multiple Projects

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO", "BAR", "BAZ"}},
}
// project IN ('FOO', 'BAR', 'BAZ')

Multiple Status Groups (AND)

jql := gojira.JQL{
    StatusesIncl: [][]string{
        {"Open", "In Progress"},  // First condition
        {"Blocked"},               // AND this condition
    },
}
// status IN ('Open', 'In Progress') AND status = 'Blocked'

Exclude Conditions

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    StatusesExcl: [][]string{{"Done", "Closed"}},
}
// project = 'FOO' AND status NOT IN ('Done', 'Closed')

Date Filtering

import "time"

sevenDaysAgo := time.Now().AddDate(0, 0, -7)
jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    CreatedGTE:   &sevenDaysAgo,
}
// project = 'FOO' AND createdDate >= 2024-01-08
jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    TextLike:     []string{"error"},
}
// project = 'FOO' AND text ~ "error"
jql := gojira.JQL{
    SummaryLike: []string{"login", "authentication"},
}
// summary ~ "login" AND summary ~ "authentication"

Custom Fields

jql := gojira.JQL{
    CustomFieldIncl: map[string][]string{
        "customfield_10001": {"Team A", "Team B"},
    },
}
// cf[10001] IN ('Team A', 'Team B')

Raw JQL

For complex conditions not supported by the builder:

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
    Raw: []string{
        "fixVersion = '1.0'",
        "sprint in openSprints()",
    },
}
// project = 'FOO' AND fixVersion = '1.0' AND sprint in openSprints()

Labels

jql := gojira.JQL{
    LabelsIncl: [][]string{{"bug", "urgent"}},
}
// labels IN ('bug', 'urgent')

Query String

Generate URL-encoded query string:

jql := gojira.JQL{
    ProjectsIncl: [][]string{{"FOO"}},
}

qs := jql.QueryString()
// jql=project%20%3D%20%27FOO%27

JQL Metadata

Store query metadata for tracking:

jql := gojira.JQL{
    Meta: gojira.JQLMeta{
        Name:        "Open Bugs",
        Description: "All open bugs in FOO project",
        QueryTime:   time.Now(),
    },
    ProjectsIncl: [][]string{{"FOO"}},
    TypesIncl:    [][]string{{"Bug"}},
    StatusesIncl: [][]string{{"Open"}},
}

Splitting Long Queries

For very long value lists that exceed Jira's limits:

values := []string{"KEY-1", "KEY-2", ..., "KEY-1000"}

jqls := gojira.JQLStringsSimple(
    "key",    // Field name
    false,    // Exclude (false = include)
    values,   // Values
    0,        // Max length (0 = default)
)

// Returns multiple JQL strings, each under the length limit
for _, jql := range jqls {
    issues, _ := client.IssueAPI.SearchIssues(jql, false)
    // Process issues...
}

Complete Example

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/grokify/gojira"
    "github.com/grokify/gojira/rest"
)

func main() {
    // Build JQL query
    oneMonthAgo := time.Now().AddDate(0, -1, 0)

    jql := gojira.JQL{
        ProjectsIncl: [][]string{{"FOO", "BAR"}},
        TypesIncl:    [][]string{{"Bug", "Task"}},
        StatusesExcl: [][]string{{"Done", "Closed"}},
        CreatedGTE:   &oneMonthAgo,
    }

    query := jql.String()
    fmt.Printf("JQL: %s\n", query)

    // Use with client
    client, err := rest.NewClientFromBasicAuth(
        "https://your-instance.atlassian.net",
        "your-email@example.com",
        "your-api-token",
        false,
    )
    if err != nil {
        log.Fatal(err)
    }

    issues, err := client.IssueAPI.SearchIssues(query, true)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d issues\n", len(issues))
}