# How the Scoring Mechanism for Tool Search Results Works in FckSignups

> Understand the FckSignups tool search scoring mechanism. Learn how keywords in names, descriptions, and tags determine search result ranking, sorted by score and star count.

- Repository: [Abdullah/FckSignups](https://github.com/BraveOPotato/FckSignups)
- Tags: internals
- Published: 2026-09-08

---

**The scoring mechanism counts how many query keywords appear across a tool's name, description, and tags, filtering for matches that contain all tokens and sorting by highest score followed by star count.**

The BraveOPotato/FckSignups repository implements a keyword-matching algorithm to rank tools during search. Understanding this **scoring mechanism for tool search results** helps developers optimize tool metadata and debug ranking issues. The implementation lives primarily in [`src/hooks/useTools.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useTools.ts) and combines tokenization, exact-match filtering, and multi-criteria sorting.

## Tokenizing the Search Query

Before scoring begins, the raw search string undergoes normalization in the `tokenize` function (lines 37-44). This process converts input to lowercase and splits it into alphanumeric tokens using the regex `/[^a-z0-9]+/`, ensuring that "video‑editor", "Video Editor", and "video_editor" are treated identically.

```typescript
// src/hooks/useTools.ts (lines 37-44)
const tokenize = (query: string): string[] => {
  return query
    .toLowerCase()
    .split(/[^a-z0-9]+/)
    .filter(token => token.length > 0);
};

```

## Calculating the Match Score

The `matchScore` function (lines 46-54) evaluates each tool by constructing a searchable "haystack" from three fields defined in [`src/types/index.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/types/index.ts): `name`, `description`, and `tags`. It then counts how many query tokens appear within this concatenated string.

```typescript
// src/hooks/useTools.ts (lines 46-54)
const matchScore = (tool: Tool, keywords: string[]): number => {
  const haystack = `${tool.name} ${tool.description} ${tool.tags.join(' ')}`.toLowerCase();
  return keywords.filter(keyword => haystack.includes(keyword)).length;
};

```

For example, searching "video editor" produces tokens `["video", "editor"]`. A tool named "Super Video Editor" with tags `["media"]` generates the haystack `"super video editor [description contents] media"`, yielding a score of 2 because both tokens are present.

## Filtering and Ranking Results

After calculating scores, the system applies strict filtering and secondary sorting in the `filteredTools` memo (lines 101-110) to determine final result order.

### Strict Filtering Requirements

A tool only passes the filter if its **match score equals the total number of query tokens** (`score === keywords.length`). This enforces an AND logic where every keyword must be present. If the query is empty, the filter returns all available tools from [`src/data/schema.js`](https://github.com/BraveOPotato/FckSignups/blob/main/src/data/schema.js).

### Secondary Sorting by Popularity

When multiple tools achieve identical scores, the sorting logic compares star counts in descending order (`b.tool.stars - a.tool.stars`). This ensures higher-popularity tools surface first when keyword relevance is equal.

```typescript
// src/hooks/useTools.ts (lines 101-110)
const filteredTools = useMemo(() => {
  return tools
    .filter(tool => matchScore(tool, keywords) === keywords.length || keywords.length === 0)
    .sort((a, b) => {
      const scoreDiff = matchScore(b, keywords) - matchScore(a, keywords);
      if (scoreDiff !== 0) return scoreDiff;
      return b.tool.stars - a.tool.stars;
    });
}, [tools, keywords]);

```

## Summary

- The `tokenize` function normalizes queries to lowercase alphanumeric tokens to handle punctuation variations.
- `matchScore` concatenates `name`, `description`, and `tags` into a haystring and counts keyword matches.
- The filter requires tools to match **all** query tokens, implementing strict conjunctive search logic.
- When scores tie, tools with higher star counts rank first according to the secondary sort in the `filteredTools` memo.

## Frequently Asked Questions

### How does the tokenization handle special characters?

The `tokenize` function splits the query using the regex `/[^a-z0-9]+/`, which treats any non-alphanumeric character as a delimiter. This means searches for "video-editor", "video_editor", or "Video.Editor" all generate the same token array `["video", "editor"]`.

### What fields are included in the search scoring?

According to the source code in [`src/hooks/useTools.ts`](https://github.com/BraveOPotato/FckSignups/blob/main/src/hooks/useTools.ts), the algorithm searches across three fields: `name`, `description`, and the `tags` array. These fields are concatenated into a single lowercase string before token matching occurs.

### Why might a tool with matching keywords not appear in results?

The filtering logic requires the match score to equal the number of query tokens. If a user searches for "video editor" (two tokens) and a tool only contains "video" in its searchable fields, it receives a score of 1, which fails the strict equality check and excludes it from results.

### How does the system break ties between equally relevant tools?

When multiple tools achieve the same match score, the sorting logic in lines 101-110 compares `b.tool.stars - a.tool.stars`. Tools with higher star counts are placed first in the results list, ensuring popular tools surface when keyword relevance is identical.