aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/query.go
blob: 649b9eaf2f5caea50c0bde646869c331b0220932 (plain) (blame)
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
package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/jpappel/atlas/pkg/data"
	"github.com/jpappel/atlas/pkg/index"
	"github.com/jpappel/atlas/pkg/query"
)

type QueryFlags struct {
	Outputer          query.Outputer
	DocumentSeparator string
	ListSeparator     string
	CustomFormat      string
	OptimizationLevel int
}

func setupQueryFlags(args []string, fs *flag.FlagSet, flags *QueryFlags, dateFormat string) {
	// NOTE: providing `-outFormat` before `-outCustomFormat` might ignore user specified format
	fs.Func("outFormat", "output `format` for queries (default, json, pathonly, custom)",
		func(arg string) error {
			switch arg {
			case "default":
				flags.Outputer = query.DefaultOutput{}
				return nil
			case "json":
				flags.Outputer = query.JsonOutput{}
				return nil
			case "pathonly":
				flags.Outputer, _ = query.NewCustomOutput("%p", dateFormat, "\n", "")
				return nil
			case "custom":
				var err error
				flags.Outputer, err = query.NewCustomOutput(flags.CustomFormat, dateFormat, flags.DocumentSeparator, flags.ListSeparator)
				return err
			}
			return fmt.Errorf("Unrecognized output format: %s", arg)
		})
	fs.StringVar(&flags.CustomFormat, "outCustomFormat", query.DefaultOutputFormat, "format string for --outFormat custom, see Output Format for more details")
	fs.IntVar(&flags.OptimizationLevel, "optLevel", 0, "optimization `level` for queries, 0 is automatic, <0 to disable")
	fs.StringVar(&flags.DocumentSeparator, "docSeparator", "\n", "separator for custom output format")
	fs.StringVar(&flags.ListSeparator, "listSeparator", ", ", "separator for list fields")

	fs.Usage = func() {
		f := fs.Output()
		fmt.Fprintf(f, "Usage of %s %s\n", os.Args[0], fs.Name())
		fmt.Fprintf(f, "  %s [global-flags] %s [query-flags]\n\n",
			os.Args[0], fs.Name())
		fmt.Fprintln(f, "Query Flags:")
		fs.PrintDefaults()
		fmt.Fprintln(f, "\nOutput Format:")
		help := `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
       %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(f, help)
		fmt.Fprintln(f, "Global Flags:")
		flag.PrintDefaults()
	}

	fs.Parse(args)
}

func runQuery(gFlags GlobalFlags, qFlags QueryFlags, db *data.Query, searchQuery string) byte {
	tokens := query.Lex(searchQuery)
	clause, err := query.Parse(tokens)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failed to parse query: ", err)
		return 1
	}

	o := query.NewOptimizer(clause, gFlags.NumWorkers)
	o.Optimize(qFlags.OptimizationLevel)

	artifact, err := clause.Compile()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failed to compile query: ", err)
		return 1
	}

	results, err := db.Execute(artifact)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failed to execute query: ", err)
		return 1
	}

	if len(results) == 0 {
		fmt.Println("No results.")
		return 0
	}

	outputableResults := make([]*index.Document, 0, len(results))
	for _, v := range results {
		outputableResults = append(outputableResults, v)
	}

	_, err = qFlags.Outputer.OutputTo(os.Stdout, outputableResults)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error while outputting results: ", err)
		return 1
	}
	return 0
}