Search & Query Language
Search & Query Language
nano uses nPL (nano Pipe Language) — a powerful query language that makes searching and analyzing security logs intuitive and efficient. If you're familiar with piped query languages, you'll feel right at home with nPL.
Also available: Quick Reference — all 38 commands at a glance.
Understanding Fields
nano organizes log data into different field types, each optimized for specific search patterns. For detailed information, see Field Types & Search Performance.
Quick Overview:
- Metadata Fields (
timestamp,source_type,id) - Fastest, always present - Normalized Fields (
src_ip,user,process_name) - Fast, heavily indexed - UDM Fields (525+ extended fields) - Good performance, comprehensive coverage
- Enriched Fields (
enriched_src_country,enriched_dest_asn) - Fast, pre-computed - Prevalence Fields (
prevalence_min,prevalence_process_hash, etc.) - Ingest-time rarity scores (0-100) - Extension Fields (additional UDM fields) - Stored as dynamic JSON, searchable by name
Query Basics
Simple Search
Search for keywords across all fields:
failed loginThis searches for events containing both "failed" and "login" in any field.
Field Filters
Search specific fields using field=value syntax:
user=admin
status=401
src_ip=192.168.1.100Comparison Operators
| Operator | Description | Example |
|---|---|---|
= | Equal | status=200 |
!= | Not equal | status!=200 |
> | Greater than | bytes>1000 |
< | Less than | response_time<100 |
>= | Greater or equal | severity>=5 |
<= | Less or equal | port<=1024 |
Logical Operators
Combine conditions with AND, OR, NOT:
# AND (implicit or explicit)
user=admin status=401
user=admin AND status=401
# OR
status=401 OR status=403
# NOT
user=admin NOT status=200
# Grouping with parentheses
(status=401 OR status=403) AND user=adminWildcards
Use * for wildcard matching:
# Wildcard in field value
user=admin*
filename=*.exe
# Wildcard keyword search
*malware*Regex Matching
Use /pattern/ for regex searches:
# Regex on specific field
filename=/cmd\.exe$/
# Case-insensitive regex
user=/admin|root/i
# Complex patterns
CommandLine=/powershell.*-enc.*[A-Za-z0-9+\/=]{50,}/Advanced Search Operators
IN Lists
Check if a field matches any value in a list:
status IN (200, 201, 204)
user IN ("admin", "root", "administrator")
EventID NOT IN (4624, 4625)String Operators
| Operator | Description | Example |
|---|---|---|
LIKE | Pattern matching with % wildcard | filename LIKE "%.exe" |
NOT LIKE | Negated pattern match | path NOT LIKE "%temp%" |
CONTAINS | Substring match | message CONTAINS "error" |
STARTSWITH | Prefix match | domain STARTSWITH "evil" |
ENDSWITH | Suffix match | filename ENDSWITH ".ps1" |
# Examples
filename LIKE "%.dll"
CommandLine CONTAINS "powershell"
domain STARTSWITH "malicious"
path ENDSWITH "\\system32"Function Filters
Use functions in search filters:
# Length check
len(password) < 8
# Lowercase comparison
lower(user) = "admin"
# Time-based filtering
timestamp > now() - INTERVAL 1 HOUR
age(timestamp) < 3600Piped Commands
Transform and analyze results using piped commands with |. Results flow from left to right:
<search expression> | <command> | <command> | ...Aggregation & Statistics
- stats - Aggregate data with functions like count, sum, avg
- chart - Create aggregated visualizations (alias for stats)
- timechart - Time-based aggregations with automatic bucketing
- streamstats - Calculate running statistics per event
- eventstats - Add aggregate statistics to all events
- bin - Bucket timestamps or numeric values
Filtering & Selection
- where - Filter results based on conditions
- head - Return first N results
- tail - Return last N results
- dedup - Remove duplicate events
- top - Find most common values
- rare - Find least common values
- sample - Return random sample of events
Field Operations
- eval - Create calculated fields
- rename - Rename fields
- fields - Include or exclude specific fields
- table - Display specific fields
- rex - Extract fields using regex
- spath - Extract fields from JSON/XML
- fillnull - Replace null values
- mvexpand - Expand multi-value fields
Ordering & Formatting
Enrichment & Analysis
- lookup - Enrich with lookup table data
- inputlookup - Fetch data from external URLs for enrichment
- prevalence - Filter or enrich based on artifact prevalence
- resolve_identity - Resolve IP addresses to hostnames/users from EDR data
- risk - Assign risk scores for risk-based alerting
- anomaly - Detect statistical outliers
- asset - Asset investigation view with identity resolution
- cloud - Cloud investigation view with faceted summaries
Pattern Detection
- transaction - Group related events
- sequence - Detect ordered event patterns
- funnel - Analyze conversion through sequential steps
- tree - Visualize hierarchical relationships (process trees, web flows)
Subsearch Operations
- append - Append subsearch results
- join - Join with subsearch on common fields
- format - Format subsearch results as string
- return - Return field values from subsearch
Prevalence Filtering
nano provides two ways to filter by prevalence: ingest-time fields (recommended) and query-time enrichment.
Ingest-Time Prevalence Fields (Recommended)
Prevalence scores are computed at ingest time and stored directly in the logs. This is the fastest approach:
# Filter events with rare process hashes (score 0-25)
sourcetype = "sysmon" AND prevalence_process_hash <= 25
# Find events with any rare artifact
prevalence_min <= 10
# Combine with other filters
dest_ip != "" AND prevalence_dest_ip <= 15 | table timestamp, dest_ip, prevalence_dest_ipIngest-time prevalence fields:
| Field | Description |
|---|---|
prevalence_file_hash | Score (0-100) for file_hash. Lower = rarer. |
prevalence_process_hash | Score (0-100) for process_hash. Lower = rarer. |
prevalence_dest_domain | Score (0-100) for dest_host. Lower = rarer. |
prevalence_dest_ip | Score (0-100) for dest_ip (public IPs only). |
prevalence_min | Minimum of all scores (for filtering any rare artifact). |
Score interpretation: 0-10 = Very rare, 11-25 = Rare, 26-50 = Uncommon, 51-100 = Common, 255 = N/A
Prevalence Slider
The search interface includes a Prevalence Filter slider next to the search button:
- Drag left for rare artifacts only, right to show all
- Quick presets: Very Rare (≤10), Rare (≤25), Uncommon (≤50), All
- Automatically appends
| where prevalence_min <= Xto your query - Value is saved in URL for shareable links
Query-Time Prevalence Enrichment
For dynamic prevalence lookups (slower but always current), use the prevalence command:
# Filter rare hashes (seen on < 5 hosts)
* | prevalence hash_prevalence < 5 window=24h
# Filter rare domains
* | prevalence domain_prevalence < 3 window=7dTime Ranges
Time Picker
Use the UI time picker to set the search time range:
- Last 15 minutes
- Last hour
- Last 24 hours
- Last 7 days
- Last 30 days
- Custom range
Time Filters in Query
Filter by time in your query:
# Relative time
timestamp > now() - INTERVAL 1 HOUR
timestamp > date_sub(now(), INTERVAL 24 HOURS)
# Absolute time
timestamp > "2025-01-01 00:00:00"
timestamp BETWEEN "2025-01-01" AND "2025-01-31"
# Age-based
age(timestamp) < 3600 # Less than 1 hour oldQuery Examples
Security Use Cases
# Brute force detection
action=login status=failure
| bin span=5m
| stats count() as attempts by time_bucket, src_ip
| where attempts > 10
# Rare process execution
EventID=1
| prevalence hash_prevalence < 5 window=24h
| table timestamp, Image, CommandLine, hash_prevalence
# Suspicious PowerShell
Image=*powershell.exe CommandLine=*-enc*
| eval cmd_entropy = entropy(CommandLine)
| where cmd_entropy > 4.5
# Privilege escalation
EventID IN (4672, 4673, 4674)
| stats count() by user, PrivilegeList
| where count > 5
# Data exfiltration
bytes > 10485760 dest_port IN (80, 443)
| stats sum(bytes) as total_bytes by src_ip, dest_ip
| where total_bytes > 104857600 # 100 MBLog Analysis
# Error rate by service
level=error
| bin span=5m
| stats count() by time_bucket, service
# Slow requests
response_time > 1000
| stats avg(response_time), max(response_time), count() by endpoint
# Top talkers
* | stats sum(bytes) as total_bytes by src_ip
| sort -total_bytes
| head 10
# Failed authentication sources
status IN (401, 403)
| stats count() as failures by src_ip, user_agent
| where failures > 5Threat Hunting
# Beaconing detection
* | bin span=1m
| stats count() as connections by time_bucket, src_ip, dest_ip
| stats stdev(connections) as conn_stdev by src_ip, dest_ip
| where conn_stdev < 0.5 # Consistent timing = beaconing
# Lateral movement
EventID=4624 LogonType=3
| stats dc(dest_host) as unique_targets by src_ip
| where unique_targets > 10
# Suspicious domains
* | eval domain = extract_domain(url)
| eval domain_entropy = entropy(domain)
| where domain_entropy > 4.0
| prevalence domain_prevalence < 3
# Command and control
* | where is_public_ip(dest_ip)
| stats sum(bytes) as total, count() as connections by dest_ip
| where connections > 100 AND total < 10000 # Many small connectionsSearch Queuing
When the system is busy, your search may be queued rather than running immediately. This happens when the global or per-user concurrency limits are reached.
While queued, you'll see your position in the queue and an estimated wait time. You can cancel a queued search at any time using the cancel button.
The Active Searches panel (above the results area) shows all your current and recent searches — including queued, running, completed, and failed jobs. Click a completed search to reload its results.
Detection rules always run immediately and are never affected by search queuing.
For configuration details, see Search Concurrency.
Performance Tips
- Filter early — Apply time and field filters before piped commands
- Use specific fields —
user=adminis faster than searching all fields - Limit results — Use
headto limit large result sets - Avoid wildcards at start —
*adminis slower thanadmin* - Use stats wisely — Group by low-cardinality fields when possible
- Time-based partitioning — Always specify a time range
- Aggregate —
statsis more efficient than returning raw events - Sample for testing — Use
samplewhen developing queries
Keyboard Shortcuts
Ctrl+Enter— Execute queryCtrl+K— Open command paletteCtrl+Space— AutocompleteCtrl+/— Comment/uncommentCtrl+F— Find in results
Next Steps
- Quick Reference — All 38 commands at a glance
- Field Types & Search Performance — Understand field categories and optimization
- Detection Rules — Create threat detections
- Dashboards — Visualize your data