# Keyword Search Explained

## Content: Keyword Search Explained

### User Intent

"How does keyword/full-text search work in Graphlit?"

### Operation

* **SDK Method**: `queryContents()` with `searchType: SearchKeyword`
* **GraphQL**: `queryContents` query
* **Common Use Cases**: Exact phrase matching, names, IDs, codes, fast lookups

### How Keyword Search Works

Keyword search uses traditional full-text indexing with token-based matching. It's fast, precise, and ideal for exact phrases, names, and identifiers.

### TypeScript (Canonical)

```typescript
import { Graphlit } from 'graphlit-client';
import { ContentTypes, SearchTypes } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

// Pure keyword search
const results = await graphlit.queryContents({
  search: "Project Alpha status report",
  searchType: SearchTypes.Keyword,
  limit: 10
});

console.log(`Found ${results.contents.results.length} keyword matches`);

results.contents.results.forEach((content, index) => {
  console.log(`\n${index + 1}. ${content.name}`);
  console.log(`   Relevance: ${content.relevance}`);
  console.log(`   Type: ${content.type}`);
});

// Search for exact phrase
const exactPhrase = await graphlit.queryContents({
  search: '"quarterly earnings report"',  // Quotes = exact phrase
  searchType: SearchTypes.Keyword
});

// Search for ID or code
const byId = await graphlit.queryContents({
  search: "PROJ-1234",
  searchType: SearchTypes.Keyword
});

// Search for email address
const byEmail = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword
});

// Search for specific name
const byName = await graphlit.queryContents({
  search: "Kirk Marple",
  searchType: SearchTypes.Keyword
});
```

### Keyword Search Features

#### 1. Token-Based Matching

```typescript
// Search tokens: "machine", "learning", "tutorial"
const results = await graphlit.queryContents({
  search: "machine learning tutorial",
  searchType: SearchTypes.Keyword
});

// Matches documents containing these tokens
// Order doesn't matter (by default)
// Stemming applied: "learning" matches "learn", "learned", "learns"
```

#### 2. Exact Phrase Search

```typescript
// Use quotes for exact phrase
const exact = await graphlit.queryContents({
  search: '"machine learning tutorial"',  // Exact order required
  searchType: SearchTypes.Keyword
});

// Only matches documents with this exact phrase
```

#### 3. Boolean Operators (if supported)

```typescript
// AND operator (both terms required)
const andSearch = await graphlit.queryContents({
  search: "machine AND learning",
  searchType: SearchTypes.Keyword
});

// OR operator (either term)
const orSearch = await graphlit.queryContents({
  search: "machine OR learning",
  searchType: SearchTypes.Keyword
});

// NOT operator (exclude term)
const notSearch = await graphlit.queryContents({
  search: "machine NOT learning",
  searchType: SearchTypes.Keyword
});
```

#### 4. Case-Insensitive

```typescript
// All equivalent:
await graphlit.queryContents({ search: "graphlit", searchType: SearchTypes.Keyword });
await graphlit.queryContents({ search: "Graphlit", searchType: SearchTypes.Keyword });
await graphlit.queryContents({ search: "GRAPHLIT", searchType: SearchTypes.Keyword });

// All match "Graphlit", "graphlit", "GRAPHLIT"
```

#### 5. Stemming

```typescript
// Search for "running"
const results = await graphlit.queryContents({
  search: "running",
  searchType: SearchTypes.Keyword
});

// Also matches: "run", "runs", "ran", "runner"
// English stemming rules applied
```

### When to Use Keyword Search

\*\* Good For\*\*:

* Exact phrases ("Project Alpha v2.3")
* Names ("Kirk Marple", "Graphlit")
* Email addresses ("<kirk@graphlit.com>")
* IDs and codes ("PROJ-1234", "ORDER-5678")
* URLs ("<https://graphlit.com>")
* Specific terminology
* Fast lookups

\*\* Not Good For\*\*:

* Conceptual queries (use vector search)
* Synonyms and paraphrases (use vector search)
* Semantic similarity (use vector search)
* "Find similar" queries (use vector search)

### Performance Characteristics

