refactor: unify tags and edges into single rels table

This commit is contained in:
2026-04-01 12:50:45 +02:00
parent 27c72db042
commit 6288879468
9 changed files with 296 additions and 249 deletions

View File

@@ -26,18 +26,22 @@ var addCmd = &cobra.Command{
Title: args[0],
Content: cContent,
DueDate: cDue,
Tags: append([]string{}, cTags...),
}
// Shorthand flags expand to tags or rels.
// --tag is an alias for --rel with no target.
for _, tag := range cTags {
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType(tag), Target: ""})
}
// Shorthand flags expand to property rels or edge rels.
if cType != "" {
input.Tags = append(input.Tags, "_type::"+cType)
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_type::" + cType), Target: ""})
}
if cStatus != "" {
input.Tags = append(input.Tags, "_status::"+cStatus)
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_status::" + cStatus), Target: ""})
}
if cPrio != "" {
input.Tags = append(input.Tags, "_prio::"+cPrio)
input.Rels = append(input.Rels, service.RelInput{Type: models.RelType("_prio::" + cPrio), Target: ""})
}
if cNamespace != "" {
input.Rels = append(input.Rels, service.RelInput{Type: models.RelInNamespace, Target: cNamespace})
@@ -75,6 +79,6 @@ func init() {
f.StringVar(&cAssignee, "assignee", "", "assignee username or ID")
f.StringVar(&cDue, "due", "", "due date")
f.StringVar(&cContent, "content", "", "node body")
f.StringArrayVar(&cTags, "tag", nil, "custom tags")
f.StringArrayVar(&cRels, "rel", nil, "additional relations (type:target)")
f.StringArrayVar(&cTags, "tag", nil, "label tag (alias for --rel tagname)")
f.StringArrayVar(&cRels, "rel", nil, "relation (prefix::value or relname:target)")
}

View File

@@ -22,19 +22,22 @@ var listCmd = &cobra.Command{
return
}
filter := service.ListFilter{
Tags: append([]string{}, lTags...),
var filter service.ListFilter
// --tag is an alias for a label filter with no target.
for _, tag := range lTags {
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType(tag), Target: ""})
}
// Shorthand flags expand to tag prefixes or rel filters.
// Shorthand flags expand to property filters or edge filters.
if lStatus != "" {
filter.Tags = append(filter.Tags, "_status::"+lStatus)
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_status::" + lStatus), Target: ""})
}
if lPrio != "" {
filter.Tags = append(filter.Tags, "_prio::"+lPrio)
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_prio::" + lPrio), Target: ""})
}
if lType != "" {
filter.Tags = append(filter.Tags, "_type::"+lType)
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelType("_type::" + lType), Target: ""})
}
if lNamespace != "" {
filter.Rels = append(filter.Rels, service.RelInput{Type: models.RelInNamespace, Target: lNamespace})
@@ -66,8 +69,8 @@ var listCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(listCmd)
f := listCmd.Flags()
f.StringArrayVar(&lTags, "tag", nil, "filter by tag")
f.StringArrayVar(&lRels, "rel", nil, "filter by relation (type:target)")
f.StringArrayVar(&lTags, "tag", nil, "filter by label tag")
f.StringArrayVar(&lRels, "rel", nil, "filter by relation (prefix::value or relname:target)")
f.StringVar(&lStatus, "status", "", "filter by status")
f.StringVar(&lPrio, "prio", "", "filter by priority")
f.StringVar(&lType, "type", "", "filter by type")

View File

@@ -3,14 +3,24 @@ package cmd
import (
"axolotl/models"
"axolotl/service"
"fmt"
"strings"
)
// parseRelInput parses a "type:target" string into a RelInput.
// parseRelInput parses a rel string into a RelInput.
//
// Formats:
// - "prefix::value" → property rel with no target (tag)
// - "relname:target" → edge rel with a target node
// - "tagname" → simple label rel with no target (alias for --tag)
func parseRelInput(s string) (service.RelInput, error) {
if p := strings.SplitN(s, ":", 2); len(p) == 2 {
return service.RelInput{Type: models.RelType(p[0]), Target: p[1]}, nil
if strings.Contains(s, "::") {
// Property: name::value — no target node.
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
}
return service.RelInput{}, fmt.Errorf("invalid relation format: %s (expected type:target)", s)
if idx := strings.Index(s, ":"); idx >= 0 {
// Edge rel: relname:target.
return service.RelInput{Type: models.RelType(s[:idx]), Target: s[idx+1:]}, nil
}
// Simple label tag — no target node.
return service.RelInput{Type: models.RelType(s), Target: ""}, nil
}

View File

@@ -27,10 +27,7 @@ var updateCmd = &cobra.Command{
return
}
input := service.UpdateInput{
AddTags: append([]string{}, uAddTags...),
RemoveTags: uRmTags,
}
var input service.UpdateInput
if cmd.Flags().Changed("title") {
input.Title = &uTitle
@@ -46,15 +43,23 @@ var updateCmd = &cobra.Command{
input.DueDate = &empty
}
// Shorthand flags expand to tags or rels.
// --tag / --tag-remove are aliases for --rel / --rel-remove with no target.
for _, tag := range uAddTags {
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType(tag), Target: ""})
}
for _, tag := range uRmTags {
input.RemoveRels = append(input.RemoveRels, service.RelInput{Type: models.RelType(tag), Target: ""})
}
// Shorthand flags expand to property rels or edge rels.
if cmd.Flags().Changed("type") {
input.AddTags = append(input.AddTags, "_type::"+uType)
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_type::" + uType), Target: ""})
}
if cmd.Flags().Changed("status") {
input.AddTags = append(input.AddTags, "_status::"+uStatus)
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_status::" + uStatus), Target: ""})
}
if cmd.Flags().Changed("prio") {
input.AddTags = append(input.AddTags, "_prio::"+uPrio)
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelType("_prio::" + uPrio), Target: ""})
}
if cmd.Flags().Changed("namespace") {
input.AddRels = append(input.AddRels, service.RelInput{Type: models.RelInNamespace, Target: uNamespace})
@@ -101,8 +106,8 @@ func init() {
f.StringVar(&uType, "type", "", "node type")
f.StringVar(&uNamespace, "namespace", "", "namespace name or ID")
f.StringVar(&uAssignee, "assignee", "", "assignee username or ID")
f.StringArrayVar(&uAddTags, "tag", nil, "add tags")
f.StringArrayVar(&uRmTags, "tag-remove", nil, "remove tags")
f.StringArrayVar(&uAddRels, "rel", nil, "add relations (type:target)")
f.StringArrayVar(&uRmRels, "rel-remove", nil, "remove relations (type:target)")
f.StringArrayVar(&uAddTags, "tag", nil, "add label tag (alias for --rel tagname)")
f.StringArrayVar(&uRmTags, "tag-remove", nil, "remove label tag")
f.StringArrayVar(&uAddRels, "rel", nil, "add relation (prefix::value or relname:target)")
f.StringArrayVar(&uRmRels, "rel-remove", nil, "remove relation (prefix::value or relname:target)")
}