Chapter 24 · Graph Algorithms: Centrality, Community Detection, Similarity, Paths, and Topology Analytics

Node Similarity, KNN, Jaccard/Overlap/Cosine Concepts, and Candidate Generation

Use Node Similarity and KNN for AtlasMart candidate generation, manually validate Jaccard/Overlap/Cosine semantics, and separate structural association from causal recommendation claims.

Advanced240–350 minutesSimilarity/KNN · candidate generationNeo4j 2026.07.1 · Community mandatoryGDS Community 2026.07.0 · Cypher 25Algorithms · estimates · stream first · concurrency ≤4 CEJava 21/25 · GDS plugin requiredLast reviewed: September 2026

Learning outcomes

01

Distinguish structural Node Similarity from property-vector KNN candidate generation.

02

Compute Jaccard and Overlap manually on a tiny AtlasMart customer-product bipartite fixture.

03

Explain when Cosine applies to weighted neighborhoods versus KNN float-vector properties.

04

Bound candidate generation with topK/filtering and validate candidates against business rules and holdout judgments.

05

Prevent similarity scores from being presented as causal evidence or guaranteed recommendation quality.

1. AtlasMart problem: “similar customer” can mean shared neighbors or similar features

AtlasMart recommendation engineers ask for similar customers. In graph analytics, that phrase can mean two different mechanisms. Node Similarity compares overlap in outgoing neighborhoods in a bipartite graph. KNN compares configured node properties such as numeric vectors and uses approximate neighbor search machinery. They can both produce candidate pairs, but their evidence is different.

Dimension Chapter 24 reproducible assumption
Neo4j 2026.07.1 Community, disposable local/container deployment; database neo4j.
Cypher Cypher 25 for course examples; Cypher 5 differences are not needed for the GDS procedures used here.
Java Java 21/25 supported by the 2026 line; container image supplies the runtime.
Auth/TLS User neo4j, password atlasmart-course-2026; loopback Bolt without TLS only for this isolated lab.
Driver No application driver is required for mandatory GDS procedure labs; cypher-shell/Browser is sufficient.
GDS GDS Community 2026.07.0. Community includes all algorithms; maximum GDS concurrency is four CPU cores.
APOC Not required.
Graph scope Only identifiers prefixed CH24- plus named in-memory projections beginning atlas-ch24- are created.
Runtime evidence This artifact does not execute Neo4j/GDS. Exact fixture counts and hand calculations are deterministic; timings, memory estimates, and algorithm scores must be measured locally.
Verify the Chapter 24 lab baseline
// Run in the disposable atlasmart-gds lab from Chapter 23.
RETURN gds.version() AS gdsVersion;
CALL dbms.components() YIELD name, versions, edition
RETURN name, versions, edition;

// Defensive cleanup of Chapter 24 projections if rerunning.
CALL gds.graph.list()
YIELD graphName
WITH graphName WHERE graphName STARTS WITH 'atlas-ch24-'
CALL gds.graph.drop(graphName) YIELD graphName AS dropped
RETURN dropped;

2. Structural versus property-based similarity

Method Input evidence Typical metric Interpretation
Node Similarity Outgoing neighbor sets/weighted neighbor lists JACCARD, OVERLAP, COSINE Structural equivalence: two source nodes connect to similar targets.
KNN Projected scalar/list node properties COSINE, EUCLIDEAN, PEARSON, JACCARD/OVERLAP depending property type Property-space proximity; topology matters only if encoded into the properties.
gds.similarity.* functions Two supplied lists/vectors Jaccard, overlap, cosine, etc. Point calculation, not a graph-wide candidate algorithm.

Using the same word “cosine” does not make two experiments equivalent. In Node Similarity, cosine may be applied to weighted neighbor vectors; in KNN, cosine can compare a list-of-float property such as a deterministic feature vector.

3. Build a customer-view bipartite graph with a manual baseline