```typescript
// Keyword search is FAST:
// - Inverted index lookup
// - Token-based (no vector computation)
// - Sub-100ms queries typical
// - Scales to billions of documents

const startTime = Date.now();
const results = await graphlit.queryContents({
  search: "PROJ-1234",
  searchType: SearchTypes.Keyword
});
console.log(`Query time: ${Date.now() - startTime}ms`);
// Typically: 20-50ms
```

### BM25 Ranking

**Algorithm**: BM25 (Best Matching 25)

* Standard information retrieval algorithm
* Considers term frequency and document length
* More sophisticated than simple TF-IDF

**Relevance Score**:

```typescript
const results = await graphlit.queryContents({
  search: "machine learning",
  searchType: SearchTypes.Keyword
});

results.contents.results.forEach(content => {
  console.log(`${content.name}: ${content.relevance}`);
  // Higher score = more relevant
  // Multiple occurrences of terms increase score
  // Shorter documents rank higher (BM25 length normalization)
});
```

## Keyword search (snake\_case)

results = await graphlit.client.query\_contents( search="Project Alpha", search\_type=SearchTypes.Keyword, limit=10 )

for content in results.contents.results: print(f"{content.name} - Relevance: {content.relevance}")

## Exact phrase

exact = await graphlit.client.query\_contents( search='"quarterly report"', search\_type=SearchTypes.Keyword )

## Search by ID

by\_id = await graphlit.client.query\_contents( search="PROJ-1234", search\_type=SearchTypes.Keyword )

````

**C#**:
```csharp
using Graphlit;

var client = new Graphlit();

// Keyword search (PascalCase)
var results = await graphlit.QueryContents(new ContentFilter
{
    Search = "Project Alpha",
    SearchType = SearchKeyword,
    Limit = 10
});

foreach (var content in results.Contents.Results)
{
    Console.WriteLine($"{content.Name} - Relevance: {content.Relevance}");
}

// Exact phrase
var exact = await graphlit.QueryContents(new ContentFilter
{
    Search = "\"quarterly report\"",
    SearchType = SearchKeyword
});

// Search by ID
var byId = await graphlit.QueryContents(new ContentFilter
{
    Search = "PROJ-1234",
    SearchType = SearchKeyword
});
````

### Developer Hints

#### Exact Phrase vs Token Search

```typescript
// Token search (default):
const tokens = await graphlit.queryContents({
  search: "machine learning",  // No quotes
  searchType: SearchTypes.Keyword
});
// Matches: "machine learning", "learning about machines", "machine for learning"

// Exact phrase:
const exact = await graphlit.queryContents({
  search: '"machine learning"',  // With quotes
  searchType: SearchTypes.Keyword
});
// Matches: Only "machine learning" in that exact order
```

#### Special Characters

```typescript
// Email addresses work as-is
const email = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword
});

// URLs work (may need quotes for special chars)
const url = await graphlit.queryContents({
  search: "https://graphlit.com",
  searchType: SearchTypes.Keyword
});

// IDs with hyphens work
const id = await graphlit.queryContents({
  search: "PROJ-1234",
  searchType: SearchTypes.Keyword
});
```

#### Combining with Filters

```typescript
// Keyword search + metadata filters
const filtered = await graphlit.queryContents({
  search: "Project Alpha",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email],
    creationDateRange: {
      from: '2024-01-01'
    }
  });

// Fast and precise
```

### Variations

#### 1. Basic Keyword Search

```typescript
const results = await graphlit.queryContents({
  search: "Graphlit platform",
  searchType: SearchTypes.Keyword
});
```

#### 2. Exact Phrase Search

```typescript
const exact = await graphlit.queryContents({
  search: '"context layer for AI"',
  searchType: SearchTypes.Keyword
});
```

#### 3. Search by ID

```typescript
const byId = await graphlit.queryContents({
  search: "PROJ-1234",
  searchType: SearchTypes.Keyword
});
```

#### 4. Search by Email

```typescript
const byEmail = await graphlit.queryContents({
  search: "kirk@graphlit.com",
  searchType: SearchTypes.Keyword
});
```

#### 5. Search with Content Type Filter

```typescript
const emailsOnly = await graphlit.queryContents({
  search: "Project Alpha",
  searchType: SearchTypes.Keyword,
  
    types: [ContentTypes.Email]
  });
```

#### 6. Search in Specific Collection

```typescript
const inCollection = await graphlit.queryContents({
  search: "meeting notes",
  searchType: SearchTypes.Keyword,
  
    collections: [{ id: 'team-docs-collection' }]
  });
```

#### 7. Multi-Term Search

```typescript
// All terms required (implicit AND)
const multiTerm = await graphlit.queryContents({
  search: "quarterly report Q4 2024",
  searchType: SearchTypes.Keyword
});
```

### Common Issues & Solutions

**Issue**: No results for partial words **Solution**: Keyword search doesn't do prefix matching by default

```typescript
//  Won't match "Graphlit"
await graphlit.queryContents({
  search: "Graph",
  searchType: SearchTypes.Keyword
});

//  Use full word
await graphlit.queryContents({
  search: "Graphlit",
  searchType: SearchTypes.Keyword
});

//  Or use vector search for fuzzy matching
await graphlit.queryContents({
  search: "Graph",
  searchType: SearchTypes.Vector
});
```

**Issue**: Too many results **Solution**: Use exact phrase or add filters

```typescript
//  Too broad
await graphlit.queryContents({
  search: "report",
  searchType: SearchTypes.Keyword
});

//  More specific
await graphlit.queryContents({
  search: '"quarterly earnings report"',
  searchType: SearchTypes.Keyword
});

//  Add date filter
await graphlit.queryContents({
  search: "report",
  searchType: SearchTypes.Keyword,
  
    creationDateRange: { from: '2024-01-01' }
  });
```

**Issue**: Want semantic + keyword **Solution**: Use hybrid search (combines both)

```typescript
// ✓ Best of both worlds
await graphlit.queryContents({
  search: "Project Alpha status",
  searchType: SearchTypes.Hybrid  // Recommended
});
```

**Issue**: Special characters breaking search **Solution**: Use quotes for exact phrase

```typescript
// ✓ Escape with quotes
await graphlit.queryContents({
  search: '"C++ programming guide"',
  searchType: SearchTypes.Keyword
});
```

### Production Example

```typescript
async function keywordSearch(query: string) {
  console.log(`\n=== KEYWORD SEARCH ===`);
  console.log(`Query: "${query}"`);
  
  const startTime = Date.now();
  
  const results = await graphlit.queryContents({
    search: query,
    searchType: SearchTypes.Keyword,
    limit: 20
  });
  
  const elapsed = Date.now() - startTime;
  
  console.log(`\nFound ${results.contents.results.length} results in ${elapsed}ms`);
  
  // Group by content type
  const byType = new Map<string, number>();
  results.contents.results.forEach(content => {
    byType.set(content.type, (byType.get(content.type) || 0) + 1);
  });
  
  console.log(`\n Results by Type:`);
  byType.forEach((count, type) => {
    console.log(`   ${type}: ${count}`);
  });
  
  // Top results
  console.log(`\n🔝 Top 5 Results:`);
  results.contents.results.slice(0, 5).forEach((content, index) => {
    console.log(`\n${index + 1}. ${content.name}`);
    console.log(`   Type: ${content.type}`);
    console.log(`   Relevance: ${(content.relevance * 100).toFixed(1)}%`);
    console.log(`   Created: ${new Date(content.creationDate).toLocaleDateString()}`);
    
    // Show snippet if available
    if (content.markdown) {
      const snippet = content.markdown
        .split('\n')
        .find(line => line.toLowerCase().includes(query.toLowerCase()));
      
      if (snippet) {
        console.log(`   Snippet: "${snippet.trim().substring(0, 100)}..."`);
      }
    }
  });
  
  // Performance analysis
  console.log(`\n⚡ Performance:`);
  console.log(`   Query time: ${elapsed}ms`);
  console.log(`   Results per ms: ${(results.contents.results.length / elapsed).toFixed(2)}`);
}

// Usage examples
await keywordSearch("Project Alpha");
await keywordSearch("kirk@graphlit.com");
await keywordSearch("PROJ-1234");
await keywordSearch('"quarterly earnings report"');
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.graphlit.dev/api-guides/use-cases/content/content-search-keyword-explained.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
