Security News
Supply Chain Attack Detected in Solana's web3.js Library
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Nekdis is the temporary name for a proposal for redis-om that aims to improve the user experience and performance by providing an ODM-like naming scheme like the famous library mongoose for MongoDB
Right now the proposal includes almost every feature that redis-om already has (See: Missing Features) and introduces some like References.
The next steps for the proposal include:
node-redis
to improve its performance.RecordId
Nekdis is available on npm via the command
npm i nekdis
Nekdis already exports a global client but you can also create your own instance with the Client
class.
import { client } from "nekdis";
client.connect().then(() => {
console.log("Connected to redis");
});
import { Client } from "nekdis";
const client = new Client();
client.connect().then(() => {
console.log("Connected to redis");
});
The client provides a helper to build a schema without any extra steps.
import { client } from "nekdis";
const catSchema = client.schema({
name: { type: "string" }
});
The client also provides a helper to create a model.
import { client } from "nekdis";
const catModel = client.model("Cat", catSchema);
The model is what provides all the functions to manage your data on the database.
const aCat = catModel.createAndSave({
name: "Nozomi"
});
RecordId
This proposal introduces a new way to create unique ids called RecordId
.
RecordIds allow you to set prefixes and other properties to your id that is shared across all of the records.
There are 3 types of vss queries as said in the documentation
Lets use the following schema & model for the next examples
import { client } from "nekdis";
const testSchema = client.schema({
age: "number",
vec: "vector"
})
const testModel = client.model("Test", testSchema);
A note on the schema. Passing the string "vector"
will default to the following options:
const vectorDefaults = {
ALGORITHM: "FLAT",
// the vector type, nekdis calls it `vecType`
TYPE: "FLOAT32",
DIM: 128,
// nekdis calls it `distance`
DISTANCE_METRIC: "L2",
}
testModel.search().where("vec").eq((vector) => vector
.knn()
.from([2, 5, 7])
.return(8))
.returnAll();
// Generates the following query
// "*=>[KNN 8 @vec $BLOB]" PARAMS 2 BLOB \x02\x05\x07 DIALECT 2
testModel.search().where("age").between(18, 30)
.and("vec").eq((vector) => vector
.knn()
.from([2, 5, 7])
.return(8))
.returnAll();
// Generates the following query
// "((@age:[18 30]))=>[KNN 8 @vec $BLOB]" PARAMS 2 BLOB \x02\x05\x07 DIALECT 2
testModel.search().where("vec").eq((vector) => vector
.range(5)
.from([2, 5, 7]))
.returnAll();
// Generates the following query
// "((@vec:[VECTOR_RANGE 5 $BLOB]))" PARAMS 2 BLOB \x02\x05\x07 DIALECT 2
In this proposal you can create your own custom methods that will be added to the Model
, this methods are defined on the schema directly.
WARNING: Anonymous functions cannot be used when defining custom methods/functions
const albumSchema = client.schema({
artist: { type: "string", required: true },
name: { type: "text", required: true },
year: "number"
}, {
searchByName: async function (name: string) {
return await this.search().where("name").matches(name).returnAll();
}
})
const albumModel = client.model("Album", albumSchema);
const results = await albumModel.searchByName("DROP");
Nekdis allows you to add modules to the client, modules are something that adds extra functionality to the library, you pass in a class where the constructor will receive the client as its first argument.
Keep in mind that this might be more useful if you are creating your own instance of the client and exporting it because that way you will also get intellisense for the module.
import {type Client, client} from "nekdis";
class MyModule {
constructor(client: Client) {
// Do something
}
myFunction() {
// Do something
}
}
client.withModules({ name: "myMod", ctor: MyModule });
// Access it
client.myMod.myFunction()
This proposal adds some new data types and removes the string[]
& number[]
types.
Type | Description |
---|---|
string | A standard string that will be indexed as TAG |
number | A standard float64 number that will be indexed as NUMERIC |
bigint | A javascript BigInt that will be indexed as TAG |
boolean | A standard boolean that will be indexed as TAG |
text | A standard string that will be indexed as TEXT which allows for full text search |
date | This field will internally be indexed as NUMERIC , it gets saved as a Unix Epoch but you will be able to interact with it normally as it will be a Date when you access it |
point | This is an object containing a latitude and longitude and will be indexed as GEO |
array | Internally it will be indexed as the type given to the elements property which defaults to string |
object | This type allows you to nest forever using the properties property in the schema and what gets indexed are its properties, if none are given it will not be indexed not checked |
reference | When using this type you will be given a ReferenceArray which is a normal array with a reference method that you can pass in another document or a record id to it, references can be auto fetched but auto fetched references cannot be changed |
tuple | Tuples will be presented as per-index type safe arrays but they are dealt with in a different way. They will be indexed as static props so you can search on a specific element only, this also affects the query builder instead of where(arrayName) it will be where(arrayName.idx.prop) but this has working intellisense just like all the other fields so it shouldn't be an issue |
vector | A vector field that is an array but indexed as VECTOR |
This proposal includes the addition of 2 new shared properties and some unique ones
Property | Description |
---|---|
type | The type of the field |
optional | Defines whether the field is optional or not (this doesn't work if validation is disabled) |
default | Chose a default value for the field making so that it will always exist even if it isn't required |
index | Defines whether the field should be indexed or not (defaults to false ) |
sortable | Defines whether the field is sortable or not (note that this doesn't exist nor work on object fields & reference fields) |
Vector properties wont be documented here, check the types instead
Property | Type | Description |
---|---|---|
elements | array | Defines the type of the array |
elements | tuple | Even tho it has the same name this field is required in tuples and there are no ways to define infinite length tuples (just use normal arrays) |
separator | array | This defines the separator that will be used for arrays on hash fields |
properties | object | The properties the object contains, if this isn't defined the object wont be type checked nor indexed |
schema | reference | This is a required property when using references and it allows for intellisense to give the types on auto fetch and later on for certain type checking to also work as well |
literal | string | number | bigint | Make it so that the saved value has to be exactly one of the literal values |
caseSensitive | string | Defines whether the string is case sensitive or not |
phonetic | text | Choose the phonetic matcher the field will use |
weight | text | Declare the importance of the field |
in
operator for number search$id
alias3In this part of the document im going to cover how this proposal compares to the current redis-om (0.4.2) and the major differences.
In Nekdis the Client
does not provide any methods to interact directly with the database and its pretty much only used to store your models and handle the connection, however you can access the node-redis
client by accessing client.raw
.
The schema in Nekdis is just where you define the shape of your data while in redis-om it also takes care of creating indexes and some other internal bits.
With this comes the big question "Well, why not use just a plain object then", the simple answer to this question is ease of use but to explain it further, having the schema defined this way allows the library to internally check if there isn't anything missing and parse it so you are allowed to use the shorthand methods like field: "string"
, this approach also allows for you to define methods an options that will be passed down to the model down the road and not to mention that this is one of the only ways to have references working properly without affecting performance.
In redis-om you use a repository
to interact with the db by using methods like fetch
, save
and search
.
In Nekdis the model
is not that different but it allows you to add more functionality to it (see: Custom Methods) and overall gives more functionality out of the box.
In Nekdis you have what are called documents, this is just an abstraction to the data to allow better interaction with references and faster parsing.
At first this might look daunting compared to redis-om that now uses plain objects but i can assure you that there isn't that much of a difference, and i will give some examples to demonstrate it.
See, its just as easy
Nekdis | Redis-OM |
---|---|
|
|
This is where things start to be a bit different, even tho you can use a plain object that isn't recommended since it would just use more memory.
Nekdis | Nekdis with plain object | Redis-OM |
---|---|---|
|
|
|
Looking at search for the first time it is pretty much the same, the only difference is that equals
operations exist in every data type so a lot of times changing the data type in the schema wont break the query and the best part is that eq
, equals
and other operators like them support arrays (so they pretty much work like an in
operator).
Currently in redis-om you need to define a path for each field to define your nested objects, meanwhile in Nekdis they just work like normal js objects!
There are several advantages to this, two of the main ones being, faster serialization/deserialization and simpler to use, here is an example comparing both
Nekdis | Redis-OM |
---|---|
|
|
This is a simple program example that generates 30 random users with random ages and fetches the ones matching a certain age just to show the differences between the libraries
Nekdis | Redis-OM |
---|---|
|
|
equals
work just like itrequired
was addedThere were a lot of benchmarks made and they can be found here
Currently the deepMerge
function will take longer the more objects and nested objects you have, the idea i received is to do it all in one go by using a function to flatten it but im not sure yet on how to do it ↩
Is this really needed? From my point of view the complexity required to add this would outweigh any benefits from it specially on the type-level transformations ↩
This could be a nice addition but im still unsure if this should be added and needs to be further discussed ↩
FAQs
Object mapping, and more, for Redis and Node.js. Written in TypeScript.
We found that nekdis demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
A supply chain attack has been detected in versions 1.95.6 and 1.95.7 of the popular @solana/web3.js library.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.