relatedposts
v3.0.0
Published
A utility package to find related posts or blogs based on semantic similarity of titles, categories, and tags. Works with standard content objects and Astro blog collections.
Maintainers
Readme
Related Posts
A utility function to find related content based on title, category, and tags using semantic embeddings and cosine similarity for more accurate content matching. Now with support for both standard content objects and Astro blog collections.
Features
- Semantic similarity: Uses transformer-based embeddings to understand meaning beyond surface-level text matching
- Cosine similarity: Calculate precise similarity scores between content embeddings
- Multi-dimensional matching: Find related content based on title, category, and tags
- Customizable weights: Adjust importance of title, category, and tags in similarity calculations
- Smart preprocessing: Remove stopwords and non-letter characters for cleaner comparisons
- Performance optimized: Cached embedding pipeline for faster subsequent operations
Installation
Install the package and its dependencies via npm:
bash
npm install relatedposts @xenova/transformersbash
import getRelated, { getRelatedAstro } from 'relatedposts';Function
The getRelated function evaluates and returns a list of Comparison objects that are most similar to a given Input object. Similarity is determined using semantic embeddings for titles, exact matching for categories, and overlap analysis for tags. The results are sorted by their similarity scores in descending order.
Note: This function uses machine learning embeddings and is asynchronous. The first call may take longer as it downloads and initializes the transformer model (~90MB).
Parameters
input(Input):- An object representing the input data to be compared against.
- Fields:
title(string): The title of the input.category(string): The category of the input.tags(string[]): An array of tags associated with the input.
Example:
typescript
const input = { title: "The quick brown dog", category: "Animals", tags: ["quick", "brown", "dog"], };comparisons(Comparison[]):- An array of objects representing the items to be compared against the input.
- Fields:
title(string): The title of the comparison item.category(string): The category of the comparison item.tags(string[]): An array of tags associated with the comparison item.
Example:
typescript
const comparisons = [ { title: "The quick brown fox", category: "Animals", tags: ["quick", "brown", "fox"], }, { title: "The lazy dog", category: "Animals", tags: ["lazy", "dog"], }, { title: "A fast brown cat", category: "Animals", tags: ["fast", "brown", "cat"], }, ];numResults(number, optional):- The number of results to return. Defaults to 5.
options(Options, optional):- An object containing weights to influence the similarity score calculations.
- Fields:
weights(Weights, optional): Object containing weight values for different similarity components.title(number, optional): Weight for the title similarity score. Defaults to 1.category(number, optional): Weight for the category similarity score. Defaults to 1.tags(number, optional): Weight for the tags similarity score. Defaults to 1.
Example:
typescript
const options = { weights: { title: 2.0, // Title similarity is twice as important category: 1.5, // Category matching is 1.5x as important tags: 1.0 // Tags have standard weight } };
Returns
- A Promise that resolves to an array of
ScoredComparisonobjects. - Fields:
- All fields of the original
Comparisonobject. score(number): The computed similarity score (0-1 range) based on semantic similarity, category matching, and tag overlap.
- All fields of the original
Example output:
typescript
const results = [
{
title: "A fast brown cat",
category: "Animals",
tags: ["fast", "brown", "cat"],
score: 0.87, // High semantic similarity between "quick dog" and "fast cat"
},
{
title: "The quick brown fox",
category: "Animals",
tags: ["quick", "brown", "fox"],
score: 0.82, // Very similar words and structure
},
{
title: "The lazy dog",
category: "Animals",
tags: ["lazy", "dog"],
score: 0.71, // Same animal type but different characteristics
},
];Usage
typescript
// Basic usage
const results = await getRelated(input, comparisons, 5);
console.log(results);
// With custom weights
const results = await getRelated(input, comparisons, 3, {
weights: {
title: 2.0,
category: 1.0,
tags: 0.5
}
});
console.log(results);getRelatedAstro Function
The getRelatedAstro function is specifically designed for Astro blog collections. It works with Astro's data structure where content is nested under a data property and uses ID-based filtering to exclude the current blog post from related suggestions.
Parameters
currentId(string):- The ID of the current blog post to exclude from results
blogs(AstroBlog[]):- Array of Astro blog objects with nested data structure
numResults(number, optional):- Number of results to return. Defaults to 5
options(Options, optional):- Same weight options as
getRelatedfunction
- Same weight options as
Astro Blog Structure
interface AstroBlog {
id: string;
collection: string;
data: {
title: string;
category?: string;
tags?: string[];
publishDate?: string;
// ... other frontmatter properties
};
body: string;
}Usage with Astro
import { getRelatedAstro } from 'relatedposts';
// Example: Finding related posts in an Astro project
const currentPostId = "my-current-post.md";
const allBlogPosts = [
{
id: "my-current-post.md",
collection: "blog",
data: {
title: "Getting Started with TypeScript",
category: "Development",
tags: ["typescript", "programming", "tutorial"],
publishDate: "2025-10-13"
},
body: "..."
},
{
id: "advanced-typescript.md",
collection: "blog",
data: {
title: "Advanced TypeScript Patterns",
category: "Development",
tags: ["typescript", "advanced", "patterns"],
publishDate: "2025-10-10"
},
body: "..."
}
// ... more blog posts
];
const relatedPosts = await getRelatedAstro(currentPostId, allBlogPosts, 3);
console.log(relatedPosts);Example Output
const relatedPosts = [
{
title: "Advanced TypeScript Patterns",
category: "Development",
tags: ["typescript", "advanced", "patterns"],
score: 0.89
},
{
title: "JavaScript Best Practices",
category: "Development",
tags: ["javascript", "best-practices"],
score: 0.76
}
];How It Works
- Title Similarity: Converts titles to semantic embeddings using a pre-trained transformer model and calculates cosine similarity
- Category Matching: Performs exact string matching between categories (binary score)
- Tag Overlap: Calculates the proportion of input tags that appear in comparison tags
- Weighted Scoring: Combines all three scores using customizable weights and returns a final similarity score
