Curiosity
A four-stage pipeline diagram illustrating Signals, Fusion, Rules, and Results with icons and connections.

The recommendation engine

A single lookup answers "what is like this?". For "what should we recommend?", IQuery.ToSimilarity(...) combines multiple signals into one ranking with fusion, then clips or reweights it with rules.


var result = await Graph.Query()
    .StartAt(seedUID)                          // the subject signals see in ctx.Subjects
    .ToSimilarity(o => o.MaxCandidates(200))

    // 1. Text similarity over product names (embedding signal).
    .AddSignal("SimilarName", s => s
        .Weight(1.0f)
        .FromAsync(async ctx =>
            (await ctx.Graph.Query().StartAtSimilarTextAsync(
                seedName, count: 100, nodeTypes: new[] { N.Product.Type },
                indexUID: productNameIndex, applyCutoff: false))
            .Except(ctx.Subjects)              // never recommend the seed itself
            .AsUIDEnumerable()))

    // 2. Same manufacturer (graph traversal signal).
    .AddSignal("SameManufacturer", s => s
        .Weight(0.7f)
        .From(ctx => ctx.Graph.Query().StartAt(ctx.Subjects)
            .Out(N.Manufacturer.Type, E.ManufacturedBy)
            .Out(N.Product.Type, E.Manufactures)
            .Except(ctx.Graph.Query().StartAt(ctx.Subjects))
            .AsUIDEnumerable()))

    // Combine the rankings.
    .Fuse(f => f.UsingReciprocalRankFusion())
    .ExecuteAsync(ct);

Piece What it does
Signal A candidate source returning ranked UIDs. Text, graph, external — as many as you need
Weight(...) Scales a signal's contribution to the fused rank
Fusion Merges the rankings. ReciprocalRankFusion (default) is robust across different score scales and rewards consensus; MaxScore lets one signal dominate

With one signal, the scenario uses its scores directly. With two or more, you must call Fuse(...) or it falls back to MaxScore (rarely what you want).

Similarity engine — anatomy of a scenario