Create CH24 similarity fixture
UNWIND [
  ['CH24-SIM-C1','Ada'], ['CH24-SIM-C2','Ben'], ['CH24-SIM-C3','Cy']
] AS row
CREATE (:Customer:CH24SimilarityCustomer {customerId:row[0], name:row[1], chapter24:true});

UNWIND range(1,4) AS i
CREATE (:Product:CH24SimilarityProduct {
  productId:'CH24-SIM-P' + toString(i),
  name:'Similarity Product ' + toString(i),
  featureVec: CASE i
    WHEN 1 THEN [1.0,0.0,0.1]
    WHEN 2 THEN [0.9,0.1,0.1]
    WHEN 3 THEN [0.1,0.9,0.2]
    ELSE [0.0,1.0,0.3] END,
  chapter24:true
});

MATCH (c1:Customer {customerId:'CH24-SIM-C1'}),
      (c2:Customer {customerId:'CH24-SIM-C2'}),
      (c3:Customer {customerId:'CH24-SIM-C3'}),
      (p1:Product {productId:'CH24-SIM-P1'}),
      (p2:Product {productId:'CH24-SIM-P2'}),
      (p3:Product {productId:'CH24-SIM-P3'}),
      (p4:Product {productId:'CH24-SIM-P4'})
CREATE (c1)-[:VIEWED]->(p1), (c1)-[:VIEWED]->(p2), (c1)-[:VIEWED]->(p3),
       (c2)-[:VIEWED]->(p1), (c2)-[:VIEWED]->(p2),
       (c3)-[:VIEWED]->(p3), (c3)-[:VIEWED]->(p4);

RETURN 'C1/C2 Jaccard = 2/3; C1/C3 Jaccard = 1/4' AS manualBaseline;

The manual Jaccard calculation is deterministic: C1 and C2 share P1/P2, so intersection size is 2 and union size is 3. C1 and C3 share only P3 and their union is four products, so Jaccard is 1/4.

4. Node Similarity: neighborhood overlap is the data

Project the bipartite graph and run Node Similarity
CALL gds.graph.project(
  'atlas-ch24-similarity',
  ['CH24SimilarityCustomer','CH24SimilarityProduct'],
  'VIEWED'
) YIELD graphName, nodeCount, relationshipCount;

CALL gds.nodeSimilarity.stream.estimate('atlas-ch24-similarity', {
  similarityMetric:'JACCARD', topK:2, concurrency:2
}) YIELD nodeCount, relationshipCount, requiredMemory
RETURN *;

CALL gds.nodeSimilarity.stream('atlas-ch24-similarity', {
  similarityMetric:'JACCARD',
  similarityCutoff:0.0,
  topK:2,
  concurrency:2
}) YIELD node1, node2, similarity
WITH gds.util.asNode(node1) AS a, gds.util.asNode(node2) AS b, similarity
WHERE a:Customer AND b:Customer
RETURN a.customerId AS customer1, b.customerId AS customer2, similarity
ORDER BY customer1, similarity DESC;
Complexity boundary

Current Node Similarity computes pairwise structural similarity and can be expensive; topK bounds returned results/memory but does not magically remove all computation. Estimate and constrain the source graph.

5. Jaccard, Overlap, and Cosine answer different normalization questions

Metric Hand formula intuition When it can mislead
Jaccard |A∩B| / |A∪B| Penalizes extra neighbors; two sparse users may look very similar from one accidental overlap.
Overlap |A∩B| / min(|A|,|B|) Returns 1 when the smaller set is contained in the larger; can overstate similarity when activity levels differ greatly.
Cosine dot(A,B) / (||A||·||B||) Useful for non-negative weighted neighbor vectors or float features, but depends strongly on weight construction.
Compare similarity metrics on the same projection
UNWIND ['JACCARD','OVERLAP','COSINE'] AS metric
CALL gds.nodeSimilarity.stats('atlas-ch24-similarity', {
  similarityMetric:metric,
  topK:2,
  concurrency:2
})
YIELD nodesCompared, similarityDistribution
RETURN metric, nodesCompared, similarityDistribution;

