feat(core, tests): add standalone getStatistics function and improve storage configuration
- **Core**: Introduced a new `getStatistics` utility function in `statistics.ts` for fetching database statistics at the root level of the library. Enhanced `BrainyData` methods to ensure metadata includes `id` field and refined statistics calculations, excluding verbs from the noun count. - **Tests**: Added comprehensive test coverage in `statistics.test.ts` for the new utility function, validating proper error handling, statistics accuracy, and consistent results between instance methods and standalone function. - **Storage Config**: Enabled dynamic support for AWS S3, Cloudflare R2, and Google Cloud Storage in web service configuration, utilizing environment variables for adapter setup. Addressed a race condition in `FileSystemStorage` initialization by deferring path module imports. **Purpose**: Enhance database analytics by introducing a reusable `getStatistics` function, improve flexibility in storage configuration, and ensure robust testing for reliability and accuracy.
This commit is contained in:
parent
b8f10ba39a
commit
2322b53a0c
13 changed files with 346 additions and 61 deletions
|
|
@ -342,4 +342,52 @@ describe('Brainy Core Functionality', () => {
|
|||
expect(results[0].metadata?.id).toBe('known')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Database Statistics', () => {
|
||||
it('should provide accurate statistics about the database', async () => {
|
||||
const data = new brainy.BrainyData({
|
||||
dimensions: 3,
|
||||
metric: 'euclidean'
|
||||
})
|
||||
|
||||
await data.init()
|
||||
await data.clear() // Clear any existing data
|
||||
|
||||
// Add some vectors (nouns)
|
||||
await data.add([1, 0, 0], { id: 'v1', label: 'x-axis' })
|
||||
await data.add([0, 1, 0], { id: 'v2', label: 'y-axis' })
|
||||
await data.add([0, 0, 1], { id: 'v3', label: 'z-axis' })
|
||||
|
||||
// Add some connections (verbs)
|
||||
await data.connect('v1', 'v2', 'related_to')
|
||||
await data.connect('v2', 'v3', 'related_to')
|
||||
|
||||
// Get statistics
|
||||
const stats = await data.getStatistics()
|
||||
|
||||
// Debug: Log all nouns in the database
|
||||
const allNouns = await data.getAllNouns()
|
||||
console.log('All nouns in database:', allNouns.map(n => n.id))
|
||||
|
||||
// Debug: Log all verbs in the database
|
||||
const allVerbs = await data.getAllVerbs()
|
||||
console.log('All verbs in database:', allVerbs.map(v => v.id))
|
||||
|
||||
// Debug: Log the verb IDs set used in getStatistics
|
||||
const verbIds = new Set(allVerbs.map(verb => verb.id))
|
||||
console.log('Verb IDs set:', Array.from(verbIds))
|
||||
|
||||
// Verify statistics
|
||||
expect(stats).toBeDefined()
|
||||
expect(stats).toHaveProperty('nounCount')
|
||||
expect(stats).toHaveProperty('verbCount')
|
||||
expect(stats).toHaveProperty('metadataCount')
|
||||
expect(stats).toHaveProperty('hnswIndexSize')
|
||||
|
||||
// Verify counts
|
||||
expect(stats.nounCount).toBe(3)
|
||||
expect(stats.verbCount).toBe(2)
|
||||
expect(stats.hnswIndexSize).toBe(3)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
76
tests/statistics.test.ts
Normal file
76
tests/statistics.test.ts
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Statistics Functionality Tests
|
||||
* Tests the getStatistics function as a consumer would use it
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'vitest'
|
||||
|
||||
describe('Brainy Statistics Functionality', () => {
|
||||
let brainy: any
|
||||
|
||||
beforeAll(async () => {
|
||||
// Load brainy library as a consumer would
|
||||
brainy = await import('../dist/unified.js')
|
||||
})
|
||||
|
||||
describe('Library Exports', () => {
|
||||
it('should export getStatistics function at the root level', () => {
|
||||
expect(brainy.getStatistics).toBeDefined()
|
||||
expect(typeof brainy.getStatistics).toBe('function')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getStatistics Functionality', () => {
|
||||
it('should retrieve statistics from a BrainyData instance', async () => {
|
||||
// Create a BrainyData instance
|
||||
const data = new brainy.BrainyData({
|
||||
dimensions: 3,
|
||||
metric: 'euclidean'
|
||||
})
|
||||
|
||||
await data.init()
|
||||
await data.clear() // Clear any existing data
|
||||
|
||||
// Add some test data
|
||||
await data.add([1, 0, 0], { id: 'v1', label: 'x-axis' })
|
||||
await data.add([0, 1, 0], { id: 'v2', label: 'y-axis' })
|
||||
await data.add([0, 0, 1], { id: 'v3', label: 'z-axis' })
|
||||
|
||||
// Add a verb
|
||||
await data.addVerb('v1', 'v2', [0.5, 0.5, 0], { type: 'connected_to' })
|
||||
|
||||
// Get statistics using the standalone function
|
||||
const stats = await brainy.getStatistics(data)
|
||||
|
||||
// Verify statistics
|
||||
expect(stats).toBeDefined()
|
||||
expect(stats.nounCount).toBe(3)
|
||||
expect(stats.verbCount).toBe(1)
|
||||
expect(stats.metadataCount).toBe(3) // Each noun has metadata
|
||||
expect(stats.hnswIndexSize).toBe(3)
|
||||
})
|
||||
|
||||
it('should throw an error when no instance is provided', async () => {
|
||||
await expect(brainy.getStatistics()).rejects.toThrow('BrainyData instance must be provided')
|
||||
})
|
||||
|
||||
it('should match the instance method results', async () => {
|
||||
// Create a BrainyData instance
|
||||
const data = new brainy.BrainyData({
|
||||
dimensions: 3
|
||||
})
|
||||
|
||||
await data.init()
|
||||
|
||||
// Add some test data
|
||||
await data.add([1, 1, 1], { id: 'test1' })
|
||||
|
||||
// Get statistics using both methods
|
||||
const instanceStats = await data.getStatistics()
|
||||
const functionStats = await brainy.getStatistics(data)
|
||||
|
||||
// Verify they match
|
||||
expect(functionStats).toEqual(instanceStats)
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue