Skip to content

Commit 63d07cd

Browse files
committed
Add 26 programmatic SEO pages: 7 framework guides + 8 use cases + 8 comparisons + 3 hubs
1 parent 15e6239 commit 63d07cd

27 files changed

Lines changed: 1934 additions & 0 deletions

public/frameworks/angular.html

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Vector Search in Angular — Service Pattern</title>
7+
<meta name="description" content="Angular service-based vector search tutorial with altor-vec, dependency injection, and browser performance guidance." />
8+
<meta name="keywords" content="angular vector search, altor-vec, semantic search angular, client-side search" />
9+
<meta name="geo.region" content="US" />
10+
<link rel="canonical" href="https://altorlab.dev/frameworks/angular.html" />
11+
<link rel="alternate" hreflang="en-us" href="https://altorlab.dev/frameworks/angular.html" />
12+
<meta property="og:type" content="article" />
13+
<meta property="og:title" content="Vector Search in Angular — Service Pattern" />
14+
<meta property="og:description" content="Angular service-based vector search tutorial with altor-vec, dependency injection, and browser performance guidance." />
15+
<meta property="og:url" content="https://altorlab.dev/frameworks/angular.html" />
16+
<meta property="og:locale" content="en_US" />
17+
<meta name="twitter:card" content="summary_large_image" />
18+
<meta name="twitter:title" content="Vector Search in Angular — Service Pattern" />
19+
<meta name="twitter:description" content="Angular service-based vector search tutorial with altor-vec, dependency injection, and browser performance guidance." />
20+
21+
<link rel="preconnect" href="https://fonts.googleapis.com" />
22+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
23+
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
24+
25+
<style>
26+
:root {
27+
--bg:#080a09;
28+
--surface:#0e1210;
29+
--surface-raised:#141a17;
30+
--border:#1e2823;
31+
--border-hover:#2a3831;
32+
--text:#edf0ee;
33+
--text-secondary:#8b9690;
34+
--muted:#556059;
35+
--altor:#6ee7b7;
36+
--altor-glow:rgba(110,231,183,.07);
37+
--code:#c3e88d;
38+
}
39+
* { box-sizing:border-box; }
40+
body { margin:0; background:var(--bg); color:var(--text); font-family:'Plus Jakarta Sans',system-ui,sans-serif; line-height:1.74; }
41+
a { color:var(--altor); text-decoration:none; }
42+
a:hover { text-decoration:underline; }
43+
main { max-width:920px; margin:0 auto; padding:30px 22px 78px; }
44+
.breadcrumbs { display:flex; gap:8px; flex-wrap:wrap; color:var(--muted); font-size:13px; }
45+
.mono, code, pre { font-family:'JetBrains Mono',monospace; }
46+
h1 { font-family:'Instrument Serif',serif; font-size:clamp(2.3rem,5vw,3.7rem); line-height:1.08; margin:.55rem 0 1rem; }
47+
h2 { margin-top:2.35rem; font-size:1.55rem; }
48+
h3 { margin-top:1.5rem; font-size:1.08rem; }
49+
p, li, td, th { color:var(--text-secondary); }
50+
ul, ol { padding-left:20px; }
51+
pre { background:#0d1512; border:1px solid var(--border); border-radius:12px; padding:14px; overflow:auto; color:var(--code); line-height:1.6; }
52+
code { color:#d9f99d; }
53+
.table-wrap { overflow:auto; }
54+
table { width:100%; border-collapse:collapse; margin:14px 0; background:var(--surface-raised); border:1px solid var(--border); border-radius:12px; overflow:hidden; }
55+
th, td { padding:12px 14px; border-bottom:1px solid var(--border); text-align:left; vertical-align:top; }
56+
th { color:var(--text); background:#111814; }
57+
tr:last-child td { border-bottom:none; }
58+
.k { text-transform:uppercase; letter-spacing:.16em; color:var(--muted); font-size:11px; }
59+
.lead { font-size:1.03rem; }
60+
.install, .callout, .diagram, .note { background:var(--surface-raised); border:1px solid rgba(110,231,183,.2); border-radius:12px; padding:14px; }
61+
.callout { background:var(--altor-glow); }
62+
.diagram { white-space:pre; overflow:auto; color:var(--code); }
63+
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:14px; }
64+
.card { background:var(--surface-raised); border:1px solid var(--border); border-radius:14px; padding:16px; }
65+
.footer-nav { display:flex; gap:14px; flex-wrap:wrap; margin-top:28px; font-size:14px; }
66+
.small { font-size:13px; color:var(--muted); }
67+
hr { border:none; border-top:1px solid var(--border); margin:32px 0; }
68+
</style>
69+
<script type="application/ld+json">{"@context":"https://schema.org","@type":"TechArticle","headline":'Vector Search in Angular — Service Pattern',"url":'https://altorlab.dev/frameworks/angular.html',"datePublished":'2026-03-29',"dateModified":'2026-03-29',"author":{"@type":"Organization","name":"AltorLab"},"publisher":{"@type":"Organization","name":"AltorLab"},"keywords":['angular','vector search','altor-vec','client-side search'],"audience":{"@type":"Audience","audienceType":'Angular, JavaScript, and TypeScript developers'},"about":{"@type":"SoftwareApplication","name":"altor-vec"},"inLanguage":"en-US"}</script><script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":'Does altor-vec need a backend in Angular?',"acceptedAnswer":{"@type":"Answer","text":'No. The library can load a serialized index or build one from vectors directly inside a Angular application. You only need a backend when your corpus is private, very large, or updated continuously.'}},{"@type":"Question","name":'Can I add vectors after initialization in Angular?',"acceptedAnswer":{"@type":"Answer","text":'Yes. WasmSearchEngine exposes add_vectors(flat, dims), which is useful for appending a few new embeddings without rebuilding the whole index during a demo or a controlled sync step.'}},{"@type":"Question","name":'What is the biggest performance risk in Angular?',"acceptedAnswer":{"@type":"Answer","text":'Embedding generation and UI churn are the main risks. altor-vec search is fast; expensive models, repeated initialization, or too many renders usually dominate the user-visible delay in Angular apps.'}}]}</script>
70+
</head>
71+
<body>
72+
<main>
73+
<nav class="breadcrumbs" aria-label="Breadcrumb"><a class="mono" href="/">Home</a><span>&gt;</span><a class="mono" href="/frameworks/">Frameworks</a><span>&gt;</span><span>Vector Search in Angular — Service Pattern</span></nav>
74+
<p class="k">vector search angular</p>
75+
<h1>Vector Search in Angular — Service Pattern</h1>
76+
<p class="lead">Angular developers usually hit the same search problem: keyword search is easy to ship, but it fails when the user phrase and the document phrase do not overlap. <code>altor-vec</code> solves the retrieval side by running HNSW vector search locally in WebAssembly. That means you can keep query latency close to the browser, eliminate per-query billing, and still expose a familiar framework component API. This guide focuses on implementation details rather than marketing claims.</p><div class="install"><strong>Install altor-vec:</strong> <code>npm install altor-vec</code></div><p>The example below uses tiny four-dimensional vectors so the code is runnable as-is and easy to understand. In production you would usually replace those manual vectors with embeddings from a model such as <code>Xenova/all-MiniLM-L6-v2</code> or a build-time embedding job. The retrieval flow stays the same: install, import the WASM package, create an index, optionally add vectors, then query the engine and map result IDs back to metadata.</p><h2>Step 1: install and understand the runtime boundary</h2><p>Start with <code>npm install altor-vec</code>. The package exposes a default <code>init()</code> function that loads the WASM module and a <code>WasmSearchEngine</code> class that loads or builds an HNSW index. The important design question in Angular is not whether vector retrieval is possible. It is where initialization should live so the index is created once, memory is released intentionally, and queries do not trigger unnecessary work on each re-render or navigation.</p><h2>Step 2: import the library and create the index</h2><p>The sample builds an index from a flat <code>Float32Array</code>. That matches the real API from the package README: <code>WasmSearchEngine.from_vectors(flat, dims, m, ef_construction, ef_search)</code>. The four HNSW parameters here are conservative defaults for a small browser index. If you precompute a production index offline, you can instead serialize it with <code>to_bytes()</code> and load it using <code>new WasmSearchEngine(bytes)</code>.</p><pre><code>// vector-search.service.ts
77+
import { Injectable } from &#x27;@angular/core&#x27;;
78+
import init, { WasmSearchEngine } from &#x27;altor-vec&#x27;;
79+
80+
const docs = [
81+
{ title: &#x27;Dependency injection&#x27;, vector: [1, 0, 0, 0] },
82+
{ title: &#x27;Build optimizer&#x27;, vector: [0, 1, 0, 0] },
83+
{ title: &#x27;Search architecture&#x27;, vector: [0, 0, 1, 0] },
84+
];
85+
86+
@Injectable({ providedIn: &#x27;root&#x27; })
87+
export class VectorSearchService {
88+
private engine: WasmSearchEngine | null = null;
89+
90+
async init() {
91+
if (this.engine) return;
92+
await init();
93+
const dim = 4;
94+
const flat = new Float32Array(docs.flatMap((doc) =&gt; doc.vector));
95+
this.engine = WasmSearchEngine.from_vectors(flat, dim, 16, 200, 50);
96+
97+
docs.push({ title: &#x27;Angular hydration guide&#x27;, vector: [0.95, 0.05, 0, 0] });
98+
this.engine.add_vectors(new Float32Array([0.95, 0.05, 0, 0]), dim);
99+
}
100+
101+
search(query: [number, number, number, number]) {
102+
if (!this.engine) throw new Error(&#x27;init() first&#x27;);
103+
const hits: [number, number][] = JSON.parse(this.engine.search(new Float32Array(query), 3));
104+
return hits.map(([id, distance]) =&gt; ({ ...docs[id], distance }));
105+
}
106+
}
107+
108+
// search.component.ts
109+
@Component({ selector: &#x27;app-search&#x27;, template: `
110+
&lt;button (click)=&quot;run()&quot;&gt;Search&lt;/button&gt;
111+
&lt;ul&gt;&lt;li *ngFor=&quot;let hit of hits&quot;&gt;{{ hit.title }} — {{ hit.distance | number:&#x27;1.2-2&#x27; }}&lt;/li&gt;&lt;/ul&gt;
112+
` })
113+
export class SearchComponent {
114+
hits: Array&lt;{ title: string; distance: number }&gt; = [];
115+
constructor(private readonly vectorSearch: VectorSearchService) {}
116+
async ngOnInit() { await this.vectorSearch.init(); }
117+
run() { this.hits = this.vectorSearch.search([0.94, 0.06, 0, 0]); }
118+
}</code></pre><h2>Step 3: what the code is actually doing</h2><ol><li><strong>Install:</strong> the project adds <code>altor-vec</code> from npm.</li><li><strong>Import:</strong> the code imports <code>init</code> and <code>WasmSearchEngine</code>.</li><li><strong>Create index:</strong> manual vectors are flattened into a single <code>Float32Array</code> and passed into <code>from_vectors()</code>.</li><li><strong>Add vectors:</strong> the example appends one more vector via <code>add_vectors()</code> so you can see incremental updates.</li><li><strong>Query:</strong> it converts a query vector into a <code>Float32Array</code>, calls <code>search()</code>, parses the JSON response, and maps IDs back to the in-memory document array.</li></ol><p>That pattern is stable across browser frameworks because <code>altor-vec</code> is model-agnostic. The framework concerns are mostly lifecycle-related: where to hold the engine instance, how to debounce query creation, and whether embeddings run on the main thread or in a worker. If you keep those concerns separate, semantic retrieval feels surprisingly ordinary.</p><h2>Performance notes specific to this framework</h2><ul><li>The Angular service boundary is useful because it prevents repeated engine creation as routes and components mount.</li><li>Use RxJS debounceTime before any embedding call. altor-vec search is fast enough that over-eager query streams usually waste model cycles, not HNSW time.</li><li>Keep Zone.js change detection away from high-frequency worker messages if you process live semantic suggestions.</li></ul><div class="note"><strong>Published altor-vec baseline:</strong> Chrome p95 retrieval on 10K vectors / 384 dimensions is about <code>0.6ms</code>, index load is about <code>19ms</code>, the raw WASM binary is <code>117KB</code>, and the gzipped WASM payload is <code>54KB</code>. In real apps, embedding generation and rendering usually cost much more than the vector lookup itself.</div><h2>When to use client-side vs server-side in Angular</h2><p><strong>Client-side:</strong> Browser-only Angular works well for internal tools, admin panels, and static docs where the bundle already ships app logic.</p><p><strong>Server-side:</strong> Introduce server search when enterprise requirements demand authorization, centralized indexing pipelines, or auditability beyond the Angular frontend.</p><p>A good rule is simple. If the data is already safe to send to every browser and you mostly care about fast semantic ranking, keep it local. If the search layer also needs to enforce business rules, security boundaries, or complex shared state, put retrieval on the server and let Angular call it as a normal endpoint.</p><h2>Production checklist</h2><ul><li>Cache the WASM and serialized index aggressively with versioned asset names.</li><li>Validate vector dimensions before every search to prevent subtle runtime errors.</li><li>Keep metadata outside the HNSW graph so result rendering stays flexible.</li><li>Measure cold start, repeated search latency, and memory on at least one mid-range mobile device.</li><li>Free the engine explicitly if you unload large indexes on navigation.</li></ul><h2>Conclusion</h2><p>Angular does not require a special semantic-search abstraction. It only needs a clean place to initialize the engine and a disciplined boundary between embedding, retrieval, and UI state. <code>altor-vec</code> gives you a small browser-native ANN core, while the framework handles rendering and ergonomics. If you want a developer-friendly starting point with no backend dependency, this is the shortest path: <code>npm install altor-vec</code>, build or load an index, and search locally.</p><div class="callout"><strong>CTA:</strong> <span class="mono">npm install altor-vec</span> · <a href="https://github.com/Altor-lab/altor-vec" target="_blank" rel="noopener">Star on GitHub</a></div><p class="small">Related hubs: <a href="/frameworks/">Framework guides</a> · <a href="/use-cases/">Use cases</a> · <a href="/vs/">Comparison pages</a></p>
119+
<nav class="footer-nav" aria-label="Page navigation">
120+
<a href="/">Homepage</a>
121+
<a href="/blog/">Blog</a>
122+
</nav>
123+
</main>
124+
</body>
125+
</html>

0 commit comments

Comments
 (0)