6. KNN: candidate generation from projected properties

KNN is not a replacement for vector indexes from Chapter 22; here it is a GDS algorithm over projected properties. The first-neighbor sampling and iterative candidate refinement make KNN approximate. Treat recall against a small exact baseline as evidence, not the returned score alone.

Project product feature vectors and run KNN
CALL gds.graph.project(
  'atlas-ch24-knn',
  {CH24SimilarityProduct:{properties:['featureVec']}},
  '*'
) YIELD graphName, nodeCount, relationshipCount;

CALL gds.knn.stream('atlas-ch24-knn', {
  nodeProperties:[{featureVec:'COSINE'}],
  topK:2,
  randomSeed:24,
  concurrency:2
})
YIELD node1, node2, similarity
RETURN gds.util.asNode(node1).productId AS product1,
       gds.util.asNode(node2).productId AS product2,
       similarity
ORDER BY product1, similarity DESC;

Only compare results for the intended CH24-SIM- products. If other Product nodes are still present, the projection is too broad for the lab; filter it or reset first.

7. Deliberately wrong: similarity means “users will buy the same thing”

Structural similarity is association in the selected graph. It can be confounded by popularity, sparse activity, campaign exposure, geography, or data collection. The repair is a candidate pipeline: generate candidates, apply eligibility/business/security filters, rank with separate features, then evaluate against future or held-out outcomes. Similarity is one feature, not a causal claim.

Production judgment and bridge

Decision Evidence
Structural or feature similarity? State whether neighbor overlap or property-space distance is the actual signal.
Metric Manual fixture plus domain argument for Jaccard/Overlap/Cosine/other metric.
Candidate bounds topK/cutoffs/source filters and measured candidate volume.
Approximation For KNN, compare recall@k against an exact baseline on a small representative set.
Business effect Offline judgments + controlled online evaluation; never infer causation from graph similarity alone.
Security Ensure candidate generation does not cross tenant/data-access boundaries.

Next, the output is no longer a score or pair but a route/reachability structure. Path algorithms make weight semantics even stricter because “cost” must compose along a path.

Check your understanding

  1. What does Node Similarity compare?
  2. What is the manual Jaccard similarity of C1={P1,P2,P3} and C2={P1,P2}?
  3. Why can Overlap be 1 when Jaccard is below 1?
  4. Why is KNN approximate?
  5. What does a similarity score prove about causation?
Review the answers

1. Outgoing neighborhoods in the projected graph; it measures structural equivalence.

2. 2/3 because the intersection has 2 and the union has 3.

3. If the smaller neighbor set is completely contained in the larger, overlap divides by the smaller-set size while Jaccard divides by the union.

4. It begins with sampled neighbors and iteratively refines candidate neighborhoods rather than exhaustively comparing every pair.

5. Nothing. It is association under a defined graph/property representation and metric.

Chapter 24 cleanup/reset
// Drop all Chapter 24 in-memory projections first.
CALL gds.graph.list()
YIELD graphName
WITH graphName WHERE graphName STARTS WITH 'atlas-ch24-'
CALL gds.graph.drop(graphName) YIELD graphName AS dropped
RETURN dropped;

// Then delete only synthetic Chapter 24 persisted fixtures.
MATCH (n)
WHERE n.chapter24 = true
DETACH DELETE n;

MATCH (n) WHERE n.chapter24 IS NOT NULL REMOVE n.chapter24;
RETURN 'Chapter 24 reset complete' AS status;

Summary and next step

Node Similarity, KNN, Jaccard/Overlap/Cosine Concepts, and Candidate Generation is useful only when its assumptions and observed evidence stay attached to the decision. The examples above establish a reproducible mechanism and boundary; they do not turn one lab result into a universal production rule.

Next, continue to Pathfinding and Spanning/Reachability Algorithms: When GDS Beats Transactional Cypher. Carry forward the verified assumptions, fixture state, version/edition boundaries, and measurements from this lesson instead of treating the next topic as an isolated recipe.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.