1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
package cmd
import (
"flag"
"fmt"
"io"
"os"
"github.com/jpappel/atlas/pkg/shell"
"github.com/jpappel/atlas/pkg/util"
)
var helpTopics = []string{
"index", "i",
"index build", "i build",
"index update", "i update",
"index tidy", "i tidy",
"query", "q",
"shell",
"server",
}
func PrintHelp(w io.Writer) {
fmt.Fprintln(w, "atlas is a note indexing and querying tool")
fmt.Fprintf(w, "\nUsage:\n %s [global-flags] <command>\n\n", os.Args[0])
fmt.Fprintln(w, "Commands:")
fmt.Fprintln(w, " index <subcommand> - build, update, or modify an index")
fmt.Fprintln(w, " query <subcommand> - search against an index")
fmt.Fprintln(w, " shell - start a debug shell")
fmt.Fprintln(w, " server - start an http query server (EXPERIMENTAL)")
fmt.Fprintln(w, " help <help-topic> - print help info")
}
func PrintGlobalFlags(w io.Writer) {
fmt.Fprintln(w, "\nGlobal Flags:")
PrintFlagSet(w, flag.CommandLine)
}
func PrintFlagSet(w io.Writer, fs *flag.FlagSet) {
w_ := fs.Output()
fs.SetOutput(w)
fs.PrintDefaults()
fs.SetOutput(w_)
}
func Help(topic string, w io.Writer) {
fs := flag.NewFlagSet(topic, flag.ExitOnError)
switch topic {
case "index", "i":
SetupIndexFlags(nil, fs, &IndexFlags{})
fmt.Fprintf(w, "%s [global-flags] index [index-flags] <subcommand>\n\n", os.Args[0])
fmt.Fprintln(w, "Subcommands:")
fmt.Fprintln(w, " build - create a new index")
fmt.Fprintln(w, " update - update an existing index")
fmt.Fprintln(w, " tidy - cleanup an index")
fmt.Fprintf(w, "\nSee %s help index <subcommand> for subcommand help\n\n", os.Args[0])
fmt.Fprintln(w, "Index Flags:")
PrintFlagSet(w, fs)
case "i build", "index build":
fmt.Fprintf(w, "%s [global-flags] index [index-flags] build\n\n", os.Args[0])
fmt.Fprintln(w, "Crawl files starting at `-root` to build an index stored in `-db`")
fmt.Fprintln(w, "Use this subcommand to generate the initial index, then update it with `atlas index update`")
case "i update", "index update":
fmt.Fprintf(w, "%s [global-flags] index [index-flags] update\n\n", os.Args[0])
fmt.Fprintln(w, "Crawl files starting at `-root` to update an index stored in `-db`")
fmt.Fprintln(w, "Use this subcommand to update an existing index.")
fmt.Fprintln(w, "Deleted documents are removed from the index. To remove unused authors and tags run `atlas index tidy`")
case "i tidy", "index tidy":
fmt.Fprintf(w, "%s [global-flags] index tidy\n\n", os.Args[0])
fmt.Fprintln(w, "Remove unused authors or tags and optimize the database")
case "query", "q":
SetupQueryFlags(nil, fs, &QueryFlags{}, "")
fmt.Fprintf(w, "%s [global-flags] query [query-flags] <query>...\n\n", os.Args[0])
fmt.Fprintln(w, "Execute a query against the connected database")
fmt.Fprintln(w, "Query Flags:")
PrintFlagSet(w, fs)
fmt.Fprintln(w, "\nQuery Language:")
queryHelp := `Atlas' query language evaluates logical clauses composed of statements.
A clause is a collection of statements and clauses with a either 'and' or 'or' in prefix notation.
A statement has the form <category><operator><value> with an optional preceeding '-' to negate it.
As a quality of life feature, an implicit top level 'and' clause is added. This clause gets optimized out by default.
Examples:
Implicit and
atlas query "path:foobar -a=Goose" -> (and path:foobar -a=Goose)
Implicit and optimization
atlas query -optLevel=-1 "(or T=foo T=bar)" -> (and (or T=foo T=bar))
atlas query "(or T=foo T=bar)" -> (or T=foo T=bar)
Add trailing parenthesis
atlas query "(or (and a=Goose a=Duck) (and p:birds t:waterfowl" -> (or (and a=Goose a=Duck) (and p:birds t:waterfowl))
Categories have short and long identifiers. They also have an associated type which modifies
how operators are applied to it.
Category - Type
p path - String
T title - String
a author - Set
d date - Date
f filetime - Date
t tags - Set
h headings - String
l links - Set
m meta - String
Operator - Supported Types - Value
!= - All - Not Equal (Not In for Sets)
>= - Dates - Greater Than or Equal
<= - Dates - Less Than or Equal
< - Dates - Less Than
> - Dates - Greater Than
= - All - Equal (In for Sets)
: ~ - All - Approximate (Approximately In for Sets)
/ - String,Set - Regular Expression
Values containg spaces must be surrounded in double quotes.
Atlas recognizes many of the common date formats.
Example:
atlas query date>January 1, 2025 -> error
atlas query date>"2025 January 1" -> success
atlas query T:Meeting Minutes -> error
atlas query T:"Meeting Minutes" -> success
Values
String
Date
`
fmt.Fprint(w, queryHelp)
fmt.Fprintln(w, "\nOutput Format:")
outHelp := `The output format of query results can be customized by setting -outCustomFormat.
The output of each document has the value of -docSeparator appended to it.
Dates are formated using -dateFormat
Lists use -listSeparator to delimit elements
Placeholder - Type - Value
%p - Str - path
%T - Str - title
%d - Date - date
%f - Date - filetime
%a - List - authors
%t - List - tags
%h - Str - headings (newline separated)
%l - List - links
%m - Str - meta
Examples:
"%p %T %d tags:%t" -> '/a/path/to/document A Title 2006-01-02T15:04:05Z07:00 tags:tag1, tag2\n'
"<h1><a href="%p">%T</a></h1>" -> '<h1><a href="/a/path/to/document">A Title</a></h1>\n'
`
fmt.Fprint(w, outHelp)
case "shell":
fmt.Fprintf(w, "%s [global-flags] shell\n", os.Args[0])
fmt.Fprintln(w, "Simple shell for debugging queries")
fmt.Fprintln(w, "\nShell Help:")
shell.PrintHelp(w)
case "server":
SetupServerFlags(nil, fs, &ServerFlags{})
fmt.Fprintf(w, "%s [global-flags] server [server-flags]\n", os.Args[0])
fmt.Fprintln(w, "Run a server to execute queries over HTTP or a unix domain socket")
fmt.Fprintln(w, "HTTP Server:")
fmt.Fprintln(w, " To execute a query POST it in the request body to /search")
fmt.Fprintln(w, " ex. curl -d 'T:notes d>=\"January 1, 2025\"' 127.0.0.1:8080/search")
fmt.Fprintln(w, " To have the backend use the query params `sortBy` and `sortOrder`")
fmt.Fprintln(w, " sortBy: path, title, date, filetime, meta")
fmt.Fprintln(w, " sortOrder: desc, descending")
fmt.Fprintln(w, "Server Flags:")
PrintFlagSet(w, fs)
case "help", "":
PrintHelp(w)
fmt.Fprintln(w, "\nHelp Topics:")
curLineLen := 2
fmt.Fprint(w, " ")
for i, topic := range helpTopics {
if curLineLen+len(topic) < 80 {
curLineLen += len(topic)
fmt.Fprint(w, topic)
} else {
fmt.Fprintln(w, topic)
fmt.Fprint(w, " ")
curLineLen = 2
}
if i == len(helpTopics)-1 {
fmt.Fprintln(w)
} else if curLineLen != 2 {
fmt.Fprint(w, ", ")
curLineLen += 3
}
}
PrintGlobalFlags(w)
default:
fmt.Fprintln(os.Stderr, "Unrecognized topic: ", topic)
if suggestion, ok := util.Nearest(topic, helpTopics, util.LevensteinDistance, 3); ok {
fmt.Fprintf(w, "Did you mean %s?\n", suggestion)
}
fmt.Fprintln(w, "See `atlas help`")
}
}
|