All skills
upstash avatar

/upstash-redis-js

@35da719
by upstashupstash/skills27 stars
7

Work with the @upstash/redis TypeScript/JavaScript SDK, a serverless HTTP-based Redis client for Next.js, Vercel, Cloudflare Workers, edge runtimes, and Node.js. Use when adding a cache (cache-aside, write-through, TTL and expiration strategies), session storage and user sessions, a key-value store, leaderboards and rankings with sorted sets, counters, distributed locks, queues with lists, streams and consumer groups, sparse index-addressed arrays and ring buffers (ARSET, ARINSERT, ARRING, ARGREP, AROP), embeddings and nearest-neighbour vector search stored inside Redis (VECTOR commands via redis.vector, separate from @upstash/vector), JSON documents, pipelines and MULTI/EXEC transactions, Lua scripting, read replicas, or full-text search, typo-tolerant search, facets, aggregations, and search over Redis stream entries with Upstash Redis Search (different from regular FT.SEARCH; also available for TCP clients via @upstash/search-redis and @upstash/search-ioredis). Also use when migrating from ioredis or node-redis, when a Redis connection is needed from a serverless function without connection pooling, when integrating @upstash/ratelimit, or when the user says Redis cache, KV store, session store, serverless Redis, or Upstash Redis. Supports automatic serialization/deserialization of JavaScript types.

Use this Skill: https://skilld.dev/gh/upstash/skills/upstash-redis-js

This session only. Nothing lands on disk.

searchcommandsquerying.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Querying & Counting

Overview

Query documents from a search index using type-safe filters with support for pagination, sorting, field selection, scoring, and highlighting. Count matching documents efficiently without returning results.

Good For

  • Full-text search with fuzzy matching and phrase queries
  • Filtering by numeric ranges, dates, booleans, keywords
  • Paginated results with sorting
  • Highlighting search terms in results
  • Counting documents matching a filter

Examples

Basic Query

import { Redis, s } from "@upstash/redis";

const redis = Redis.fromEnv();

const index = await redis.search.createIndex({
  name: "products",
  prefix: "product:",
  dataType: "json",
  schema: s.object({
    name: s.string(),
    price: s.number("F64"),
    category: s.keyword(),
    inStock: s.boolean(),
  }),
});

// Insert data
await redis.json.set("product:1", "$", {
  name: "Gaming Laptop",
  price: 1299.99,
  category: "electronics",
  inStock: true,
});
await redis.json.set("product:2", "$", {
  name: "Wireless Mouse",
  price: 29.99,
  category: "electronics",
  inStock: true,
});
await redis.json.set("product:3", "$", {
  name: "Laptop Stand",
  price: 49.99,
  category: "accessories",
  inStock: false,
});
await index.waitIndexing();

// Query with filter and return data
const results = await index.query({
  filter: { category: { $eq: "electronics" } },
  select: { name: true, price: true },
});
// [
//   { key: "product:1", score: ..., data: { name: "Gaming Laptop", price: 1299.99 } },
//   { key: "product:2", score: ..., data: { name: "Wireless Mouse", price: 29.99 } },
// ]

Keys Only (No Data)

// Set select to {}
const keysOnly = await index.query({
  filter: { inStock: { $eq: true } },
  select: {},
});
// [{ key: "product:1", score: ... }, { key: "product:2", score: ... }]

Pagination

const page2 = await index.query({
  filter: { category: { $eq: "electronics" } },
  select: { name: true },
  limit: 10,
  offset: 10, // skip first 10 results
});

Sorting

const cheapest = await index.query({
  filter: { inStock: { $eq: true } },
  select: { name: true, price: true },
  orderBy: { price: "ASC" },
});

Score Function

// Rank by relevance with a score modifier
const results = await index.query({
  filter: { name: { $eq: "laptop" } },
  select: { name: true },
  scoreFunc: { field: "name", modifier: "log1p" },
});
// Available modifiers: log, log1p, log2p, ln, ln1p, ln2p, square, sqrt, reciprocal, none

Highlighting

const highlighted = await index.query({
  filter: { name: { $eq: "laptop" } },
  select: { name: true },
  highlight: {
    fields: ["name"],
    preTag: "<mark>", // optional, default <em>
    postTag: "</mark>", // optional, default </em>
  },
});
// data.name: "Gaming <mark>Laptop</mark>"

Filter Operators

Text Field Filters

// Exact substring match
{ name: { $eq: "laptop" } }

// Multiple values (OR)
{ name: { $in: ["laptop", "tablet"] } }

