Skip to content

Commit cfd17df

Browse files
Emphasize correct parentheses for mixed AND/OR queries
1 parent c39ed35 commit cfd17df

File tree

1 file changed

+204
-1
lines changed

1 file changed

+204
-1
lines changed

devel/docs/ticketsql_grammar.md

Lines changed: 204 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,166 @@ Use parentheses to control evaluation order:
764764
(Status = 'new' OR Status = 'open') AND Priority > 50
765765
```
766766

767+
### CRITICAL: AND/OR Precedence and Parentheses
768+
769+
**This section is essential for generating correct queries.** Incorrect parentheses are a common source of bugs that can either return no results or return far more results than intended.
770+
771+
#### Operator Precedence Rule
772+
773+
**AND has higher precedence than OR.** This means that without explicit parentheses, AND conditions bind together first, then OR connects the results. This is the same as standard SQL and most programming languages.
774+
775+
#### The Mandatory Rule
776+
777+
**When a query combines AND and OR, you MUST use parentheses to group the OR alternatives together.**
778+
779+
The pattern is: `(OR_alternatives) AND filter_condition`
780+
781+
Without parentheses, the query will be evaluated incorrectly and return wrong results.
782+
783+
#### Correct Patterns to Follow
784+
785+
**ALWAYS use these patterns when combining AND with OR:**
786+
787+
##### Pattern 1: Multiple Queues with Status Filter
788+
789+
**Natural language**: "Open tickets in Support or General queue"
790+
791+
```ticketsql
792+
(Queue = 'Support' OR Queue = 'General') AND Status = 'open'
793+
```
794+
795+
**Natural language**: "New or stalled tickets in the Engineering queue"
796+
797+
```ticketsql
798+
Queue = 'Engineering' AND (Status = 'new' OR Status = 'stalled')
799+
```
800+
801+
##### Pattern 2: Multiple Owners with Additional Filters
802+
803+
**Natural language**: "Tickets owned by alice or bob that are high priority"
804+
805+
```ticketsql
806+
(Owner = 'alice' OR Owner = 'bob') AND Priority = 'High'
807+
```
808+
809+
**Natural language**: "Active tickets owned by alice or bob in the Support queue"
810+
811+
```ticketsql
812+
(Owner = 'alice' OR Owner = 'bob') AND Status = '__Active__' AND Queue = 'Support'
813+
```
814+
815+
##### Pattern 3: Multiple Statuses with Queue or Owner Filter
816+
817+
**Natural language**: "My tickets that are new or open"
818+
819+
```ticketsql
820+
Owner = '__CurrentUser__' AND (Status = 'new' OR Status = 'open')
821+
```
822+
823+
**Natural language**: "New or stalled tickets in General queue with high priority"
824+
825+
```ticketsql
826+
Queue = 'General' AND (Status = 'new' OR Status = 'stalled') AND Priority = 'High'
827+
```
828+
829+
##### Pattern 4: Multiple Requestors with Filters
830+
831+
**Natural language**: "Open tickets from customer@example.com or support@example.com"
832+
833+
```ticketsql
834+
(Requestor = 'customer@example.com' OR Requestor = 'support@example.com') AND Status = 'open'
835+
```
836+
837+
##### Pattern 5: Alternative Conditions with Common Filter
838+
839+
**Natural language**: "High priority tickets that are either in Support queue or overdue"
840+
841+
```ticketsql
842+
Priority = 'High' AND (Queue = 'Support' OR Due < 'today')
843+
```
844+
845+
**Natural language**: "My tickets that are either overdue or high priority"
846+
847+
```ticketsql
848+
Owner = '__CurrentUser__' AND (Due < 'today' OR Priority = 'High')
849+
```
850+
851+
##### Pattern 6: Multiple Custom Field Values
852+
853+
**Natural language**: "Open tickets in Engineering or Sales department"
854+
855+
```ticketsql
856+
(CF.{Department} = 'Engineering' OR CF.{Department} = 'Sales') AND Status = 'open'
857+
```
858+
859+
**Natural language**: "Active bugs or feature requests"
860+
861+
```ticketsql
862+
Status = '__Active__' AND (CF.{Category} = 'Bug' OR CF.{Category} = 'Feature Request')
863+
```
864+
865+
##### Pattern 7: Complex Multi-Level Grouping
866+
867+
**Natural language**: "High priority Support tickets or any Engineering tickets that are open"
868+
869+
```ticketsql
870+
((Queue = 'Support' AND Priority = 'High') OR Queue = 'Engineering') AND Status = 'open'
871+
```
872+
873+
**Natural language**: "Tickets owned by alice in Support or bob in Engineering"
874+
875+
```ticketsql
876+
(Owner = 'alice' AND Queue = 'Support') OR (Owner = 'bob' AND Queue = 'Engineering')
877+
```
878+
879+
##### Pattern 8: Multiple Independent OR Groups
880+
881+
**Natural language**: "New or open tickets in Support or General queue"
882+
883+
```ticketsql
884+
(Status = 'new' OR Status = 'open') AND (Queue = 'Support' OR Queue = 'General')
885+
```
886+
887+
Both OR groups need their own parentheses.
888+
889+
#### Decision Guide for Parentheses
890+
891+
When translating natural language to TicketSQL:
892+
893+
1. **Identify the OR conditions**: What alternatives is the user asking for?
894+
- "Support OR General" → two queue alternatives
895+
- "new OR open" → two status alternatives
896+
- "alice OR bob" → two user alternatives
897+
898+
2. **Identify the AND conditions**: What filters apply to ALL results?
899+
- "open tickets" → status filter applies to everything
900+
- "high priority" → priority filter applies to everything
901+
- "in my queue" → queue filter applies to everything
902+
903+
3. **Group the OR alternatives**: Put parentheses around OR conditions that represent alternatives for the same concept
904+
905+
4. **Apply AND conditions outside**: The AND filters should be outside the parenthesized OR group
906+
907+
#### When Parentheses Are Optional
908+
909+
Parentheses are NOT needed when:
910+
911+
1. **Only AND conditions** (no OR):
912+
```ticketsql
913+
Queue = 'Support' AND Status = 'open' AND Priority = 'High'
914+
```
915+
916+
2. **Only OR conditions** (no AND):
917+
```ticketsql
918+
Queue = 'Support' OR Queue = 'General' OR Queue = 'Sales'
919+
```
920+
921+
3. **OR at the top level with complete AND groups**:
922+
```ticketsql
923+
(Queue = 'Support' AND Status = 'open') OR (Queue = 'General' AND Status = 'new')
924+
```
925+
Here each OR branch is a complete, self-contained condition.
926+
767927
### NOT (via !=)
768928
TicketSQL uses `!=` rather than explicit NOT:
769929
```ticketsql
@@ -1406,6 +1566,41 @@ DependsOn IS NOT NULL # Only way to query - check that dependency exists
14061566

