axiodb 20.3.2 → 20.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,597 +0,0 @@
1
- package cmd
2
-
3
- import (
4
- "encoding/json"
5
- "fmt"
6
- "os"
7
- "strings"
8
-
9
- "github.com/chzyer/readline"
10
- "github.com/nexoral/axiodb-cli/internal/config"
11
- "github.com/nexoral/axiodb-cli/pkg/commands"
12
- "github.com/nexoral/axiodb-cli/pkg/protocol"
13
- "github.com/spf13/cobra"
14
- )
15
-
16
- var replCommands = []string{
17
- "use", "show dbs", "show collections", "ping", "help", "exit", "quit", "clear",
18
- "db.createCollection(", "db..insert(", "db..insertMany(",
19
- "db..find(", "db..findOne(", "db..updateOne(", "db..updateMany(",
20
- "db..deleteOne(", "db..deleteMany(", "db..aggregate(",
21
- "db..countDocuments()", "db..createIndex(", "db..dropIndex(",
22
- "db..getIndexes()", "db..get(", "db..update(", "db..delete(",
23
- }
24
-
25
- var connectCmd = &cobra.Command{
26
- Use: "connect",
27
- Short: "Open interactive REPL session",
28
- RunE: func(cmd *cobra.Command, args []string) error {
29
- cfg, err := config.FromFlags(cmd)
30
- if err != nil {
31
- return err
32
- }
33
-
34
- client, err := cfg.ConnectAndAuth()
35
- if err != nil {
36
- return err
37
- }
38
- defer client.Disconnect()
39
-
40
- rl, err := readline.NewEx(&readline.Config{
41
- Prompt: "axiodb> ",
42
- HistoryFile: "/tmp/axiodb_history",
43
- InterruptPrompt: "^C",
44
- EOFPrompt: "exit",
45
- AutoComplete: readline.NewPrefixCompleter(completerItems...),
46
- })
47
- if err != nil {
48
- return fmt.Errorf("init readline: %w", err)
49
- }
50
- defer rl.Close()
51
-
52
- var currentDB, currentColl string
53
-
54
- fmt.Println("AxioDB Shell. Type 'help' for commands, 'exit' to quit.")
55
-
56
- for {
57
- rl.SetPrompt(buildPrompt(currentDB, currentColl))
58
- line, err := rl.Readline()
59
- if err != nil {
60
- break
61
- }
62
-
63
- input := strings.TrimSpace(line)
64
- if input == "" {
65
- continue
66
- }
67
-
68
- if err := handleREPLInput(client, input, &currentDB, &currentColl); err != nil {
69
- fmt.Fprintf(os.Stderr, "Error: %v\n", err)
70
- }
71
- }
72
-
73
- return nil
74
- },
75
- }
76
-
77
- func init() {
78
- rootCmd.AddCommand(connectCmd)
79
- }
80
-
81
- func buildPrompt(db, coll string) string {
82
- if db == "" {
83
- return "axiodb> "
84
- }
85
- if coll == "" {
86
- return fmt.Sprintf("axiodb:%s> ", db)
87
- }
88
- return fmt.Sprintf("axiodb:%s:%s> ", db, coll)
89
- }
90
-
91
- var completerItems = []readline.PrefixCompleterInterface{
92
- readline.PcItem("use"),
93
- readline.PcItem("show",
94
- readline.PcItem("dbs"),
95
- readline.PcItem("collections"),
96
- ),
97
- readline.PcItem("ping"),
98
- readline.PcItem("help"),
99
- readline.PcItem("exit"),
100
- readline.PcItem("quit"),
101
- readline.PcItem("clear"),
102
- readline.PcItem("db.",
103
- readline.PcItem("createCollection("),
104
- ),
105
- }
106
-
107
- func handleREPLInput(client *protocol.Client, input string, db, coll *string) error {
108
- switch {
109
- case input == "exit" || input == "quit":
110
- fmt.Println("Bye!")
111
- os.Exit(0)
112
-
113
- case input == "help":
114
- printREPLHelp()
115
-
116
- case input == "clear":
117
- fmt.Print("\033[H\033[2J")
118
-
119
- case strings.HasPrefix(input, "use "):
120
- target := strings.TrimSpace(strings.TrimPrefix(input, "use"))
121
- parts := strings.SplitN(target, ".", 2)
122
- *db = parts[0]
123
- if len(parts) > 1 {
124
- *coll = parts[1]
125
- } else {
126
- *coll = ""
127
- }
128
- fmt.Printf("switched to %s\n", buildPrompt(*db, *coll))
129
-
130
- case input == "show dbs":
131
- resp, err := commands.GetInstanceInfo(client)
132
- if err != nil {
133
- return err
134
- }
135
- printDatabases(resp)
136
-
137
- case input == "show collections":
138
- if *db == "" {
139
- return fmt.Errorf("no database selected. Use 'use <db>' first")
140
- }
141
- resp, err := commands.GetCollectionInfo(client, *db)
142
- if err != nil {
143
- return err
144
- }
145
- printCollections(resp)
146
-
147
- case input == "ping":
148
- if err := client.Ping(); err != nil {
149
- return err
150
- }
151
- fmt.Println("PONG")
152
-
153
- case strings.HasPrefix(input, "db."):
154
- return handleDBCommand(client, input, *db, *coll)
155
-
156
- default:
157
- return fmt.Errorf("unknown command: %s (type 'help' for available commands)", input)
158
- }
159
-
160
- return nil
161
- }
162
-
163
- func handleDBCommand(client *protocol.Client, input, db, coll string) error {
164
- if db == "" {
165
- return fmt.Errorf("no database selected. Use 'use <db>' first")
166
- }
167
-
168
- switch {
169
- case strings.HasPrefix(input, "db.createCollection("):
170
- name := extractParenArg(input, "db.createCollection")
171
- if name == "" {
172
- return fmt.Errorf("syntax: db.createCollection(<name>)")
173
- }
174
- resp, err := commands.CreateCollection(client, db, name)
175
- if err != nil {
176
- return err
177
- }
178
- printJSON(resp)
179
-
180
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".insert("):
181
- if coll == "" {
182
- return fmt.Errorf("no collection selected. Use 'use <db>.<collection>' first")
183
- }
184
- parts := strings.SplitN(input, ".insert(", 2)
185
- if len(parts) < 2 {
186
- return fmt.Errorf("syntax: db.<coll>.insert(<json>)")
187
- }
188
- jsonStr := strings.TrimSuffix(parts[1], ")")
189
- var data interface{}
190
- if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
191
- return fmt.Errorf("invalid JSON: %w", err)
192
- }
193
- resp, err := commands.InsertDocument(client, db, coll, data)
194
- if err != nil {
195
- return err
196
- }
197
- printJSON(resp)
198
-
199
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".insertMany("):
200
- if coll == "" {
201
- return fmt.Errorf("no collection selected. Use 'use <db>.<collection>' first")
202
- }
203
- parts := strings.SplitN(input, ".insertMany(", 2)
204
- if len(parts) < 2 {
205
- return fmt.Errorf("syntax: db.<coll>.insertMany(<json-array>)")
206
- }
207
- jsonStr := strings.TrimSuffix(parts[1], ")")
208
- var docs interface{}
209
- if err := json.Unmarshal([]byte(jsonStr), &docs); err != nil {
210
- return fmt.Errorf("invalid JSON: %w", err)
211
- }
212
- resp, err := commands.InsertManyDocuments(client, db, coll, docs)
213
- if err != nil {
214
- return err
215
- }
216
- printJSON(resp)
217
-
218
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".find("):
219
- if coll == "" {
220
- return fmt.Errorf("no collection selected. Use 'use <db>.<collection>' first")
221
- }
222
- parts := strings.SplitN(input, ".find(", 2)
223
- if len(parts) < 2 {
224
- return fmt.Errorf("syntax: db.<coll>.find(<query>)")
225
- }
226
- jsonStr := strings.TrimSuffix(parts[1], ")")
227
- opts := commands.QueryOptions{}
228
- if jsonStr != "" {
229
- var query interface{}
230
- if err := json.Unmarshal([]byte(jsonStr), &query); err != nil {
231
- return fmt.Errorf("invalid query JSON: %w", err)
232
- }
233
- opts.Query = query
234
- }
235
- resp, err := commands.QueryDocuments(client, db, coll, opts)
236
- if err != nil {
237
- return err
238
- }
239
- printJSON(resp)
240
-
241
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".findOne("):
242
- if coll == "" {
243
- return fmt.Errorf("no collection selected. Use 'use <db>.<collection>' first")
244
- }
245
- parts := strings.SplitN(input, ".findOne(", 2)
246
- if len(parts) < 2 {
247
- return fmt.Errorf("syntax: db.<coll>.findOne(<query>)")
248
- }
249
- jsonStr := strings.TrimSuffix(parts[1], ")")
250
- opts := commands.QueryOptions{FindOne: true}
251
- if jsonStr != "" {
252
- var query interface{}
253
- if err := json.Unmarshal([]byte(jsonStr), &query); err != nil {
254
- return fmt.Errorf("invalid query JSON: %w", err)
255
- }
256
- opts.Query = query
257
- }
258
- resp, err := commands.QueryDocuments(client, db, coll, opts)
259
- if err != nil {
260
- return err
261
- }
262
- printJSON(resp)
263
-
264
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".updateOne("):
265
- if coll == "" {
266
- return fmt.Errorf("no collection selected")
267
- }
268
- query, update, err := extractTwoJSONArgs(input, ".updateOne(")
269
- if err != nil {
270
- return err
271
- }
272
- resp, err := commands.UpdateDocumentsByQuery(client, db, coll, query, update, true)
273
- if err != nil {
274
- return err
275
- }
276
- printJSON(resp)
277
-
278
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".updateMany("):
279
- if coll == "" {
280
- return fmt.Errorf("no collection selected")
281
- }
282
- query, update, err := extractTwoJSONArgs(input, ".updateMany(")
283
- if err != nil {
284
- return err
285
- }
286
- resp, err := commands.UpdateDocumentsByQuery(client, db, coll, query, update, false)
287
- if err != nil {
288
- return err
289
- }
290
- printJSON(resp)
291
-
292
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".deleteOne("):
293
- if coll == "" {
294
- return fmt.Errorf("no collection selected")
295
- }
296
- parts := strings.SplitN(input, ".deleteOne(", 2)
297
- if len(parts) < 2 {
298
- return fmt.Errorf("syntax: db.<coll>.deleteOne(<query>)")
299
- }
300
- jsonStr := strings.TrimSuffix(parts[1], ")")
301
- var query interface{}
302
- if jsonStr != "" {
303
- if err := json.Unmarshal([]byte(jsonStr), &query); err != nil {
304
- return fmt.Errorf("invalid query JSON: %w", err)
305
- }
306
- }
307
- resp, err := commands.DeleteDocumentsByQuery(client, db, coll, query, true)
308
- if err != nil {
309
- return err
310
- }
311
- printJSON(resp)
312
-
313
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".deleteMany("):
314
- if coll == "" {
315
- return fmt.Errorf("no collection selected")
316
- }
317
- parts := strings.SplitN(input, ".deleteMany(", 2)
318
- if len(parts) < 2 {
319
- return fmt.Errorf("syntax: db.<coll>.deleteMany(<query>)")
320
- }
321
- jsonStr := strings.TrimSuffix(parts[1], ")")
322
- var query interface{}
323
- if jsonStr != "" {
324
- if err := json.Unmarshal([]byte(jsonStr), &query); err != nil {
325
- return fmt.Errorf("invalid query JSON: %w", err)
326
- }
327
- }
328
- resp, err := commands.DeleteDocumentsByQuery(client, db, coll, query, false)
329
- if err != nil {
330
- return err
331
- }
332
- printJSON(resp)
333
-
334
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".aggregate("):
335
- if coll == "" {
336
- return fmt.Errorf("no collection selected")
337
- }
338
- parts := strings.SplitN(input, ".aggregate(", 2)
339
- if len(parts) < 2 {
340
- return fmt.Errorf("syntax: db.<coll>.aggregate(<pipeline>)")
341
- }
342
- jsonStr := strings.TrimSuffix(parts[1], ")")
343
- var pipeline interface{}
344
- if err := json.Unmarshal([]byte(jsonStr), &pipeline); err != nil {
345
- return fmt.Errorf("invalid pipeline JSON: %w", err)
346
- }
347
- resp, err := commands.Aggregate(client, db, coll, pipeline)
348
- if err != nil {
349
- return err
350
- }
351
- printJSON(resp)
352
-
353
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".countDocuments()"):
354
- if coll == "" {
355
- return fmt.Errorf("no collection selected")
356
- }
357
- resp, err := commands.TotalDocuments(client, db, coll)
358
- if err != nil {
359
- return err
360
- }
361
- printJSON(resp)
362
-
363
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".createIndex("):
364
- if coll == "" {
365
- return fmt.Errorf("no collection selected")
366
- }
367
- parts := strings.SplitN(input, ".createIndex(", 2)
368
- if len(parts) < 2 {
369
- return fmt.Errorf("syntax: db.<coll>.createIndex(<fields>)")
370
- }
371
- jsonStr := strings.TrimSuffix(parts[1], ")")
372
- var fields []string
373
- if err := json.Unmarshal([]byte(jsonStr), &fields); err != nil {
374
- return fmt.Errorf("invalid fields JSON: %w", err)
375
- }
376
- resp, err := commands.CreateIndex(client, db, coll, fields)
377
- if err != nil {
378
- return err
379
- }
380
- printJSON(resp)
381
-
382
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".dropIndex("):
383
- if coll == "" {
384
- return fmt.Errorf("no collection selected")
385
- }
386
- parts := strings.SplitN(input, ".dropIndex(", 2)
387
- if len(parts) < 2 {
388
- return fmt.Errorf("syntax: db.<coll>.dropIndex(<name>)")
389
- }
390
- name := strings.TrimSuffix(parts[1], ")")
391
- name = strings.Trim(name, "\"'")
392
- resp, err := commands.DropIndex(client, db, coll, name)
393
- if err != nil {
394
- return err
395
- }
396
- printJSON(resp)
397
-
398
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".getIndexes()"):
399
- if coll == "" {
400
- return fmt.Errorf("no collection selected")
401
- }
402
- resp, err := commands.ListIndexes(client, db, coll)
403
- if err != nil {
404
- return err
405
- }
406
- printJSON(resp)
407
-
408
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".get("):
409
- if coll == "" {
410
- return fmt.Errorf("no collection selected")
411
- }
412
- parts := strings.SplitN(input, ".get(", 2)
413
- if len(parts) < 2 {
414
- return fmt.Errorf("syntax: db.<coll>.get(<id>)")
415
- }
416
- id := strings.TrimSuffix(parts[1], ")")
417
- id = strings.Trim(id, "\"'")
418
- resp, err := commands.QueryByID(client, db, coll, id)
419
- if err != nil {
420
- return err
421
- }
422
- printJSON(resp)
423
-
424
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".update("):
425
- if coll == "" {
426
- return fmt.Errorf("no collection selected")
427
- }
428
- parts := strings.SplitN(input, ".update(", 2)
429
- if len(parts) < 2 {
430
- return fmt.Errorf("syntax: db.<coll>.update(<id>, <json>)")
431
- }
432
- args := strings.TrimSuffix(parts[1], ")")
433
- commaIdx := strings.Index(args, ",")
434
- if commaIdx < 0 {
435
- return fmt.Errorf("syntax: db.<coll>.update(<id>, <json>)")
436
- }
437
- id := strings.TrimSpace(args[:commaIdx])
438
- id = strings.Trim(id, "\"'")
439
- jsonStr := strings.TrimSpace(args[commaIdx+1:])
440
- var updateData interface{}
441
- if err := json.Unmarshal([]byte(jsonStr), &updateData); err != nil {
442
- return fmt.Errorf("invalid update JSON: %w", err)
443
- }
444
- resp, err := commands.UpdateDocumentByID(client, db, coll, id, updateData)
445
- if err != nil {
446
- return err
447
- }
448
- printJSON(resp)
449
-
450
- case strings.HasPrefix(input, "db.") && strings.Contains(input, ".delete("):
451
- if coll == "" {
452
- return fmt.Errorf("no collection selected")
453
- }
454
- parts := strings.SplitN(input, ".delete(", 2)
455
- if len(parts) < 2 {
456
- return fmt.Errorf("syntax: db.<coll>.delete(<id>)")
457
- }
458
- id := strings.TrimSuffix(parts[1], ")")
459
- id = strings.Trim(id, "\"'")
460
- resp, err := commands.DeleteDocumentByID(client, db, coll, id)
461
- if err != nil {
462
- return err
463
- }
464
- printJSON(resp)
465
-
466
- default:
467
- return fmt.Errorf("unknown db command: %s", input)
468
- }
469
-
470
- return nil
471
- }
472
-
473
- func extractParenArg(input, prefix string) string {
474
- fullPrefix := prefix + "("
475
- idx := strings.Index(input, fullPrefix)
476
- if idx < 0 {
477
- return ""
478
- }
479
- rest := input[idx+len(fullPrefix):]
480
- endIdx := strings.Index(rest, ")")
481
- if endIdx < 0 {
482
- return ""
483
- }
484
- return strings.TrimSpace(rest[:endIdx])
485
- }
486
-
487
- func extractTwoJSONArgs(input, methodSep string) (interface{}, interface{}, error) {
488
- parts := strings.SplitN(input, methodSep, 2)
489
- if len(parts) < 2 {
490
- return nil, nil, fmt.Errorf("syntax: db.<coll>%s<query>, <update>)", methodSep)
491
- }
492
- inner := strings.TrimSuffix(parts[1], ")")
493
-
494
- depth := 0
495
- commaIdx := -1
496
- for i, ch := range inner {
497
- switch ch {
498
- case '{', '[':
499
- depth++
500
- case '}', ']':
501
- depth--
502
- case ',':
503
- if depth == 0 {
504
- commaIdx = i
505
- }
506
- }
507
- }
508
- if commaIdx < 0 {
509
- return nil, nil, fmt.Errorf("expected two JSON arguments separated by comma")
510
- }
511
-
512
- var query, update interface{}
513
- if err := json.Unmarshal([]byte(strings.TrimSpace(inner[:commaIdx])), &query); err != nil {
514
- return nil, nil, fmt.Errorf("invalid query JSON: %w", err)
515
- }
516
- if err := json.Unmarshal([]byte(strings.TrimSpace(inner[commaIdx+1:])), &update); err != nil {
517
- return nil, nil, fmt.Errorf("invalid update JSON: %w", err)
518
- }
519
- return query, update, nil
520
- }
521
-
522
- func printJSON(data interface{}) {
523
- encoder := json.NewEncoder(os.Stdout)
524
- encoder.SetIndent("", " ")
525
- encoder.Encode(data)
526
- }
527
-
528
- func printDatabases(resp *protocol.Response) {
529
- data, ok := resp.Data.(map[string]interface{})
530
- if !ok {
531
- printJSON(resp)
532
- return
533
- }
534
-
535
- databases, _ := data["ListOfDatabases"].([]interface{})
536
- total, _ := data["TotalDatabases"].(string)
537
-
538
- if len(databases) == 0 {
539
- fmt.Println("No databases found")
540
- return
541
- }
542
-
543
- fmt.Printf("Databases (%s):\n", total)
544
- for _, db := range databases {
545
- fmt.Printf(" - %s\n", db)
546
- }
547
- }
548
-
549
- func printCollections(resp *protocol.Response) {
550
- data, ok := resp.Data.(map[string]interface{})
551
- if !ok {
552
- printJSON(resp)
553
- return
554
- }
555
-
556
- collections, _ := data["ListOfCollections"].([]interface{})
557
- total, _ := data["TotalCollections"].(string)
558
-
559
- if len(collections) == 0 {
560
- fmt.Println("No collections found")
561
- return
562
- }
563
-
564
- fmt.Printf("Collections (%s):\n", total)
565
- for _, coll := range collections {
566
- fmt.Printf(" - %s\n", coll)
567
- }
568
- }
569
-
570
- func printREPLHelp() {
571
- fmt.Println(`Available commands:
572
- use <db> Switch database
573
- use <db>.<collection> Switch database and collection
574
- show dbs List all databases
575
- show collections List collections in current db
576
- ping Test connection
577
- clear Clear screen
578
- db.createCollection(<name>) Create collection
579
- db.<coll>.insert(<json>) Insert document
580
- db.<coll>.insertMany(<json>) Insert multiple documents
581
- db.<coll>.find(<query>) Query documents
582
- db.<coll>.findOne(<query>) Query single document
583
- db.<coll>.updateOne(<q>, <u>) Update one document
584
- db.<coll>.updateMany(<q>, <u>) Update many documents
585
- db.<coll>.deleteOne(<query>) Delete one document
586
- db.<coll>.deleteMany(<query>) Delete many documents
587
- db.<coll>.aggregate(<pipeline>) Run aggregation
588
- db.<coll>.countDocuments() Count documents
589
- db.<coll>.createIndex(<fields>) Create index
590
- db.<coll>.dropIndex(<name>) Drop index
591
- db.<coll>.getIndexes() List indexes
592
- db.<coll>.get(<id>) Get document by ID
593
- db.<coll>.update(<id>, <json>) Update document by ID
594
- db.<coll>.delete(<id>) Delete document by ID
595
- help Show this help
596
- exit / quit Exit shell`)
597
- }