// Fuzzy matching (typo tolerance)
{ name: { $fuzzy: { value: "lapto", distance: 1 } } }

// Phrase matching (adjacent words with tolerance)
{ name: { $phrase: { value: "gaming laptop", slop: 1 } } }

// Regex pattern
{ name: { $regex: "lap.*" } }

// Smart matching (automatic fuzzy + phrase + term)
{ name: { $smart: "gaming laptop" } }

Numeric Field Filters

// Exact value
{ price: { $eq: 29.99 } }

// Range
{ price: { $gte: 10, $lte: 100 } }

// Greater/less than
{ price: { $gt: 50 } }
{ stock: { $lt: 10 } }

Boolean Field Filters

{
  inStock: {
    $eq: true;
  }
}
{
  inStock: {
    $in: [true, false];
  }
}

Date Field Filters

{ createdAt: { $gte: "2024-01-01", $lt: "2025-01-01" } }

Keyword Field Filters

// Exact match
{ category: { $eq: "electronics" } }

// Multiple values
{ category: { $in: ["electronics", "accessories"] } }

// Lexicographic range
{ category: { $gte: "a", $lt: "m" } }

Facet Field Filters

{
  brand: {
    $eq: "Apple";
  }
}
{
  brand: {
    $in: ["Apple", "Samsung"];
  }
}

Boolean Operators

Combine filters using boolean operators:

// AND - all conditions must match
{
  $and: [
    { category: { $eq: "electronics" } },
    { price: { $lte: 500 } },
  ]
}

// OR - any condition matches
{
  $or: [
    { category: { $eq: "electronics" } },
    { category: { $eq: "accessories" } },
  ]
}

// MUST + MUST NOT - require some, exclude others
{
  $must: [{ category: { $eq: "electronics" } }],
  $mustNot: [{ inStock: { $eq: false } }],
}

// MUST + SHOULD - required conditions + optional boosters
{
  $must: [{ category: { $eq: "electronics" } }],
  $should: [{ name: { $eq: "premium" } }], // boosts score if matched
}

// SHOULD alone - at least one must match (acts like OR)
{
  $should: [
    { name: { $eq: "laptop" } },
    { name: { $eq: "tablet" } },
  ]
}

Common mistake: $should next to $must does NOT filter

Adding $must silently turns sibling $should clauses into score boosters only — documents are not required to match them. Membership is decided entirely by $must, so the query "always finds something" even for nonsense terms (matches come back, just with a low score). This is the most common search bug.

// ❌ WRONG: the term only boosts; every in-stock doc still matches via $must,
// so a nonsensical searchTerm still returns results (with low scores).
{
  $must: { inStock: { $eq: true } },
  $should: [
    { name: { $smart: searchTerm }, $boost: 10 },
    { description: { $smart: searchTerm }, $boost: 5 },
  ],
}

// ✅ RIGHT: require a match in at least one field by nesting the OR ($should
// alone acts as OR) inside $must. $boost still ranks name above description.
{
  $must: [
    { inStock: { $eq: true } },
    {
      $should: [
        { name: { $smart: searchTerm }, $boost: 10 },
        { description: { $smart: searchTerm }, $boost: 5 },
      ],
    },
  ],
}

There is no minimum-score / cutoff option on query(). Don't filter by the returned score (scores are relative and shift with $boost, so a fixed threshold isn't reliable) — instead make the term required as above so non-matches are excluded.

Boosting

Boost the score of specific conditions:

{
  $must: [
    { name: { $eq: "laptop", $boost: 2.0 } }, // double the score weight
    { category: { $eq: "electronics", $boost: 0.5 } },
  ];
}

Counting

Count matching documents without returning them:

const { count } = await index.count({
  filter: { category: { $eq: "electronics" } },
});
// count: 2

Source: SKILL.md on GitHub

1 warningtoday3 checks · Risk SAFE
  • Gen Agent Trust Hubtoday

    The skill provides a comprehensive guide for using the Upstash Redis JavaScript SDK. It follows security best practices for credential management by advocating for environment variables and relies on official vendor resources. While the skill facilitates data ingestion from external databases, it does so as a standard database client with no malicious patterns or unexpected behaviors detected.

  • Sockettoday

    1 alert: gptSecurity

  • Snyktoday

    Risk: LOW · No issues

Signed by skilld at 35da719. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 5 days ago.

Activeupdated 2 weeks ago
metadata
{
  "author": "Upstash",
  "homepage": "https://upstash.com"
}

README badge

README badge for upstash/skills/upstash-redis-js