Search with Exclusion
Semantic search's answer to 'but not the beach one'
- index
- model
- results
Plain semantic search has a hole in it: you can't say "no." Ask Search by Text for "sunset over the ocean" and you'll get sunsets, oceans, and every beach in between, because "ocean" and "beach" are close neighbors in embedding space. Search with Exclusion closes that hole - it searches for your query and then actively filters out anything too similar to terms you list as forbidden. The README's example is the whole pitch: query "sunset over ocean", exclude "beach, sand", get ocean sunsets without beaches.
How it works
This is negative prompting, but honest about the mechanism. The node embeds your main query, retrieves candidates as usual, and then separately embeds each of your comma-separated exclusion terms. For every candidate that made the first pass, it computes the similarity to each exclusion vector; any candidate whose similarity to a negative term crosses exclusion_threshold gets dropped. Higher threshold = stricter exclusion = fewer results.
That threshold is the dial you'll actually tune. The default is 0.3, and the tooltip's "higher = stricter" phrasing is worth internalizing: at 0.3, only candidates that are quite similar to the excluded concept get removed; at 0.5, almost anything adjacent to "beach" disappears. If your exclusions are killing everything, lower it; if the stuff you wanted gone keeps leaking through, raise it.
Inputs
Required: index, model, query (what you want), exclude (comma-separated terms to filter out). Optional: top_k (default 50), exclusion_threshold (default 0.3), min_score, result_type, instruction. Output: results (SEARCH_RESULTS).
Install
Standard pack install - ComfyUI Manager (search "Semantic-Search") or git clone https://github.com/EricRollei/Semantic-Search into custom_nodes, install the requirements.txt deps, restart, nodes under Eric/SemanticSearch.
Where people get burned
- Exclusion terms that are too broad. Exclude
"water"and you'll lose most ocean shots too, because water is inside the thing you wanted. Exclusions work best as narrow concepts that are adjacent to, but distinct from, your target -"beach, sand, umbrellas"rather than"water". - The tradeoff is silent. This filters on embedding similarity, which is fuzzy. You'll occasionally lose a keeper that happens to sit near the exclusion space, and you'll never know. If precision matters, check the full result set once with the filter off and compare.
- Stacking with other search nodes. Search with Exclusion is its own full search - it doesn't take a
SEARCH_RESULTSfrom another node. If you want to exclude terms after a search (say, after a rerank), that's Combine Results + Filter by Score territory instead.
It's the node that makes semantic search feel like a real query language instead of a magic one-liner. Not every library needs it - but the moment you start hunting in a mixed archive, "find X but not Y" is a question you'll keep asking, and this is the pack's answer.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| index | SEMANTIC_INDEX | — | |
| model | EMBEDDING_MODEL | — | |
| query | STRING | Main search query (what you want to find) | |
| exclude | STRING | Terms to exclude, comma-separated (e.g., 'beach, sand, water') | |
| top_kopt | INT | 501–500 | — |
| exclusion_thresholdopt | FLOAT | 0.300–1 | Similarity threshold for exclusion. Higher = stricter exclusion (fewer results). |
| min_scoreopt | FLOAT | 0.000–1 | — |
| result_typeopt | COMBO | all | Filter results by media type: all, images, videos, documents, or media (images+videos) |
| instructionopt | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| results | SEARCH_RESULTS | — |