Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions builder/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,15 @@ function generateIndexKeyEncoding(type) {
const component = type.keyEncoding[i]

const keyType = type.builder.schema.types.get(`@${type.namespace}/${component}`)

if (keyType?.isArray) str += ' c.array('

if (keyType?.isEnum) str += ' IndexEncoder.UINT'
else if (keyType?.isArray) str += IndexTypeMap.get(keyType.type.fqn)
else str += ' ' + IndexTypeMap.get(component)

if (keyType?.isArray) str += ')'

if (i !== type.keyEncoding.length - 1) str += ',\n'
else str += '\n'
}
Expand Down
47 changes: 47 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -548,3 +548,50 @@ test('enum as key type', async function ({ create, bee }, t) {

await db.close()
})

test('array as key type', async function ({ create, bee }, t) {
const db = await create({ fixture: 8 })

await db.insert('@db/books', {
title: 'Brave New World',
tags: ['science fiction', 'dystopian', 'ficton']
})
await db.insert('@db/books', {
title: 'Anathem',
tags: ['science fiction', 'philosophy', 'math', 'ficton']
})
await db.flush()

{
const book = await db.get('@db/books', {
title: 'Brave New World',
tags: ['science fiction', 'dystopian', 'ficton']
})
t.alike(book, { title: 'Brave New World', tags: ['science fiction', 'dystopian', 'ficton'] })
}
{
const distopianScifi = await db
.find('@db/books-by-tag', {
lte: { tags: ['science fiction', 'dystopian', 'ficton'] },
gte: { tags: ['science fiction', 'dystopian', 'ficton'] }
})
.toArray()
t.alike(distopianScifi, [
{ title: 'Brave New World', tags: ['science fiction', 'dystopian', 'ficton'] }
])
}
{
const scifi = await db
.find('@db/books-by-tag', {
lte: { tags: ['science fiction', 'z', null, null] },
gte: { tags: ['science fiction'] }
})
.toArray()
t.alike(scifi, [
{ title: 'Brave New World', tags: ['science fiction', 'dystopian', 'ficton'] },
{ title: 'Anathem', tags: ['science fiction', 'philosophy', 'math', 'ficton'] }
])
}

await db.close()
})
51 changes: 51 additions & 0 deletions test/fixtures/builders/8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const HyperDB = require('../../../builder')
const Hyperschema = require('hyperschema')
const path = require('path')

const SCHEMA_DIR = path.join(__dirname, '../generated/8/hyperschema')
const DB_DIR = path.join(__dirname, '../generated/8/hyperdb')

const schema = Hyperschema.from(SCHEMA_DIR)

const dbSchema = schema.namespace('db')

dbSchema.register({
name: 'tags',
type: 'string',
array: true
})

dbSchema.register({
name: 'book',
fields: [
{
name: 'title',
type: 'string',
required: true
},
{
name: 'tags',
type: '@db/tags',
required: true
}
]
})

Hyperschema.toDisk(schema)

const db = HyperDB.from(SCHEMA_DIR, DB_DIR)
const testDb = db.namespace('db')

testDb.collections.register({
name: 'books',
schema: '@db/book',
key: ['title', 'tags']
})

testDb.indexes.register({
name: 'books-by-tag',
collection: '@db/books',
key: ['tags']
})

HyperDB.toDisk(db)
1 change: 1 addition & 0 deletions test/fixtures/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ require('./builders/4.js')
require('./builders/5.js')
require('./builders/6.js')
require('./builders/7.js')
require('./builders/8.js')
37 changes: 37 additions & 0 deletions test/fixtures/generated/8/hyperdb/db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": 1,
"offset": 0,
"schema": [
{
"name": "books",
"namespace": "db",
"id": 0,
"type": 1,
"version": 1,
"versionField": null,
"indexes": [
"@db/books-by-tag"
],
"schema": "@db/book",
"derived": false,
"key": [
"title",
"tags"
],
"trigger": null
},
{
"name": "books-by-tag",
"namespace": "db",
"id": 1,
"type": 2,
"version": 1,
"collection": "@db/books",
"unique": false,
"deprecated": false,
"key": [
"tags"
]
}
]
}
161 changes: 161 additions & 0 deletions test/fixtures/generated/8/hyperdb/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// This file is autogenerated by the hyperdb compiler
/* eslint-disable camelcase */

