Hi aj-hoffer,
I hit the same wall, so I actually tried this out with Dancing Goat template before answering.
First thing I tried was wrapping WhereContainsTags in WhereNot, since that's the negation method used elsewhere in Xperience's WHERE API:
subqueryParameters.Where(where =>
where.WhereNot(new WhereCondition().WhereContainsTags(nameof(ProductCoffee.CoffeeProcessing), excludedTagGuids)));
That doesn't compile:
'WhereParameters' does not contain a definition for 'WhereNot'...
The type or namespace name 'WhereCondition' could not be found...
Turns out WhereNot/WhereCondition belong to the ObjectQuery/DataEngine WHERE API, not the WhereParameters type you get inside a content item query's .Where(...) — they look similar (share names like WhereEquals, WhereLike) but they're different builder types under the hood, and WhereNot just isn't one of the shared members.
What actually works is a two-step query: pull the IDs of items that do match the tag, then exclude them from a second query with WhereNotIn:
var matchingBuilder = new ContentItemQueryBuilder()
.ForContentType(ProductCoffee.CONTENT_TYPE_NAME, config => config
.Where(where => where.WhereContainsTags(nameof(ProductCoffee.CoffeeProcessing), excludedTagGuids))
.Columns("ContentItemID"))
.InLanguage("en");
var matchingIds = await executor.GetResult(matchingBuilder,
c => c.GetValue<int>("ContentItemID"), cancellationToken: cancellationToken);
var resultBuilder = new ContentItemQueryBuilder()
.ForContentType(ProductCoffee.CONTENT_TYPE_NAME, config => config
.Where(where => where.WhereNotIn("ContentItemID", matchingIds.ToArray()))
.Columns("ContentItemName"))
.InLanguage("en");
var itemsWithoutTag = await executor.GetResult(resultBuilder,
c => c.GetValue<string>("ContentItemName"), cancellationToken: cancellationToken);
I ran this against the Dancing Goat sample data, excluding the "Decaf" tag on ProductCoffee.CoffeeProcessing. Output:
excludedTag: "Decaf"
matchingIds: [52, 87]
itemsWithoutTag:
BoliviaFincaIllimani-9nut1evb
BrazilNaturalBarraGrande-g30f5chq
ColombiaCarlosImbachi-6pwcdcic
ElSalvadorFincaSanJose-ssa7ezz7
GuatemalaFincaElInjerto-k3bwkxk3
KenyaGakuyuniAA-j2ujcs2m
PanamaLosLajonesHoney-oqi18lde
2 tagged + 7 untagged = 9, which matches the total ProductCoffee item count in Content Hub exactly - so items with no tag at all in that field are correctly included too, not silently dropped (that was the edge case I was most worried about).
Two queries instead of one, but it's the only version that actually compiles and returns correct results.
References: