Skip to main content

Overview

The Data API allows you to store custom data in collections with powerful semantic search capabilities using vector embeddings. Returns DataEntryInstance objects with direct property access.
import { Data } from 'lua-cli';

// Create with search indexing - returns DataEntryInstance
const entry = await Data.create('movies', {
  title: 'Inception',
  director: 'Christopher Nolan'
}, 'Inception Christopher Nolan sci-fi thriller dreams');

// Direct property access
console.log(entry.title);     // "Inception"
console.log(entry.director);  // "Christopher Nolan"
console.log(entry.id);        // "entry_abc123"

// Instance methods
await entry.update({ rating: 9.5 }, 'inception nolan 9.5 rating');
await entry.save();
await entry.delete();

// Semantic search - returns array of DataEntryInstance
const results = await Data.search('movies', 'mind-bending thriller', 10, 0.7);

// Direct array methods and property access
results.forEach(entry => {
  console.log(`${entry.title}: ${entry.score * 100}% match`);
});

const highScoring = results.filter(entry => entry.score > 0.8);
const titles = results.map(entry => entry.title);

Direct Access

Access entry.title not entry.data.title

Search Scores

Results include relevance scores

Instance Methods

Built-in update(), save(), and delete()

Array Methods

Use .map(), .filter() on search results

Key Features

Custom Collections

Store any JSON data in named collections

Vector Search

Semantic similarity search using AI embeddings

Flexible Schema

No fixed schema - store any structure

Filtering

Query by field values with operators

Methods

create()

Create a new entry in a collection.
Data.create(
  collectionName: string,
  data: object,
  searchText?: string
): Promise<Entry>
collectionName
string
required
Name of the collection (e.g., ‘movies’, ‘customers’, ‘articles’)
data
object
required
Any JSON-serializable object to store
searchText
string
Text to index for vector search. Include all searchable content.
Returns:
{
  id: string;
  data: object;
  createdAt: number;
  updatedAt: number;
  searchText?: string;
}
Example:
const movie = await Data.create('movies', {
  title: 'The Matrix',
  year: 1999,
  director: 'Wachowski Sisters',
  genre: 'Sci-Fi',
  rating: 8.7
}, 'The Matrix 1999 Wachowski sci-fi action cyberpunk reality virtual');

console.log(movie.id); // "entry_abc123"
Semantic search using vector embeddings.
Data.search(
  collectionName: string,
  query: string,
  limit?: number,
  scoreThreshold?: number
): Promise<DataEntryInstance[]>
collectionName
string
required
Name of the collection to search
query
string
required
Search query (natural language)
limit
number
default:10
Maximum number of results to return
scoreThreshold
number
Minimum similarity score (0-1). Higher = more similar.
Returns: Array of DataEntryInstance objects, each with a score property. Similarity Scores:
  • 1.0 = Perfect match
  • 0.8-0.9 = Very similar
  • 0.6-0.7 = Somewhat similar
  • <0.6 = Low similarity
Example:
// Finds movies even if query doesn't match exact words!
const results = await Data.search('movies', 'mind-bending thriller', 5, 0.7);

results.forEach(entry => {
  console.log(`${entry.title} - Relevance: ${entry.score}`);
});
// Output:
// Inception - Relevance: 0.92
// The Matrix - Relevance: 0.85
// Interstellar - Relevance: 0.78

get()

Retrieve entries with optional filtering and pagination.
Data.get(
  collectionName: string,
  filter?: object,
  page?: number,
  limit?: number
): Promise<GetResult>
collectionName
string
required
Name of the collection
filter
object
Filter criteria (MongoDB-style queries)
page
number
default:1
Page number (1-indexed)
limit
number
default:10
Items per page
Returns:
{
  data: Entry[];
  pagination: {
    currentPage: number;
    totalPages: number;
    totalCount: number;
    limit: number;
    hasNextPage: boolean;
    hasPrevPage: boolean;
    nextPage: number | null;
    prevPage: number | null;
  };
}
Examples:
// Get all
const all = await Data.get('movies');

// With pagination
const page2 = await Data.get('movies', {}, 2, 20);

// With filter
const recent = await Data.get('movies', {
  year: { $gte: 2020 }
});

// Complex filter
const sciFi = await Data.get('movies', {
  genre: 'Sci-Fi',
  rating: { $gte: 8.0 },
  year: { $gte: 2000, $lte: 2020 }
});

getEntry()

Retrieve a specific entry by ID.
Data.getEntry(
  collectionName: string,
  entryId: string
): Promise<Entry>
Example:
const movie = await Data.getEntry('movies', 'entry_abc123');
console.log(movie.data.title); // "Inception"

update()

Update an existing entry.
Data.update(
  collectionName: string,
  entryId: string,
  data: object,
  searchText?: string
): Promise<UpdateResponse>
collectionName
string
required
Name of the collection
entryId
string
required
ID of the entry to update
data
object
required
Data to merge with existing entry
searchText
string
Optional new text for vector search indexing
Returns:
{
  status: string;   // "success"
  message: string;  // "Custom data entry updated"
}
Example:
// Update data and search text
const result = await Data.update('movies', 'entry_abc123', {
  rating: 8.8,      // Updated rating
  awards: ['Oscar'] // New field
}, 'Inception Christopher Nolan oscar winner');

// Update data only
await Data.update('movies', 'entry_abc123', {
  views: 1500
});
Updates merge with existing data. Only the specified fields are updated; other fields are preserved.

DataEntryInstance Methods

When you retrieve or create data entries, you get a DataEntryInstance object with convenient instance methods.

save()

Save the current state of the data entry to the server. This is a convenience method that persists all changes made to the entry.
entry.save(searchText?: string): Promise<boolean>
searchText
string
Optional new text for vector search indexing
Returns: Promise resolving to true if successful Example:
const entry = await Data.getEntry('movies', 'entry_abc123');

// Modify properties directly
entry.title = "Inception";
entry.rating = 9.0;
entry.year = 2010;

// Save all changes at once
await entry.save();

// Or save with updated search text
await entry.save('Inception 2010 Nolan sci-fi thriller dreams');
The save() method provides a simpler workflow - modify properties then save, rather than calling Data.update() with the collection name and entry ID.

update() (Instance Method)

Update the entry using the instance method.
entry.update(data: object, searchText?: string): Promise<object>
Returns: Promise resolving to the updated data object Example:
const entry = await Data.getEntry('movies', 'entry_abc123');

// Update with new search text
const updatedData = await entry.update(
  { rating: 9.5, review: 'Mind-bending masterpiece' }, 
  'inception nolan 9.5 rating masterpiece'
);

console.log(updatedData.rating); // 9.5

// Update data only (keeps existing search text)
await entry.update({ views: 1000 });

delete() (Instance Method)

Delete the entry using the instance method.
entry.delete(): Promise<void>
Example:
const entry = await Data.getEntry('movies', 'entry_abc123');
await entry.delete();

Static Methods

delete()

Delete an entry using the static method.
Data.delete(
  collectionName: string,
  entryId: string
): Promise<void>
Example:
await Data.delete('movies', 'entry_abc123');

Use Cases

Knowledge Base

export class CreateArticleTool implements LuaTool {
  async execute(input: any) {
    // Create searchable article
    const article = await Data.create('kb_articles', {
      title: input.title,
      content: input.content,
      category: input.category,
      tags: input.tags
    }, `${input.title} ${input.content} ${input.tags.join(' ')}`);
    
    return { articleId: article.id };
  }
}

export class SearchArticlesTool implements LuaTool {
  async execute(input: any) {
    // Semantic search
    const results = await Data.search(
      'kb_articles',
      input.query,
      10,
      0.7
    );
    
    return {
      articles: results.data.map(entry => ({
        id: entry.id,
        title: entry.data.title,
        content: entry.data.content.substring(0, 200),
        relevance: Math.round(entry.score * 100) + '%'
      }))
    };
  }
}

Customer CRM

export class CreateCustomerTool implements LuaTool {
  async execute(input: any) {
    const customer = await Data.create('customers', {
      name: input.name,
      email: input.email,
      company: input.company,
      status: 'active',
      createdAt: new Date().toISOString()
    }, `${input.name} ${input.company} ${input.email}`);
    
    // Log interaction
    await Data.create('interactions', {
      customerId: customer.id,
      type: 'created',
      notes: 'Initial contact',
      timestamp: new Date().toISOString()
    });
    
    return { customerId: customer.id };
  }
}

Task Management

export class CreateTaskTool implements LuaTool {
  async execute(input: any) {
    const task = await Data.create('tasks', {
      title: input.title,
      description: input.description,
      status: 'pending',
      priority: input.priority,
      createdAt: new Date().toISOString()
    }, `${input.title} ${input.description}`);
    
    return { taskId: task.id };
  }
}

export class SearchTasksTool implements LuaTool {
  async execute(input: any) {
    const results = await Data.search('tasks', input.query, 20, 0.6);
    
    return {
      tasks: results.data.map(entry => ({
        id: entry.id,
        ...entry.data,
        relevance: entry.score
      }))
    };
  }
}

Filter Operators

The Data API supports MongoDB-style filter operators:
// Comparison
{ age: { $eq: 25 } }       // Equal
{ age: { $ne: 25 } }       // Not equal
{ age: { $gt: 25 } }       // Greater than
{ age: { $gte: 25 } }      // Greater than or equal
{ age: { $lt: 25 } }       // Less than
{ age: { $lte: 25 } }      // Less than or equal

// Logical
{ $and: [{ age: { $gte: 18 } }, { age: { $lte: 65 } }] }
{ $or: [{ status: 'active' }, { status: 'pending' }] }

// Array
{ tags: { $in: ['urgent', 'important'] } }
{ tags: { $nin: ['spam', 'archived'] } }

// Existence
{ email: { $exists: true } }

Best Practices

Include all searchable content in searchText:
const searchText = [
  item.title,
  item.description,
  item.category,
  item.tags.join(' '),
  item.author
].filter(Boolean).join(' ');

await Data.create('items', item, searchText);
  • 0.8+: High precision, few results
  • 0.7: Balanced (recommended default)
  • 0.6: More results, lower precision
  • <0.6: May return irrelevant results
Use consistent field names across entries:
// ✅ Good - Consistent
await Data.create('items', {
  name: 'Item 1',
  price: 10.99,
  inStock: true
});

// ❌ Bad - Inconsistent
await Data.create('items', {
  title: 'Item 2',  // Different field name
  cost: 15.99,      // Different field name
  available: true   // Different field name
});
Track when entries are created/modified:
await Data.create('items', {
  ...data,
  createdAt: new Date().toISOString(),
  updatedAt: new Date().toISOString()
});

Vector Search Tips

Vector search uses AI to understand meaning, not just match keywords.Example:
  • Query: “affordable laptop for students”
  • Finds: “budget-friendly notebook for college”
  • Even though no words match exactly!

Next Steps