14071567
**Why**: Link fields (DependsOn, RefersTo, etc.) do NOT support subfield syntax. You can only query whether a link exists (`IS NOT NULL`) or query by ticket ID (`DependsOn = 123`). To find tickets depending on open tickets, you would need two queries: first find open tickets, then search for tickets depending on those IDs.
14081568

1569+
### Common Error #15: Missing Parentheses with AND/OR
1570+
1571+
**The Rule**: When a query combines AND and OR operators, you MUST wrap the OR alternatives in parentheses.
1572+
1573+
AND has higher precedence than OR. Without parentheses, the query will return incorrect results - typically returning far more tickets than intended because one of the OR branches won't be filtered.
1574+
1575+
**Correct Examples**:
1576+
1577+
"Open tickets in Support or General queue":
1578+
```ticketsql
1579+
(Queue = 'Support' OR Queue = 'General') AND Status = 'open'
1580+
```
1581+
1582+
"My tickets that are new or open":
1583+
```ticketsql
1584+
Owner = '__CurrentUser__' AND (Status = 'new' OR Status = 'open')
1585+
```
1586+
1587+
"High priority tickets from alice or bob":
1588+
```ticketsql
1589+
(Requestor.Name = 'alice' OR Requestor.Name = 'bob') AND Priority = 'High'
1590+
```
1591+
1592+
"Overdue tickets in Support or Sales":
1593+
```ticketsql
1594+
(Queue = 'Support' OR Queue = 'Sales') AND Due < 'today'
1595+
```
1596+
1597+
"Active bugs or feature requests":
1598+
```ticketsql
1599+
Status = '__Active__' AND (CF.{Type} = 'Bug' OR CF.{Type} = 'Feature')
1600+
```
1601+
1602+
**Key insight**: The OR alternatives (queues, statuses, users, etc.) must be grouped together with parentheses so the AND filter applies to ALL of them.
1603+
14091604
### Translation Decision Tree for AI Systems
14101605

14111606
When translating natural language to TicketSQL, follow this decision tree:
@@ -1438,13 +1633,21 @@ When translating natural language to TicketSQL, follow this decision tree:
14381633
- Column comparisons? → Don't quote the column reference
14391634
- Priority strings? → Use Priority = 'High' (if configured) or Priority > 80
14401635

1441-
5. **Validate syntax**:
1636+
5. **Handle AND/OR combinations** (CRITICAL):
1637+
- Does the query have both AND and OR? → Apply parentheses rules
1638+
- Are there OR alternatives that should be filtered by AND conditions? → Wrap OR in parentheses
1639+
- Example: "open tickets in Support or General" → `(Queue = 'Support' OR Queue = 'General') AND Status = 'open'`
1640+
- Example: "my tickets that are new or open" → `Owner = '__CurrentUser__' AND (Status = 'new' OR Status = 'open')`
1641+
- Remember: AND binds tighter than OR, so without parentheses `A OR B AND C` = `A OR (B AND C)`
1642+
1643+
6. **Validate syntax**:
14421644
- Dates quoted? ✓
14431645
- Column references unquoted? ✓
14441646
- IS NULL not = NULL? ✓
14451647
- Custom role has subfield? ✓
14461648
- Time in minutes not hours? ✓
14471649
- SHALLOW only on watchers? ✓
1650+
- AND/OR parentheses correct? ✓ (OR alternatives grouped when filtered by AND)
14481651

14491652
---
14501653

0 commit comments

Comments
 (0)