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
#!/bin/bash

# mongo_ui.sh - MongoDB Terminal Explorer
# ----------------------------------------
# View MongoDBs locally from a Linux shell window in an easy-to-use terminal UI.
# Only tested in Ubuntu, because that's all I have.
#
# This script connects to a MongoDB instance and allows you to:
#   - Select a database and collection using fzf
#   - View a sample document
#   - View inferred schema types
#   - View the top 10 documents in a collection
#
# 🔒 This tool is strictly read-only. It does not write, update, or delete any data.
#
# ✅ Requirements:
#     - fzf:        sudo apt install fzf
#     - jq:         sudo apt install jq
#     - mongosh:    https://www.mongodb.com/try/download/shell
#
# 🪪 License: MIT
# You are free to use, modify, and share this script however you'd like.
# There is absolutely NO WARRANTY — use at your own risk.
# War is bad. Cookies are good! Except the tracking kind.

MONGO_URI="mongodb://localhost:27017"

# Check deps
command -v fzf >/dev/null || { echo "fzf not found. Try: sudo apt install fzf"; exit 1; }
command -v jq >/dev/null || { echo "jq not found. Try: sudo apt install jq"; exit 1; }
command -v mongosh >/dev/null || { echo "mongosh not found. See: https://www.mongodb.com/try/download/shell"; exit 1; }

# Graceful shutdown on Ctrl-C, Ctrl-\, or kill
trap "echo -e '\n[INFO] Exiting...'; exit 0" SIGINT SIGQUIT SIGTERM

# Fetch and display database list.  Either run this code
read -n 1 -s -r -p "Press any key to fetch databases..."; echo
RAW_DBS=$(mongosh "$MONGO_URI" --quiet --eval "JSON.stringify(db.adminCommand('listDatabases'))")
DB=$(echo "$RAW_DBS" | jq -r '.databases[].name' | fzf --prompt="Select Database: ")
[ -z "$DB" ] && exit
# -- OR --
#DB="<HARDCODE YOUR DB HERE>"
echo "You selected: $DB"

while true; do
  read -n 1 -s -r -p "Press any key to fetch collections from $DB..."; echo
  RAW_COLLS=$(mongosh "$MONGO_URI/$DB" --quiet --eval "JSON.stringify(db.getCollectionNames())")
  COLLECTION=$(echo "$RAW_COLLS" | jq -r '.[]' | fzf --prompt="Select Collection (or ESC to quit): ")
  [ -z "$COLLECTION" ] && exit

  echo "You selected collection: $COLLECTION"

  while true; do
    ACTION=$(printf "View Sample Document\nView Schema (Quick)\nView Top 10 Rows\nBack\nExit" | fzf --prompt="Choose Action: ")
    [ "$ACTION" == "Back" ] && break
    [ "$ACTION" == "Exit" ] && exit

    if [ "$ACTION" == "View Sample Document" ]; then
      read -n 1 -s -r -p "Press any key to fetch a sample document..."; echo
      RAW_DOC=$(mongosh "$MONGO_URI/$DB" --quiet --eval "JSON.stringify(db.getCollection('$COLLECTION').findOne())")
      echo "$RAW_DOC" | jq .
      echo
      read -n 1 -s -r -p "Done. Press any key to return to the previous menu..."

    elif [ "$ACTION" == "View Schema (Quick)" ]; then
      echo "[INFO] Sampling data..."
      RAW=$(mongosh --quiet --eval "JSON.stringify(db.getCollection('$COLLECTION').find().limit(100).toArray())" "$MONGO_URI/$DB")

      if [[ -z "$RAW" || "$RAW" == "undefined" || "$RAW" == "[]" ]]; then
        echo "[WARN] No documents found or error fetching data."
      else
        echo "$RAW" | jq '
          map(to_entries)
          | flatten
          | group_by(.key)
          | map({ key: .[0].key, types: (map(.value | type) | unique | join("/")) })
          | map({ (.key): .types })
          | add
        '
      fi
      echo
      read -n 1 -s -r -p "Done. Press any key to return to the previous menu..."

    elif [ "$ACTION" == "View Top 10 Rows" ]; then
      RAW_TOP=$(mongosh "$MONGO_URI/$DB" --quiet --eval "JSON.stringify(db.getCollection('$COLLECTION').find().limit(10).toArray())")
      echo "$RAW_TOP" | jq -c '.[]' | while read -r doc; do echo "$doc" | jq .; echo; done
      echo
      read -n 1 -s -r -p "Done. Press any key to return to the previous menu..."
    fi
  done
done