<!-- CANARY: REQ=REQ-GQL-016; FEATURE="MultiIndexOptimizer"; ASPECT=IndexTypeSelection; STATUS=TESTED; OWNER=engine; UPDATED=2025-10-03 --> <h2 id="index-optimization-tutorial" class="position-relative d-flex align-items-center group"> <span>Index Optimization Tutorial</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="index-optimization-tutorial" aria-haspopup="dialog" aria-label="Share link: Index Optimization Tutorial"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h2><div id="headingShareModal" class="heading-share-modal" role="dialog" aria-modal="true" aria-labelledby="headingShareTitle" hidden> <div class="hsm-dialog" role="document"> <div class="hsm-header"> <h2 id="headingShareTitle" class="h6 mb-0 fw-bold">Share this section</h2> <button type="button" class="hsm-close" aria-label="Close"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="hsm-body"> <label for="headingShareInput" class="form-label small text-muted mb-1 text-uppercase fw-bold" style="font-size: 0.7rem; letter-spacing: 0.5px;">Permalink</label> <div class="input-group mb-4 hsm-url-group"> <input id="headingShareInput" type="text" class="form-control font-monospace" readonly aria-readonly="true" style="font-size: 0.85rem;" /> <button class="btn btn-primary hsm-copy" type="button" aria-label="Copy" title="Copy"> <i class="fa-duotone fa-clipboard" aria-hidden="true"></i> </button> </div> <div class="small fw-bold mb-2 text-muted text-uppercase" style="font-size: 0.7rem; letter-spacing: 0.5px;">Share via</div> <div class="hsm-share-grid"> <a id="share-twitter" class="btn btn-outline-secondary w-100" target="_blank" rel="noopener noreferrer"> <i class="fa-brands fa-twitter me-2"></i>Twitter </a> <a id="share-linkedin" class="btn btn-outline-secondary w-100" target="_blank" rel="noopener noreferrer"> <i class="fa-brands fa-linkedin me-2"></i>LinkedIn </a> <a id="share-facebook" class="btn btn-outline-secondary w-100" target="_blank" rel="noopener noreferrer"> <i class="fa-brands fa-facebook me-2"></i>Facebook </a> </div> </div> </div> </div> <style> .heading-share-modal { position: fixed; inset: 0; display: flex; justify-content: center; align-items: center; background: rgba(0, 0, 0, 0.6); z-index: 1050; padding: 1rem; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); } .heading-share-modal[hidden] { display: none !important; } .hsm-dialog { max-width: 420px; width: 100%; background: var(--bs-body-bg, #fff); color: var(--bs-body-color, #212529); border: 1px solid var(--bs-border-color, rgba(0,0,0,0.1)); border-radius: 1rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); overflow: hidden; animation: hsm-fade-in 0.2s ease-out; } @keyframes hsm-fade-in { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } [data-bs-theme="dark"] .hsm-dialog { background: #1e293b; border-color: rgba(255,255,255,0.1); color: #f8f9fa; } .hsm-header { display: flex; justify-content: space-between; align-items: center; padding: 1rem 1.5rem; border-bottom: 1px solid var(--bs-border-color, rgba(0,0,0,0.1)); background: rgba(0,0,0,0.02); } [data-bs-theme="dark"] .hsm-header { background: rgba(255,255,255,0.02); border-color: rgba(255,255,255,0.1); } .hsm-close { background: transparent; border: none; color: inherit; opacity: 0.5; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 1.2rem; line-height: 1; transition: opacity 0.2s; } .hsm-close:hover { opacity: 1; } .hsm-body { padding: 1.5rem; } .hsm-url-group { display: flex !important; align-items: stretch; } .hsm-url-group .form-control { flex: 1; min-width: 0; margin: 0; background: var(--bs-secondary-bg, #f8f9fa); border-color: var(--bs-border-color, #dee2e6); border-top-right-radius: 0; border-bottom-right-radius: 0; height: 42px; } .hsm-url-group .btn { flex: 0 0 auto; margin: 0; margin-left: -1px; border-top-left-radius: 0; border-bottom-left-radius: 0; height: 42px; display: flex; align-items: center; justify-content: center; padding: 0 1.25rem; z-index: 2; } [data-bs-theme="dark"] .hsm-url-group .form-control { background: #0f172a; border-color: #334155; color: #e2e8f0; } .hsm-share-grid { display: flex; flex-direction: column; gap: 0.5rem; } .hsm-share-grid .btn { display: flex; align-items: center; justify-content: center; font-size: 0.9rem; padding: 0.6rem; border-color: var(--bs-border-color); width: 100%; } [data-bs-theme="dark"] .hsm-share-grid .btn { color: #e2e8f0; border-color: #475569; } [data-bs-theme="dark"] .hsm-share-grid .btn:hover { background: #334155; border-color: #cbd5e1; } </style> <script> (function(){ const modal = document.getElementById('headingShareModal'); if(!modal) return; const input = modal.querySelector('#headingShareInput'); const copyBtn = modal.querySelector('.hsm-copy'); const twitter = modal.querySelector('#share-twitter'); const linkedin = modal.querySelector('#share-linkedin'); const facebook = modal.querySelector('#share-facebook'); const closeBtn = modal.querySelector('.hsm-close'); let lastFocus=null; let trapBound=false; function buildUrl(id){ return window.location.origin + window.location.pathname + '#' + id; } function isOpen(){ return !modal.hasAttribute('hidden'); } function hydrate(id){ const url=buildUrl(id); input.value=url; const enc=encodeURIComponent(url); const text=encodeURIComponent(document.title); if(twitter) twitter.href=`https://twitter.com/intent/tweet?url=${enc}&text=${text}`; if(linkedin) linkedin.href=`https://www.linkedin.com/sharing/share-offsite/?url=${enc}`; if(facebook) facebook.href=`https://www.facebook.com/sharer/sharer.php?u=${enc}`; } function openModal(id){ lastFocus=document.activeElement; hydrate(id); if(!isOpen()){ modal.removeAttribute('hidden'); } requestAnimationFrame(()=>{ input.focus(); }); trapFocus(); } function closeModal(){ if(!isOpen()) return; modal.setAttribute('hidden',''); if(lastFocus && typeof lastFocus.focus==='function') lastFocus.focus(); } function copyCurrent(){ try{ navigator.clipboard.writeText(input.value).then(()=>feedback(true),()=>fallback()); } catch(e){ fallback(); } } function fallback(){ input.select(); try{ document.execCommand('copy'); feedback(true);}catch(e){ feedback(false);} } function feedback(ok){ if(!copyBtn) return; const icon=copyBtn.querySelector('i'); if(!icon) return; const prev=copyBtn.getAttribute('data-prev')||icon.className; if(!copyBtn.getAttribute('data-prev')) copyBtn.setAttribute('data-prev',prev); icon.className= ok ? 'fa-duotone fa-clipboard-check':'fa-duotone fa-circle-exclamation'; setTimeout(()=>{ icon.className=prev; },1800); } function handleShareClick(e){ e.preventDefault(); const btn=e.currentTarget; const id=btn.getAttribute('data-share-target'); if(id) openModal(id); } function bindShareButtons(){ document.querySelectorAll('.h-share').forEach(btn=>{ if(!btn.dataset.hShareBound){ btn.addEventListener('click', handleShareClick); btn.dataset.hShareBound='1'; } }); } bindShareButtons(); if(document.readyState==='loading'){ document.addEventListener('DOMContentLoaded', bindShareButtons); } else { requestAnimationFrame(bindShareButtons); } document.addEventListener('click', function(e){ const shareBtn=e.target.closest && e.target.closest('.h-share'); if(shareBtn && !shareBtn.dataset.hShareBound){ handleShareClick.call(shareBtn, e); } }, true); document.addEventListener('click', e=>{ if(e.target===modal) closeModal(); if(e.target.closest && e.target.closest('.hsm-close')){ e.preventDefault(); closeModal(); } if(copyBtn && (e.target===copyBtn || (e.target.closest && e.target.closest('.hsm-copy')))) { e.preventDefault(); copyCurrent(); } }); document.addEventListener('keydown', e=>{ if(e.key==='Escape' && isOpen()) closeModal(); }); function trapFocus(){ if(trapBound) return; trapBound=true; modal.addEventListener('keydown', f=>{ if(f.key==='Tab' && isOpen()){ const focusable=[...modal.querySelectorAll('a[href],button,input,textarea,select,[tabindex]:not([tabindex="-1"])')].filter(el=>!el.hasAttribute('disabled')); if(!focusable.length) return; const first=focusable[0]; const last=focusable[focusable.length-1]; if(f.shiftKey && document.activeElement===first){ f.preventDefault(); last.focus(); } else if(!f.shiftKey && document.activeElement===last){ f.preventDefault(); first.focus(); } } }); } if(closeBtn) closeBtn.addEventListener('click', e=>{ e.preventDefault(); closeModal(); }); })(); </script><p>Learn how to optimize query performance in Geode through strategic index design, EXPLAIN analysis, and performance tuning. This tutorial covers B-tree, HNSW vector, spatial, and full-text indexes with practical examples.</p> <h3 id="prerequisites" class="position-relative d-flex align-items-center group"> <span>Prerequisites</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="prerequisites" aria-haspopup="dialog" aria-label="Share link: Prerequisites"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3><ul> <li>Geode server running (port 3141)</li> <li>Basic GQL knowledge</li> <li>Sample dataset loaded (we&rsquo;ll create one)</li> <li>Understanding of query patterns</li> </ul> <h3 id="learning-objectives" class="position-relative d-flex align-items-center group"> <span>Learning Objectives</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="learning-objectives" aria-haspopup="dialog" aria-label="Share link: Learning Objectives"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3><p>By completing this tutorial, you will:</p> <ol> <li>Understand different index types and their use cases</li> <li>Use EXPLAIN to analyze query execution plans</li> <li>Create and optimize B-tree indexes</li> <li>Implement HNSW vector indexes for similarity search</li> <li>Configure full-text search with BM25 ranking</li> <li>Monitor index performance and effectiveness</li> </ol> <h3 id="tutorial-dataset" class="position-relative d-flex align-items-center group"> <span>Tutorial Dataset</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="tutorial-dataset" aria-haspopup="dialog" aria-label="Share link: Tutorial Dataset"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3><p>Let&rsquo;s create a realistic e-commerce dataset:</p> <p>```gql &ndash; Create customers CREATE (c1:Customer { id: 1, name: &lsquo;Alice Johnson&rsquo;, email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;, location: point({latitude: 37.7749, longitude: -122.4194}), joined: datetime(&lsquo;2023-01-15T10:30:00&rsquo;) })</p> <p>CREATE (c2:Customer { id: 2, name: &lsquo;Bob Smith&rsquo;, email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;, location: point({latitude: 40.7128, longitude: -74.0060}), joined: datetime(&lsquo;2023-02-20T14:15:00&rsquo;) })</p> <p>CREATE (c3:Customer { id: 3, name: &lsquo;Charlie Brown&rsquo;, email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;, location: point({latitude: 34.0522, longitude: -118.2437}), joined: datetime(&lsquo;2023-03-10T09:45:00&rsquo;) })</p> <p>&ndash; Create products CREATE (p1:Product { id: 101, name: &lsquo;Wireless Headphones&rsquo;, description: &lsquo;Premium wireless headphones with noise cancellation and 30-hour battery life&rsquo;, price: 199.99, category: &lsquo;Electronics&rsquo;, stock: 150, embedding: [0.1, 0.3, 0.5, 0.2, 0.8, 0.4, 0.6, 0.7] })</p> <p>CREATE (p2:Product { id: 102, name: &lsquo;Smartphone Case&rsquo;, description: &lsquo;Durable protective case for smartphones with shock absorption&rsquo;, price: 29.99, category: &lsquo;Accessories&rsquo;, stock: 500, embedding: [0.2, 0.4, 0.1, 0.9, 0.3, 0.5, 0.2, 0.6] })</p> <p>CREATE (p3:Product { id: 103, name: &lsquo;USB-C Cable&rsquo;, description: &lsquo;Fast charging USB-C cable with data transfer support&rsquo;, price: 15.99, category: &lsquo;Accessories&rsquo;, stock: 1000, embedding: [0.3, 0.2, 0.7, 0.1, 0.4, 0.8, 0.3, 0.5] })</p> <p>&ndash; Create orders and relationships MATCH (c:Customer {id: 1}), (p:Product {id: 101}) CREATE (c)-[:PURCHASED {date: datetime(&lsquo;2023-04-01T10:00:00&rsquo;), quantity: 1}]-&gt;(p)</p> <p>MATCH (c:Customer {id: 1}), (p:Product {id: 102}) CREATE (c)-[:PURCHASED {date: datetime(&lsquo;2023-04-01T10:05:00&rsquo;), quantity: 2}]-&gt;(p)</p> <p>MATCH (c:Customer {id: 2}), (p:Product {id: 103}) CREATE (c)-[:PURCHASED {date: datetime(&lsquo;2023-04-05T15:30:00&rsquo;), quantity: 5}]-&gt;(p)</p> <p>&ndash; Create product views MATCH (c:Customer {id: 3}), (p:Product {id: 101}) CREATE (c)-[:VIEWED {timestamp: datetime(&lsquo;2023-04-10T08:20:00&rsquo;)}]-&gt;(p) ```</p> <h3 id="part-1-understanding-query-plans-with-explain" class="position-relative d-flex align-items-center group"> <span>Part 1: Understanding Query Plans with EXPLAIN</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-1-understanding-query-plans-with-explain" aria-haspopup="dialog" aria-label="Share link: Part 1: Understanding Query Plans with EXPLAIN"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-baseline-query-analysis" class="position-relative d-flex align-items-center group"> <span>Step 1: Baseline Query Analysis</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-baseline-query-analysis" aria-haspopup="dialog" aria-label="Share link: Step 1: Baseline Query Analysis"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Let&rsquo;s analyze a simple query without indexes:</p> <p>```gql EXPLAIN MATCH (c:Customer {email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;}) RETURN c ```</p> <p><strong>Expected Output</strong>: ```</p> <table> <thead> <tr> <th>plan</th> </tr> </thead> <tbody> <tr> <td>EXPLAIN</td> </tr> <tr> <td>MATCH</td> </tr> <tr> <td>NodeScan</td> </tr> <tr> <td>Filter</td> </tr> <tr> <td>RETURN</td> </tr> <tr> <td>Project</td> </tr> <tr> <td>```</td> </tr> </tbody> </table> <p><strong>Analysis</strong>:</p> <ul> <li><strong>NodeScan</strong>: Scans all Customer nodes (O(n) complexity)</li> <li><strong>Filter</strong>: Applies email predicate after scan</li> <li><strong>Problem</strong>: Inefficient for large datasets</li> </ul> <h4 id="step-2-create-b-tree-index" class="position-relative d-flex align-items-center group"> <span>Step 2: Create B-tree Index</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-create-b-tree-index" aria-haspopup="dialog" aria-label="Share link: Step 2: Create B-tree Index"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create an index on the email field:</p> <p>```gql CREATE INDEX ON :Customer(email) ```</p> <p>Now re-run the EXPLAIN:</p> <p>```gql EXPLAIN MATCH (c:Customer {email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;}) RETURN c ```</p> <p><strong>Improved Output</strong>: ```</p> <table> <thead> <tr> <th>plan</th> </tr> </thead> <tbody> <tr> <td>EXPLAIN</td> </tr> <tr> <td>MATCH</td> </tr> <tr> <td>IndexSeek</td> </tr> <tr> <td>RETURN</td> </tr> <tr> <td>Project</td> </tr> <tr> <td>```</td> </tr> </tbody> </table> <p><strong>Analysis</strong>:</p> <ul> <li><strong>IndexSeek</strong>: Uses B-tree index for O(log n) lookup</li> <li><strong>No Filter</strong>: Predicate handled by index</li> <li><strong>Benefit</strong>: Significantly faster for large datasets</li> </ul> <h4 id="step-3-profile-query-performance" class="position-relative d-flex align-items-center group"> <span>Step 3: Profile Query Performance</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-profile-query-performance" aria-haspopup="dialog" aria-label="Share link: Step 3: Profile Query Performance"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Use PROFILE to measure actual execution:</p> <p>```gql PROFILE MATCH (c:Customer {email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;}) RETURN c ```</p> <p><strong>Output</strong>: ```</p> <table> <thead> <tr> <th>metric</th> <th>value</th> </tr> </thead> <tbody> <tr> <td>rows_returned</td> <td>1</td> </tr> <tr> <td>columns</td> <td>1</td> </tr> <tr> <td>execution_time_ms</td> <td>1</td> </tr> <tr> <td>```</td> <td></td> </tr> </tbody> </table> <p><strong>Interpretation</strong>:</p> <ul> <li>Query completes in ~1ms with index</li> <li>Without index: ~50-100ms for 100k customers</li> </ul> <h3 id="part-2-b-tree-index-optimization" class="position-relative d-flex align-items-center group"> <span>Part 2: B-tree Index Optimization</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-2-b-tree-index-optimization" aria-haspopup="dialog" aria-label="Share link: Part 2: B-tree Index Optimization"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="composite-indexes" class="position-relative d-flex align-items-center group"> <span>Composite Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="composite-indexes" aria-haspopup="dialog" aria-label="Share link: Composite Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create a composite index for complex queries:</p> <p>```gql &ndash; Create composite index CREATE INDEX ON :Product(category, price)</p> <p>&ndash; Query using composite index EXPLAIN MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; AND p.price &lt; 300 RETURN p.name, p.price ORDER BY p.price ```</p> <p><strong>Plan Analysis</strong>: ```</p> <table> <thead> <tr> <th>plan</th> </tr> </thead> <tbody> <tr> <td>EXPLAIN</td> </tr> <tr> <td>MATCH</td> </tr> <tr> <td>IndexSeek</td> </tr> <tr> <td>RETURN</td> </tr> <tr> <td>Project</td> </tr> <tr> <td>Sort</td> </tr> <tr> <td>```</td> </tr> </tbody> </table> <p><strong>Benefits</strong>:</p> <ul> <li>Index handles both WHERE predicates</li> <li>ORDER BY can use index ordering</li> <li>No separate sort operation needed</li> </ul> <h4 id="index-selection-guidelines" class="position-relative d-flex align-items-center group"> <span>Index Selection Guidelines</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="index-selection-guidelines" aria-haspopup="dialog" aria-label="Share link: Index Selection Guidelines"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>When to Use B-tree Indexes</strong>:</p> <ol> <li> <p><strong>Equality Lookups</strong>: ```gql &ndash; Excellent for B-tree MATCH (c:Customer {email: $email}) RETURN c MATCH (p:Product {id: $id}) RETURN p ```</p> </li> <li> <p><strong>Range Queries</strong>: ```gql &ndash; Excellent for B-tree MATCH (p:Product) WHERE p.price &gt;= 50 AND p.price &lt;= 200 RETURN p ```</p> </li> <li> <p><strong>Prefix Matching</strong>: ```gql &ndash; Good for B-tree MATCH (c:Customer) WHERE c.name STARTS WITH &lsquo;Alice&rsquo; RETURN c ```</p> </li> <li> <p><strong>Sorting</strong>: ```gql &ndash; B-tree index provides sorted access MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; RETURN p.name ORDER BY p.price &ndash; Uses index ordering ```</p> </li> </ol> <p><strong>When NOT to Use B-tree Indexes</strong>:</p> <ol> <li> <p><strong>Low Cardinality Fields</strong>: ```gql &ndash; Don&rsquo;t index boolean or low-cardinality fields CREATE INDEX ON :Product(in_stock) &ndash; Only true/false values ```</p> </li> <li> <p><strong>Frequently Updated Fields</strong>:</p> </li> </ol> <ul> <li>Index maintenance overhead on writes</li> <li>Consider read/write ratio</li> </ul> <ol start="3"> <li><strong>Large Text Fields</strong>:</li> </ol> <ul> <li>Use full-text indexes instead</li> </ul> <h3 id="part-3-hnsw-vector-index-for-similarity-search" class="position-relative d-flex align-items-center group"> <span>Part 3: HNSW Vector Index for Similarity Search</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-3-hnsw-vector-index-for-similarity-search" aria-haspopup="dialog" aria-label="Share link: Part 3: HNSW Vector Index for Similarity Search"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-create-hnsw-index" class="position-relative d-flex align-items-center group"> <span>Step 1: Create HNSW Index</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-create-hnsw-index" aria-haspopup="dialog" aria-label="Share link: Step 1: Create HNSW Index"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create a vector index for product embeddings:</p> <p>```gql CREATE VECTOR INDEX product_embeddings ON :Product(embedding) OPTIONS { dimensions: 8, distance: &lsquo;cosine&rsquo;, ef_construction: 200, m: 16 } ```</p> <p><strong>Index Parameters</strong>:</p> <ul> <li><strong>dimensions</strong>: Vector dimensionality (8 in our example)</li> <li><strong>distance</strong>: Distance metric (cosine, euclidean, manhattan, dot_product)</li> <li><strong>ef_construction</strong>: Build-time quality (higher = better quality, slower build)</li> <li><strong>m</strong>: Max connections per node (higher = better recall, more memory)</li> </ul> <h4 id="step-2-similarity-search-query" class="position-relative d-flex align-items-center group"> <span>Step 2: Similarity Search Query</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-similarity-search-query" aria-haspopup="dialog" aria-label="Share link: Step 2: Similarity Search Query"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Find products similar to a query vector:</p> <p>```gql MATCH (p:Product) WHERE vector_similarity(p.embedding, [0.15, 0.35, 0.45, 0.25, 0.75, 0.45, 0.55, 0.65], &lsquo;cosine&rsquo;) &gt; 0.8 RETURN p.name, p.description ORDER BY vector_similarity(p.embedding, [0.15, 0.35, 0.45, 0.25, 0.75, 0.45, 0.55, 0.65], &lsquo;cosine&rsquo;) DESC LIMIT 5 ```</p> <h4 id="step-3-optimize-hnsw-parameters" class="position-relative d-flex align-items-center group"> <span>Step 3: Optimize HNSW Parameters</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-optimize-hnsw-parameters" aria-haspopup="dialog" aria-label="Share link: Step 3: Optimize HNSW Parameters"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>For High Recall (Recommendation Systems)</strong>: ```gql CREATE VECTOR INDEX product_embeddings_high_recall ON :Product(embedding) OPTIONS { dimensions: 8, distance: &lsquo;cosine&rsquo;, ef_construction: 400, &ndash; Higher for better quality m: 32, &ndash; More connections ef_search: 200 &ndash; Search-time parameter } ```</p> <p><strong>For Low Latency (Real-time Search)</strong>: ```gql CREATE VECTOR INDEX product_embeddings_fast ON :Product(embedding) OPTIONS { dimensions: 8, distance: &rsquo;euclidean&rsquo;, ef_construction: 100, &ndash; Lower for faster build m: 8, &ndash; Fewer connections ef_search: 50 &ndash; Faster search } ```</p> <p><strong>Performance Trade-offs</strong>:</p> <table> <thead> <tr> <th>Parameter</th> <th>Higher Value</th> <th>Lower Value</th> </tr> </thead> <tbody> <tr> <td>ef_construction</td> <td>Better recall, slower build</td> <td>Faster build, lower recall</td> </tr> <tr> <td>m</td> <td>Better recall, more memory</td> <td>Less memory, lower recall</td> </tr> <tr> <td>ef_search</td> <td>Better recall, slower query</td> <td>Faster query, lower recall</td> </tr> </tbody> </table> <h4 id="step-4-distance-metric-selection" class="position-relative d-flex align-items-center group"> <span>Step 4: Distance Metric Selection</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-4-distance-metric-selection" aria-haspopup="dialog" aria-label="Share link: Step 4: Distance Metric Selection"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Cosine Similarity</strong> - Recommended for normalized embeddings: ```gql &ndash; Good for text embeddings, ML features distance: &lsquo;cosine&rsquo; ```</p> <p><strong>Euclidean Distance</strong> - For absolute magnitude: ```gql &ndash; Good for spatial data, image features distance: &rsquo;euclidean&rsquo; ```</p> <p><strong>Dot Product</strong> - For unnormalized vectors: ```gql &ndash; Good for recommendation scores distance: &lsquo;dot_product&rsquo; ```</p> <p><strong>Manhattan Distance</strong> - For sparse vectors: ```gql &ndash; Good for high-dimensional sparse data distance: &lsquo;manhattan&rsquo; ```</p> <h3 id="part-4-full-text-search-with-bm25" class="position-relative d-flex align-items-center group"> <span>Part 4: Full-Text Search with BM25</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-4-full-text-search-with-bm25" aria-haspopup="dialog" aria-label="Share link: Part 4: Full-Text Search with BM25"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-create-full-text-index" class="position-relative d-flex align-items-center group"> <span>Step 1: Create Full-Text Index</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-create-full-text-index" aria-haspopup="dialog" aria-label="Share link: Step 1: Create Full-Text Index"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create a full-text index on product descriptions:</p> <p>```gql CREATE FULLTEXT INDEX product_search ON :Product(name, description) OPTIONS { analyzer: &lsquo;standard&rsquo;, k1: 1.2, b: 0.75 } ```</p> <p><strong>BM25 Parameters</strong>:</p> <ul> <li><strong>k1</strong>: Term frequency saturation (typical: 1.2-2.0)</li> <li><strong>b</strong>: Length normalization (0.0 = no normalization, 1.0 = full normalization)</li> <li><strong>analyzer</strong>: Text tokenization (standard, english, whitespace)</li> </ul> <h4 id="step-2-full-text-search-query" class="position-relative d-flex align-items-center group"> <span>Step 2: Full-Text Search Query</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-full-text-search-query" aria-haspopup="dialog" aria-label="Share link: Step 2: Full-Text Search Query"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Search for products mentioning &ldquo;wireless charging&rdquo;:</p> <p>```gql MATCH (p:Product) WHERE fulltext_search(p, &lsquo;wireless charging&rsquo;) RETURN p.name, p.description, fulltext_score(p, &lsquo;wireless charging&rsquo;) AS score ORDER BY score DESC LIMIT 10 ```</p> <h4 id="step-3-optimize-bm25-parameters" class="position-relative d-flex align-items-center group"> <span>Step 3: Optimize BM25 Parameters</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-optimize-bm25-parameters" aria-haspopup="dialog" aria-label="Share link: Step 3: Optimize BM25 Parameters"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>For Short Documents</strong> (product names): ```gql CREATE FULLTEXT INDEX product_names ON :Product(name) OPTIONS { k1: 2.0, &ndash; Higher for short documents b: 0.5 &ndash; Less length normalization } ```</p> <p><strong>For Long Documents</strong> (descriptions, reviews): ```gql CREATE FULLTEXT INDEX product_descriptions ON :Product(description) OPTIONS { k1: 1.2, &ndash; Standard value b: 0.75 &ndash; Standard length normalization } ```</p> <p><strong>For Exact Phrase Matching</strong>: ```gql MATCH (p:Product) WHERE fulltext_search(p, &lsquo;&ldquo;noise cancellation&rdquo;&rsquo;) &ndash; Exact phrase RETURN p.name, p.description ```</p> <h4 id="step-4-multi-field-search" class="position-relative d-flex align-items-center group"> <span>Step 4: Multi-field Search</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-4-multi-field-search" aria-haspopup="dialog" aria-label="Share link: Step 4: Multi-field Search"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Search across multiple fields with weights:</p> <p>```gql MATCH (p:Product) WHERE fulltext_search(p.name, &lsquo;headphones&rsquo;, {boost: 2.0}) OR fulltext_search(p.description, &lsquo;headphones&rsquo;, {boost: 1.0}) RETURN p.name, fulltext_score(p.name, &lsquo;headphones&rsquo;) * 2.0 + fulltext_score(p.description, &lsquo;headphones&rsquo;) AS total_score ORDER BY total_score DESC LIMIT 10 ```</p> <h3 id="part-5-spatial-indexes-for-geographic-queries" class="position-relative d-flex align-items-center group"> <span>Part 5: Spatial Indexes for Geographic Queries</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-5-spatial-indexes-for-geographic-queries" aria-haspopup="dialog" aria-label="Share link: Part 5: Spatial Indexes for Geographic Queries"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-create-spatial-index" class="position-relative d-flex align-items-center group"> <span>Step 1: Create Spatial Index</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-create-spatial-index" aria-haspopup="dialog" aria-label="Share link: Step 1: Create Spatial Index"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create an R-tree spatial index for customer locations:</p> <p>```gql CREATE SPATIAL INDEX customer_locations ON :Customer(location) OPTIONS { dimensions: 2, min_entries: 4, max_entries: 16 } ```</p> <h4 id="step-2-geographic-range-query" class="position-relative d-flex align-items-center group"> <span>Step 2: Geographic Range Query</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-geographic-range-query" aria-haspopup="dialog" aria-label="Share link: Step 2: Geographic Range Query"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Find customers within 50km of a point:</p> <p>```gql MATCH (c:Customer) WHERE distance(c.location, point({latitude: 37.7749, longitude: -122.4194})) &lt; 50000 &ndash; meters RETURN c.name, c.location, distance(c.location, point({latitude: 37.7749, longitude: -122.4194})) / 1000 AS distance_km ORDER BY distance_km ```</p> <h4 id="step-3-bounding-box-query" class="position-relative d-flex align-items-center group"> <span>Step 3: Bounding Box Query</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-bounding-box-query" aria-haspopup="dialog" aria-label="Share link: Step 3: Bounding Box Query"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Find customers in a geographic bounding box:</p> <p>```gql MATCH (c:Customer) WHERE c.location.latitude &gt;= 37.0 AND c.location.latitude &lt;= 38.0 AND c.location.longitude &gt;= -123.0 AND c.location.longitude &lt;= -121.0 RETURN c.name, c.location ```</p> <h3 id="part-6-index-maintenance-and-monitoring" class="position-relative d-flex align-items-center group"> <span>Part 6: Index Maintenance and Monitoring</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-6-index-maintenance-and-monitoring" aria-haspopup="dialog" aria-label="Share link: Part 6: Index Maintenance and Monitoring"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-list-all-indexes" class="position-relative d-flex align-items-center group"> <span>Step 1: List All Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-list-all-indexes" aria-haspopup="dialog" aria-label="Share link: Step 1: List All Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>```gql CALL db.indexes() YIELD name, type, properties, options RETURN name, type, properties, options ```</p> <p><strong>Output</strong>: ```</p> <table> <thead> <tr> <th>name</th> <th>type</th> <th>properties</th> <th>options</th> </tr> </thead> <tbody> <tr> <td>customer_email</td> <td>btree</td> <td>[&ldquo;email&rdquo;]</td> <td>{}</td> </tr> <tr> <td>product_category_price</td> <td>btree</td> <td>[&ldquo;category&rdquo;,&ldquo;price&rdquo;]</td> <td>{}</td> </tr> <tr> <td>product_embeddings</td> <td>hnsw</td> <td>[&ldquo;embedding&rdquo;]</td> <td>{dimensions:8,&hellip;}</td> </tr> <tr> <td>product_search</td> <td>fulltext</td> <td>[&ldquo;name&rdquo;,&ldquo;description&rdquo;]</td> <td>{k1:1.2,b:0.75}</td> </tr> <tr> <td>customer_locations</td> <td>spatial</td> <td>[&ldquo;location&rdquo;]</td> <td>{dimensions:2}</td> </tr> <tr> <td>```</td> <td></td> <td></td> <td></td> </tr> </tbody> </table> <h4 id="step-2-analyze-index-usage" class="position-relative d-flex align-items-center group"> <span>Step 2: Analyze Index Usage</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-analyze-index-usage" aria-haspopup="dialog" aria-label="Share link: Step 2: Analyze Index Usage"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Check which indexes are being used:</p> <p>```gql CALL db.index.stats() YIELD name, hits, misses, hit_rate WHERE hit_rate &lt; 0.5 &ndash; Find underutilized indexes RETURN name, hits, misses, hit_rate ORDER BY hit_rate ```</p> <h4 id="step-3-rebuild-index" class="position-relative d-flex align-items-center group"> <span>Step 3: Rebuild Index</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-rebuild-index" aria-haspopup="dialog" aria-label="Share link: Step 3: Rebuild Index"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Rebuild an index to improve performance:</p> <p>```gql &ndash; Drop and recreate index DROP INDEX customer_email</p> <p>CREATE INDEX ON :Customer(email) ```</p> <h4 id="step-4-monitor-index-size" class="position-relative d-flex align-items-center group"> <span>Step 4: Monitor Index Size</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-4-monitor-index-size" aria-haspopup="dialog" aria-label="Share link: Step 4: Monitor Index Size"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Check index storage size:</p> <p>```gql CALL db.index.size() YIELD name, size_bytes, entry_count RETURN name, size_bytes / (1024 * 1024) AS size_mb, entry_count ORDER BY size_mb DESC ```</p> <h3 id="part-7-advanced-optimization-techniques" class="position-relative d-flex align-items-center group"> <span>Part 7: Advanced Optimization Techniques</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-7-advanced-optimization-techniques" aria-haspopup="dialog" aria-label="Share link: Part 7: Advanced Optimization Techniques"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="covering-indexes" class="position-relative d-flex align-items-center group"> <span>Covering Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="covering-indexes" aria-haspopup="dialog" aria-label="Share link: Covering Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create indexes that include all queried fields:</p> <p>```gql &ndash; Create covering index CREATE INDEX ON :Product(category, name, price)</p> <p>&ndash; Query covered by index (no table lookup needed) MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; RETURN p.name, p.price &ndash; All fields in index ```</p> <h4 id="index-hints" class="position-relative d-flex align-items-center group"> <span>Index Hints</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="index-hints" aria-haspopup="dialog" aria-label="Share link: Index Hints"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Force query planner to use specific index:</p> <p>```gql &ndash; Use index hint MATCH (p:Product) USING INDEX product_category_price WHERE p.category = &lsquo;Electronics&rsquo; AND p.price &lt; 300 RETURN p ```</p> <h4 id="partial-indexes" class="position-relative d-flex align-items-center group"> <span>Partial Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="partial-indexes" aria-haspopup="dialog" aria-label="Share link: Partial Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create indexes with filter conditions (if supported):</p> <p>```gql &ndash; Index only active products CREATE INDEX ON :Product(name) WHERE in_stock = true ```</p> <h4 id="index-only-scans" class="position-relative d-flex align-items-center group"> <span>Index-Only Scans</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="index-only-scans" aria-haspopup="dialog" aria-label="Share link: Index-Only Scans"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Optimize queries to use index-only scans:</p> <p>```gql &ndash; Before: Requires table access MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; RETURN p &ndash; Returns all properties</p> <p>&ndash; After: Index-only scan MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; RETURN p.name, p.category &ndash; Only indexed properties ```</p> <h3 id="part-8-performance-testing" class="position-relative d-flex align-items-center group"> <span>Part 8: Performance Testing</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-8-performance-testing" aria-haspopup="dialog" aria-label="Share link: Part 8: Performance Testing"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="step-1-benchmark-query-performance" class="position-relative d-flex align-items-center group"> <span>Step 1: Benchmark Query Performance</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-1-benchmark-query-performance" aria-haspopup="dialog" aria-label="Share link: Step 1: Benchmark Query Performance"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Create a benchmark dataset:</p> <p>```bash</p> <h2 id="generate-100k-customers" class="position-relative d-flex align-items-center group"> <span>Generate 100k customers</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="generate-100k-customers" aria-haspopup="dialog" aria-label="Share link: Generate 100k customers"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h2><p>for i in {1..100000}; do echo &ldquo;CREATE (:Customer { id: $i, email: &lsquo;<a href="mailto:user$%7bi%[email protected]" >user${i}@example.com</a> &rsquo;, name: &lsquo;User $i&rsquo; })&rdquo; done | geode query ```</p> <h4 id="step-2-measure-query-performance" class="position-relative d-flex align-items-center group"> <span>Step 2: Measure Query Performance</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-2-measure-query-performance" aria-haspopup="dialog" aria-label="Share link: Step 2: Measure Query Performance"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Without Index</strong>: ```gql &ndash; Measure time PROFILE MATCH (c:Customer {email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;}) RETURN c &ndash; execution_time_ms: ~150ms (full scan) ```</p> <p><strong>With Index</strong>: ```gql CREATE INDEX ON :Customer(email)</p> <p>PROFILE MATCH (c:Customer {email: &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;}) RETURN c &ndash; execution_time_ms: ~2ms (index seek) ```</p> <p><strong>Performance Improvement</strong>: 75x faster</p> <h4 id="step-3-load-testing" class="position-relative d-flex align-items-center group"> <span>Step 3: Load Testing</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="step-3-load-testing" aria-haspopup="dialog" aria-label="Share link: Step 3: Load Testing"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p>Use a load testing tool to measure throughput:</p> <p>```python import asyncio from geode_client import open_database import time</p> <p>async def benchmark_queries(db, num_queries=1000): start = time.time() tasks = []</p> <pre><code>async with db.connection() as conn: for i in range(num_queries): task = conn.query( &quot;MATCH (c:Customer {email: $email}) RETURN c&quot;, {&quot;email&quot;: f&quot;user{i}@example.com&quot;} ) tasks.append(task) await asyncio.gather(*tasks) elapsed = time.time() - start qps = num_queries / elapsed print(f&quot;Queries: {num_queries}, Time: {elapsed:.2f}s, QPS: {qps:.0f}&quot;) </code></pre> <h2 id="run-benchmark" class="position-relative d-flex align-items-center group"> <span>Run benchmark</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="run-benchmark" aria-haspopup="dialog" aria-label="Share link: Run benchmark"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h2><p>db = open_database(&ldquo;quic://localhost:3141&rdquo;, pool_size=50) asyncio.run(benchmark_queries(db, 1000))</p> <h2 id="output-queries-1000-time-215s-qps-465" class="position-relative d-flex align-items-center group"> <span>Output: Queries: 1000, Time: 2.15s, QPS: 465</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="output-queries-1000-time-215s-qps-465" aria-haspopup="dialog" aria-label="Share link: Output: Queries: 1000, Time: 2.15s, QPS: 465"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h2><p>```</p> <h3 id="part-9-common-pitfalls-and-solutions" class="position-relative d-flex align-items-center group"> <span>Part 9: Common Pitfalls and Solutions</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="part-9-common-pitfalls-and-solutions" aria-haspopup="dialog" aria-label="Share link: Part 9: Common Pitfalls and Solutions"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="pitfall-1-too-many-indexes" class="position-relative d-flex align-items-center group"> <span>Pitfall 1: Too Many Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="pitfall-1-too-many-indexes" aria-haspopup="dialog" aria-label="Share link: Pitfall 1: Too Many Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Problem</strong>: Every index adds write overhead</p> <p><strong>Solution</strong>: Only create indexes for frequent queries ```gql &ndash; Analyze query patterns first CALL db.query.stats() YIELD query, execution_count WHERE execution_count &gt; 100 RETURN query, execution_count ORDER BY execution_count DESC LIMIT 10</p> <p>&ndash; Create indexes for top queries only ```</p> <h4 id="pitfall-2-wrong-index-type" class="position-relative d-flex align-items-center group"> <span>Pitfall 2: Wrong Index Type</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="pitfall-2-wrong-index-type" aria-haspopup="dialog" aria-label="Share link: Pitfall 2: Wrong Index Type"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Problem</strong>: B-tree index for vector similarity</p> <p><strong>Solution</strong>: Use appropriate index type ```gql &ndash; Wrong: B-tree for embeddings CREATE INDEX ON :Product(embedding)</p> <p>&ndash; Correct: HNSW for embeddings CREATE VECTOR INDEX ON :Product(embedding) OPTIONS {dimensions: 8, distance: &lsquo;cosine&rsquo;} ```</p> <h4 id="pitfall-3-unselective-indexes" class="position-relative d-flex align-items-center group"> <span>Pitfall 3: Unselective Indexes</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="pitfall-3-unselective-indexes" aria-haspopup="dialog" aria-label="Share link: Pitfall 3: Unselective Indexes"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Problem</strong>: Index on low-cardinality field</p> <p><strong>Solution</strong>: Check index selectivity ```gql &ndash; Check cardinality MATCH (p:Product) RETURN p.category, count(*) AS count ORDER BY count DESC</p> <p>&ndash; If most values have high count, index is not useful ```</p> <h4 id="pitfall-4-stale-statistics" class="position-relative d-flex align-items-center group"> <span>Pitfall 4: Stale Statistics</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="pitfall-4-stale-statistics" aria-haspopup="dialog" aria-label="Share link: Pitfall 4: Stale Statistics"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Problem</strong>: Query planner uses outdated statistics</p> <p><strong>Solution</strong>: Update statistics regularly ```gql CALL db.stats.update() ```</p> <h3 id="practice-exercises" class="position-relative d-flex align-items-center group"> <span>Practice Exercises</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="practice-exercises" aria-haspopup="dialog" aria-label="Share link: Practice Exercises"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="exercise-1-e-commerce-search-optimization" class="position-relative d-flex align-items-center group"> <span>Exercise 1: E-commerce Search Optimization</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="exercise-1-e-commerce-search-optimization" aria-haspopup="dialog" aria-label="Share link: Exercise 1: E-commerce Search Optimization"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Task</strong>: Optimize this query: ```gql MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; AND p.price &gt;= 100 AND p.price &lt;= 500 AND fulltext_search(p.description, &lsquo;wireless bluetooth&rsquo;) RETURN p.name, p.price, fulltext_score(p.description, &lsquo;wireless bluetooth&rsquo;) AS score ORDER BY score DESC, p.price LIMIT 20 ```</p> <p><strong>Steps</strong>:</p> <ol> <li>Create composite B-tree index: category + price</li> <li>Create full-text index on description</li> <li>Verify with EXPLAIN</li> <li>Measure with PROFILE</li> </ol> <p><strong>Solution</strong>: ```gql CREATE INDEX ON :Product(category, price) CREATE FULLTEXT INDEX ON :Product(description)</p> <p>EXPLAIN MATCH (p:Product) WHERE p.category = &lsquo;Electronics&rsquo; AND p.price &gt;= 100 AND p.price &lt;= 500 AND fulltext_search(p.description, &lsquo;wireless bluetooth&rsquo;) RETURN p.name, p.price, fulltext_score(p.description, &lsquo;wireless bluetooth&rsquo;) AS score ORDER BY score DESC, p.price LIMIT 20 ```</p> <h4 id="exercise-2-geospatial-query-optimization" class="position-relative d-flex align-items-center group"> <span>Exercise 2: Geospatial Query Optimization</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="exercise-2-geospatial-query-optimization" aria-haspopup="dialog" aria-label="Share link: Exercise 2: Geospatial Query Optimization"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Task</strong>: Find customers within 10km of stores</p> <p>```gql MATCH (c:Customer), (s:Store) WHERE distance(c.location, s.location) &lt; 10000 RETURN c.name, s.name, distance(c.location, s.location) AS distance_m ORDER BY distance_m ```</p> <p><strong>Steps</strong>:</p> <ol> <li>Create spatial indexes on both Customer and Store locations</li> <li>Use EXPLAIN to verify R-tree usage</li> <li>Compare performance with/without indexes</li> </ol> <p><strong>Solution</strong>: ```gql CREATE SPATIAL INDEX ON :Customer(location) CREATE SPATIAL INDEX ON :Store(location)</p> <p>PROFILE MATCH (c:Customer), (s:Store) WHERE distance(c.location, s.location) &lt; 10000 RETURN c.name, s.name, distance(c.location, s.location) AS distance_m ORDER BY distance_m ```</p> <h4 id="exercise-3-vector-similarity-optimization" class="position-relative d-flex align-items-center group"> <span>Exercise 3: Vector Similarity Optimization</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="exercise-3-vector-similarity-optimization" aria-haspopup="dialog" aria-label="Share link: Exercise 3: Vector Similarity Optimization"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><p><strong>Task</strong>: Implement product recommendation based on embedding similarity</p> <p>```gql &ndash; Given a product, find similar products MATCH (p:Product {id: 101}) MATCH (similar:Product) WHERE similar.id &lt;&gt; p.id AND vector_similarity(p.embedding, similar.embedding, &lsquo;cosine&rsquo;) &gt; 0.7 RETURN similar.name, vector_similarity(p.embedding, similar.embedding, &lsquo;cosine&rsquo;) AS similarity ORDER BY similarity DESC LIMIT 10 ```</p> <p><strong>Steps</strong>:</p> <ol> <li>Create HNSW index with optimal parameters</li> <li>Test different distance metrics</li> <li>Tune ef_search for recall/latency trade-off</li> </ol> <p><strong>Solution</strong>: ```gql CREATE VECTOR INDEX product_recommendations ON :Product(embedding) OPTIONS { dimensions: 8, distance: &lsquo;cosine&rsquo;, ef_construction: 200, m: 16, ef_search: 100 }</p> <p>&ndash; Query uses index automatically MATCH (p:Product {id: 101}) MATCH (similar:Product) WHERE similar.id &lt;&gt; p.id AND vector_similarity(p.embedding, similar.embedding, &lsquo;cosine&rsquo;) &gt; 0.7 RETURN similar.name, vector_similarity(p.embedding, similar.embedding, &lsquo;cosine&rsquo;) AS similarity ORDER BY similarity DESC LIMIT 10 ```</p> <h3 id="quick-reference-card" class="position-relative d-flex align-items-center group"> <span>Quick Reference Card</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="quick-reference-card" aria-haspopup="dialog" aria-label="Share link: Quick Reference Card"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3> <h4 id="index-type-selection" class="position-relative d-flex align-items-center group"> <span>Index Type Selection</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="index-type-selection" aria-haspopup="dialog" aria-label="Share link: Index Type Selection"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><table> <thead> <tr> <th>Query Pattern</th> <th>Index Type</th> <th>Example</th> </tr> </thead> <tbody> <tr> <td>Equality lookup</td> <td>B-tree</td> <td>`email = &lsquo;<a href="mailto:[email protected]" >[email protected]</a> &rsquo;`</td> </tr> <tr> <td>Range query</td> <td>B-tree</td> <td>`price BETWEEN 100 AND 200`</td> </tr> <tr> <td>Text search</td> <td>Full-text</td> <td>`fulltext_search(description, &lsquo;wireless&rsquo;)`</td> </tr> <tr> <td>Vector similarity</td> <td>HNSW</td> <td>`vector_similarity(embedding, query_vector)`</td> </tr> <tr> <td>Geographic range</td> <td>Spatial</td> <td>`distance(location, point) &lt; 10000`</td> </tr> <tr> <td>Prefix matching</td> <td>B-tree</td> <td>`name STARTS WITH &lsquo;Alice&rsquo;`</td> </tr> </tbody> </table> <h4 id="explain-plan-operators" class="position-relative d-flex align-items-center group"> <span>EXPLAIN Plan Operators</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="explain-plan-operators" aria-haspopup="dialog" aria-label="Share link: EXPLAIN Plan Operators"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><table> <thead> <tr> <th>Operator</th> <th>Meaning</th> <th>Performance</th> </tr> </thead> <tbody> <tr> <td>IndexSeek</td> <td>Index lookup</td> <td>O(log n) - Excellent</td> </tr> <tr> <td>NodeScan</td> <td>Full table scan</td> <td>O(n) - Poor</td> </tr> <tr> <td>Filter</td> <td>Post-scan filtering</td> <td>O(n) - Poor if large result set</td> </tr> <tr> <td>VectorIndexScan</td> <td>HNSW search</td> <td>O(log n) avg - Good</td> </tr> <tr> <td>SpatialIndexScan</td> <td>R-tree search</td> <td>O(log n) - Good</td> </tr> </tbody> </table> <h4 id="optimization-checklist" class="position-relative d-flex align-items-center group"> <span>Optimization Checklist</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="optimization-checklist" aria-haspopup="dialog" aria-label="Share link: Optimization Checklist"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h4><ul> <li><input disabled="" type="checkbox"> Analyze query patterns with db.query.stats()</li> <li><input disabled="" type="checkbox"> Create indexes for frequent queries</li> <li><input disabled="" type="checkbox"> Use EXPLAIN to verify index usage</li> <li><input disabled="" type="checkbox"> Use PROFILE to measure performance</li> <li><input disabled="" type="checkbox"> Monitor index hit rates</li> <li><input disabled="" type="checkbox"> Update statistics regularly</li> <li><input disabled="" type="checkbox"> Remove unused indexes</li> <li><input disabled="" type="checkbox"> Consider covering indexes for common queries</li> <li><input disabled="" type="checkbox"> Tune HNSW parameters for vector indexes</li> <li><input disabled="" type="checkbox"> Benchmark before and after index creation</li> </ul> <h3 id="next-steps" class="position-relative d-flex align-items-center group"> <span>Next Steps</span> <button type="button" class="h-share btn btn-link p-0 text-decoration-none link-secondary opacity-50 hover-opacity-100 transition-all ms-1" data-share-target="next-steps" aria-haspopup="dialog" aria-label="Share link: Next Steps"> <i class="fa-sharp-duotone fa-solid fa-share-nodes" aria-hidden="true" style="font-size: 0.8em;"></i> <span class="visually-hidden">Share link</span> </button> </h3><ol> <li><strong>Production Deployment</strong> - Apply index strategies to your application</li> <li><strong>Monitoring</strong> - Set up index usage monitoring</li> <li><strong>Query Optimization</strong> - Review slow queries and add indexes</li> <li><strong>Advanced Topics</strong> - Explore query hints and optimizer settings</li> <li><strong>Performance Testing</strong> - Benchmark with realistic workloads</li> </ol> <p><strong>Related Documentation</strong>:</p> <ul> <li><a href="/docs/query/performance-tuning" >Query Performance Tuning</a> </li> <li><a href="/docs/query/explain-profile" >EXPLAIN/PROFILE Commands</a> </li> <li><a href="/docs/query/full-text-search" >Full-Text Search</a> </li> <li><a href="/docs/query/indexing-and-optimization/" >Indexing Reference</a> </li> </ul> <hr> <p><em>Last Updated: January 2026</em> <em>Difficulty: Intermediate</em> <em>Estimated Time: 90 minutes</em></p>