const { IndexEncoder, c, b4a } = require('hyperdb/runtime')
const { version, getEncoding, setVersion } = require('./messages.js')

const versions = { schema: version, db: 1 }

// '@db/books' collection key
const collection0_key = new IndexEncoder([
IndexEncoder.STRING,
c.array(IndexEncoder.STRING)
], { prefix: 0 })

function collection0_indexify (record) {
const arr = []

const a0 = record.title
if (a0 === undefined) return arr
arr.push(a0)

const a1 = record.tags
if (a1 === undefined) return arr
arr.push(a1)

return arr
}

// '@db/books' value encoding
const collection0_enc = getEncoding('@db/book/hyperdb#0')

// '@db/books' reconstruction function
function collection0_reconstruct (schemaVersion, keyBuf, valueBuf) {
const key = collection0_key.decode(keyBuf)
setVersion(schemaVersion)
const state = { start: 0, end: valueBuf.byteLength, buffer: valueBuf }
const type = c.uint.decode(state)
if (type !== 0) throw new Error('Unknown collection type: ' + type)
collection0.decodedVersion = c.uint.decode(state)
const record = collection0_enc.decode(state)
record.title = key[0]
record.tags = key[1]
return record
}
// '@db/books' key reconstruction function
function collection0_reconstruct_key (keyBuf) {
const key = collection0_key.decode(keyBuf)
return {
title: key[0],
tags: key[1]
}
}

// '@db/books'
const collection0 = {
name: '@db/books',
id: 0,
version: 1,
encodeKey (record) {
const key = [record.title, record.tags]
return collection0_key.encode(key)
},
encodeKeyRange ({ gt, lt, gte, lte } = {}) {
return collection0_key.encodeRange({
gt: gt ? collection0_indexify(gt) : null,
lt: lt ? collection0_indexify(lt) : null,
gte: gte ? collection0_indexify(gte) : null,
lte: lte ? collection0_indexify(lte) : null
})
},
encodeValue (schemaVersion, collectionVersion, record) {
setVersion(schemaVersion)
const state = { start: 0, end: 2, buffer: null }
collection0_enc.preencode(state, record)
state.buffer = b4a.allocUnsafe(state.end)
state.buffer[state.start++] = 0
state.buffer[state.start++] = collectionVersion
collection0_enc.encode(state, record)
return state.buffer
},
trigger: null,
reconstruct: collection0_reconstruct,
reconstructKey: collection0_reconstruct_key,
indexes: [],
decodedVersion: 0
}

// '@db/books-by-tag' collection key
const index1_key = new IndexEncoder([
c.array(IndexEncoder.STRING),
IndexEncoder.STRING,
c.array(IndexEncoder.STRING)
], { prefix: 1 })

function index1_indexify (record) {
const arr = []

const a0 = record.tags
if (a0 === undefined) return arr
arr.push(a0)

const a1 = record.title
if (a1 === undefined) return arr
arr.push(a1)

const a2 = record.tags
if (a2 === undefined) return arr
arr.push(a2)

return arr
}

// '@db/books-by-tag'
const index1 = {
name: '@db/books-by-tag',
version: 1,
id: 1,
encodeKey (record) {
return index1_key.encode(index1_indexify(record))
},
encodeKeyRange ({ gt, lt, gte, lte } = {}) {
return index1_key.encodeRange({
gt: gt ? index1_indexify(gt) : null,
lt: lt ? index1_indexify(lt) : null,
gte: gte ? index1_indexify(gte) : null,
lte: lte ? index1_indexify(lte) : null
})
},
encodeValue: (record) => index1.collection.encodeKey(record),
encodeIndexKeys (record, context) {
return [index1_key.encode([record.tags, record.title, record.tags])]
},
reconstruct: (keyBuf, valueBuf) => valueBuf,
offset: collection0.indexes.length,
collection: collection0
}
collection0.indexes.push(index1)

const collections = [
collection0
]

const indexes = [
index1
]

module.exports = { versions, collections, indexes, resolveCollection, resolveIndex }

function resolveCollection (name) {
switch (name) {
case '@db/books': return collection0
default: return null
}
}

function resolveIndex (name) {
switch (name) {
case '@db/books-by-tag': return index1
default: return null
}
}
Loading