sync: temporal-awareness from private repo
- Surface created_at, updated_at, event_date in queryNodes + queryDimensionNodes responses - Add date range filters (createdAfter/Before, eventAfter/Before) to NodeFilters + nodeService.getNodes() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
0095641278
commit
9954792b1d
@@ -10,15 +10,16 @@ export class NodeService {
|
||||
// PostgreSQL path removed in SQLite-only consolidation
|
||||
|
||||
private async getNodesSQLite(filters: NodeFilters = {}): Promise<Node[]> {
|
||||
const { dimensions, search, limit = 100, offset = 0, sortBy, dimensionsMatch = 'any' } = filters;
|
||||
const { dimensions, search, limit = 100, offset = 0, sortBy, dimensionsMatch = 'any',
|
||||
createdAfter, createdBefore, eventAfter, eventBefore } = filters;
|
||||
const sqlite = getSQLiteClient();
|
||||
|
||||
// Use nodes_v view for array-like dimensions behavior (exclude embedding BLOB for performance)
|
||||
let query = `
|
||||
SELECT n.id, n.title, n.description, n.notes, n.link, n.event_date, n.metadata, n.chunk,
|
||||
SELECT n.id, n.title, n.description, n.notes, n.link, n.event_date, n.metadata, n.chunk,
|
||||
n.chunk_status, n.embedding_updated_at, n.embedding_text,
|
||||
n.created_at, n.updated_at,
|
||||
COALESCE((SELECT JSON_GROUP_ARRAY(d.dimension)
|
||||
COALESCE((SELECT JSON_GROUP_ARRAY(d.dimension)
|
||||
FROM node_dimensions d WHERE d.node_id = n.id), '[]') as dimensions_json,
|
||||
(SELECT COUNT(*) FROM edges WHERE from_node_id = n.id OR to_node_id = n.id) as edge_count
|
||||
FROM nodes n
|
||||
@@ -47,15 +48,33 @@ export class NodeService {
|
||||
}
|
||||
}
|
||||
|
||||
// Text search in title, description, and content (SQLite LIKE with COLLATE NOCASE)
|
||||
// Text search in title, description, and notes (SQLite LIKE with COLLATE NOCASE)
|
||||
if (search) {
|
||||
query += ` AND (n.title LIKE ? COLLATE NOCASE OR n.description LIKE ? COLLATE NOCASE OR n.notes LIKE ? COLLATE NOCASE)`;
|
||||
params.push(`%${search}%`, `%${search}%`, `%${search}%`);
|
||||
}
|
||||
|
||||
// Temporal filters
|
||||
if (createdAfter) {
|
||||
query += ` AND n.created_at >= ?`;
|
||||
params.push(createdAfter);
|
||||
}
|
||||
if (createdBefore) {
|
||||
query += ` AND n.created_at < ?`;
|
||||
params.push(createdBefore);
|
||||
}
|
||||
if (eventAfter) {
|
||||
query += ` AND n.event_date >= ?`;
|
||||
params.push(eventAfter);
|
||||
}
|
||||
if (eventBefore) {
|
||||
query += ` AND n.event_date < ?`;
|
||||
params.push(eventBefore);
|
||||
}
|
||||
|
||||
// Sorting logic
|
||||
if (search) {
|
||||
// For search queries, prioritize by relevance: exact title → starts with → contains in title → description → content
|
||||
// For search queries, prioritize by relevance: exact title → starts with → contains in title → description → notes
|
||||
query += ` ORDER BY
|
||||
CASE WHEN LOWER(n.title) = LOWER(?) THEN 1 ELSE 6 END,
|
||||
CASE WHEN LOWER(n.title) LIKE LOWER(?) THEN 2 ELSE 6 END,
|
||||
@@ -68,7 +87,7 @@ export class NodeService {
|
||||
`${search}%`, // Starts with search term
|
||||
`%${search}%`, // Contains in title
|
||||
`%${search}%`, // Contains in description
|
||||
`%${search}%` // Contains in content
|
||||
`%${search}%` // Contains in notes
|
||||
);
|
||||
} else if (sortBy === 'edges') {
|
||||
// Sort by edge count (most connected first)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { formatNodeForChat } from '../infrastructure/nodeFormatter';
|
||||
import type { Node } from '@/types/database';
|
||||
|
||||
export const queryDimensionNodesTool = tool({
|
||||
description: 'Query all nodes within a specific dimension. Returns nodes sorted by edge count (most connected first).',
|
||||
description: 'Get nodes in a dimension, sorted by connection count.',
|
||||
inputSchema: z.object({
|
||||
dimension: z.string().describe('The dimension name to query nodes from'),
|
||||
limit: z.number().optional().default(20).describe('Maximum number of nodes to return (default: 20)'),
|
||||
@@ -43,6 +43,9 @@ export const queryDimensionNodesTool = tool({
|
||||
}),
|
||||
edgeCount: node.edge_count || 0,
|
||||
dimensions: node.dimensions || [],
|
||||
created_at: node.created_at,
|
||||
updated_at: node.updated_at,
|
||||
event_date: node.event_date ?? null,
|
||||
};
|
||||
|
||||
if (includeContent && node.notes) {
|
||||
|
||||
@@ -10,7 +10,11 @@ export const queryNodesTool = tool({
|
||||
filters: z.object({
|
||||
dimensions: z.array(z.string()).describe('Filter by dimensions (e.g., ["research", "ai", "technology"]). Replaces old type/stage filtering.').optional(),
|
||||
search: z.string().describe('Search term to match against title or notes').optional(),
|
||||
limit: z.number().min(1).max(50).default(10).describe('Maximum number of results to return')
|
||||
limit: z.number().min(1).max(50).default(10).describe('Maximum number of results to return'),
|
||||
createdAfter: z.string().optional().describe('ISO date (YYYY-MM-DD). Only return nodes created on or after this date.'),
|
||||
createdBefore: z.string().optional().describe('ISO date (YYYY-MM-DD). Only return nodes created before this date.'),
|
||||
eventAfter: z.string().optional().describe('ISO date (YYYY-MM-DD). Only return nodes with event_date on or after this date.'),
|
||||
eventBefore: z.string().optional().describe('ISO date (YYYY-MM-DD). Only return nodes with event_date before this date.'),
|
||||
}).optional()
|
||||
}),
|
||||
execute: async ({ filters = {} }) => {
|
||||
@@ -47,6 +51,9 @@ export const queryNodesTool = tool({
|
||||
id: node.id,
|
||||
title: node.title,
|
||||
dimensions: node.dimensions || [],
|
||||
created_at: node.created_at,
|
||||
updated_at: node.updated_at,
|
||||
event_date: node.event_date ?? null,
|
||||
formatted_display: formatted,
|
||||
}],
|
||||
count: 1,
|
||||
@@ -65,7 +72,11 @@ export const queryNodesTool = tool({
|
||||
const nodesPromise: Promise<Node[] | undefined> = nodeService.getNodes({
|
||||
limit,
|
||||
dimensions: filters.dimensions,
|
||||
search: filters.search
|
||||
search: filters.search,
|
||||
createdAfter: filters.createdAfter,
|
||||
createdBefore: filters.createdBefore,
|
||||
eventAfter: filters.eventAfter,
|
||||
eventBefore: filters.eventBefore,
|
||||
});
|
||||
|
||||
const nodes = await Promise.race<Node[] | undefined>([nodesPromise, timeoutPromise]);
|
||||
@@ -85,6 +96,9 @@ export const queryNodesTool = tool({
|
||||
id: node.id,
|
||||
title: node.title,
|
||||
dimensions: node.dimensions || [],
|
||||
created_at: node.created_at,
|
||||
updated_at: node.updated_at,
|
||||
event_date: node.event_date ?? null,
|
||||
formatted_display: formatted
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3,19 +3,18 @@ export interface Node {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
notes?: string; // Consolidated content from description + abstract + notes
|
||||
notes?: string; // User's notes/thoughts about this node
|
||||
link?: string;
|
||||
event_date?: string;
|
||||
event_date?: string | null; // When the thing actually happened (ISO 8601)
|
||||
dimensions: string[]; // Flexible dimensions replacing type + stage + segment + tags
|
||||
embedding?: Buffer; // Node-level embedding (BLOB data)
|
||||
chunk?: string;
|
||||
metadata?: any; // Flexible metadata storage from extras + chunk_status + sub_type
|
||||
metadata?: any; // Flexible metadata storage
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
// Legacy pin flag (read-only, slated for removal)
|
||||
edge_count?: number; // Derived count of edges, included in some queries
|
||||
|
||||
// Optional embedding fields (restored from migration)
|
||||
// Optional embedding fields
|
||||
embedding_updated_at?: string;
|
||||
embedding_text?: string;
|
||||
chunk_status?: 'not_chunked' | 'chunking' | 'chunked' | 'error' | null;
|
||||
@@ -72,6 +71,10 @@ export interface NodeFilters {
|
||||
offset?: number;
|
||||
sortBy?: 'updated' | 'edges' | 'created'; // Sort by updated_at, edge count, or created_at
|
||||
dimensionsMatch?: 'any' | 'all'; // 'any' = OR (default), 'all' = AND
|
||||
createdAfter?: string; // ISO date (YYYY-MM-DD) — nodes created on or after
|
||||
createdBefore?: string; // ISO date (YYYY-MM-DD) — nodes created before
|
||||
eventAfter?: string; // ISO date (YYYY-MM-DD) — nodes with event_date on or after
|
||||
eventBefore?: string; // ISO date (YYYY-MM-DD) — nodes with event_date before
|
||||
}
|
||||
|
||||
export interface ChunkData {
|
||||
@@ -118,6 +121,7 @@ export interface DatabaseError {
|
||||
export interface Dimension {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
icon?: string | null;
|
||||
is_priority: boolean;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user