I checked your proposed solution:
(metric_b and metric_a>=3) or metric_a<bool 3
and it worked like expected, returning the value of metric_b when metric_a is >= 3, and 1 otherwise.
It's important to note that "VECTOR1 and VECTOR2" it's not necessarily equals to "VECTOR2 and VECTOR1". Take a look at the Prometheus documentation about this:
vector1 and vector2 results in a vector consisting of the elements of vector1 for which there are elements in vector2 with exactly matching label sets. Other elements are dropped.
The results are always from the first vector of the "and" clause.
For example, the following query:

Gives a different result of the following one:

Problems with unless in a PromQL Query in a Dashboard - Dashboards - Grafana Labs Community Forums
Prometheus/PromQL/Grafana: Substraction when the right side range vector is potentially non-existent - Stack Overflow
Promql to detect null values - not absent
prometheus - Promql filter out time series with value in label same like in another metric dynamicaly - Stack Overflow
I have metrics like these :
up{client=“clientA”, job=“admin”}
up{client=“clientA”, job=“customer”}
up{client=“clientB”, job=“admin”}
up{client=“clientB”, job=“customer”}
up{client=“clientC”, job=“admin”}
up{client=“clientD”, job=“admin”}
These metrics are from different jobs. I am trying to find clients who have no customer job. The job customer doesn't scrape any target for clientC and clientD hence there is no metric at all. Absent is not working in this case as the metric was not present at all.
- The
bymodifier groups aggregate function results by labels enumerated insideby(...). - The
withoutmodifier groups aggregate function results by all the labels except those enumerated insidewithout(...).
For example, suppose process_resident_memory_bytes metric exists with job, instance and datacenter labels:
process_resident_memory_bytes{job="job1",instance="host1",datacenter="dc1"} N1
process_resident_memory_bytes{job="job1",instance="host2",datacenter="dc1"} N2
process_resident_memory_bytes{job="job1",instance="host1",datacenter="dc2"} N3
process_resident_memory_bytes{job="job2",instance="host1",datacenter="dc1"} N4
Then sum(process_resident_memory_bytes) by (datacenter) would return summary per-datacenter memory usage, while sum(process_resident_memory_bytes) without (instance) would return summary per-job per-datacenter memory usage.
All of these examples are aggregating incorrectly, as you're averaging an average. You want:
sum without (path,host) (
rate(request_duration_sum{status_code=~"2.*"}[5m])
)
/
sum without (path,host) (
rate(request_duration_count{status_code=~"2.*"}[5m])
)
Which will return the average latency per status_code plus any other remaining labels.