1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-10 11:43:01 +03:00

Update doctest client with latest v4 release (#2844)

This commit is contained in:
Shaya Potter
2024-09-29 13:19:06 +03:00
committed by GitHub
parent fd7b10be6c
commit 49fdb79897
167 changed files with 8227 additions and 9599 deletions

View File

@@ -19,6 +19,13 @@ describe('AGGREGATE', () => {
);
});
it('with ADDSCORES', () => {
assert.deepEqual(
transformArguments('index', '*', { ADDSCORES: true }),
['FT.AGGREGATE', 'index', '*', 'ADDSCORES']
);
});
describe('with LOAD', () => {
describe('single', () => {
describe('without alias', () => {
@@ -454,6 +461,13 @@ describe('AGGREGATE', () => {
['FT.AGGREGATE', 'index', '*', 'DIALECT', '1']
);
});
it('with TIMEOUT', () => {
assert.deepEqual(
transformArguments('index', '*', { TIMEOUT: 10 }),
['FT.AGGREGATE', 'index', '*', 'TIMEOUT', '10']
);
});
});
testUtils.testWithClient('client.ft.aggregate', async client => {

View File

@@ -119,11 +119,13 @@ type LoadField = PropertyName | {
}
export interface AggregateOptions {
VERBATIM?: true;
VERBATIM?: boolean;
ADDSCORES?: boolean;
LOAD?: LoadField | Array<LoadField>;
STEPS?: Array<GroupByStep | SortStep | ApplyStep | LimitStep | FilterStep>;
PARAMS?: Params;
DIALECT?: number;
TIMEOUT?: number;
}
export const FIRST_KEY_INDEX = 1;
@@ -149,6 +151,10 @@ export function pushAggregatehOptions(
args.push('VERBATIM');
}
if (options?.ADDSCORES) {
args.push('ADDSCORES');
}
if (options?.LOAD) {
args.push('LOAD');
pushArgumentsWithLength(args, () => {
@@ -213,6 +219,10 @@ export function pushAggregatehOptions(
args.push('DIALECT', options.DIALECT.toString());
}
if (options?.TIMEOUT !== undefined) {
args.push('TIMEOUT', options.TIMEOUT.toString());
}
return args;
}
@@ -303,4 +313,4 @@ export function transformReply(rawReply: AggregateRawReply): AggregateReply {
total: rawReply[0],
results
};
}
}

View File

@@ -1,7 +1,7 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CREATE';
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms } from '.';
import { SchemaFieldTypes, SchemaTextFieldPhonetics, RedisSearchLanguages, VectorAlgorithms, SCHEMA_GEO_SHAPE_COORD_SYSTEM } from '.';
describe('CREATE', () => {
describe('transformArguments', () => {
@@ -70,6 +70,18 @@ describe('CREATE', () => {
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'WITHSUFFIXTRIE']
);
});
it('with INDEXEMPTY', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.TEXT,
INDEXEMPTY: true
}
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXEMPTY']
);
});
});
it('NUMERIC', () => {
@@ -148,6 +160,18 @@ describe('CREATE', () => {
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'WITHSUFFIXTRIE']
);
});
it('with INDEXEMPTY', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.TAG,
INDEXEMPTY: true
}
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TAG', 'INDEXEMPTY']
);
});
});
describe('VECTOR', () => {
@@ -196,6 +220,42 @@ describe('CREATE', () => {
});
});
describe('GEOSHAPE', () => {
describe('without options', () => {
it('SCHEMA_FIELD_TYPE.GEOSHAPE', () => {
assert.deepEqual(
transformArguments('index', {
field: SchemaFieldTypes.GEOSHAPE
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE']
);
});
it('{ type: SCHEMA_FIELD_TYPE.GEOSHAPE }', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.GEOSHAPE
}
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE']
);
});
});
it('with COORD_SYSTEM', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.GEOSHAPE,
COORD_SYSTEM: SCHEMA_GEO_SHAPE_COORD_SYSTEM.SPHERICAL
}
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'GEOSHAPE', 'COORD_SYSTEM', 'SPHERICAL']
);
});
});
describe('with generic options', () => {
it('with AS', () => {
assert.deepEqual(
@@ -246,6 +306,18 @@ describe('CREATE', () => {
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'NOINDEX']
);
});
it('with INDEXMISSING', () => {
assert.deepEqual(
transformArguments('index', {
field: {
type: SchemaFieldTypes.TEXT,
INDEXMISSING: true
}
}),
['FT.CREATE', 'index', 'SCHEMA', 'field', 'TEXT', 'INDEXMISSING']
);
});
});
});

View File

@@ -4,15 +4,24 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CURSOR_READ';
describe('CURSOR READ', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('index', 0),
['FT.CURSOR', 'READ', 'index', '0']
);
describe('transformArguments', () => {
it('without options', () => {
assert.deepEqual(
transformArguments('index', 0),
['FT.CURSOR', 'READ', 'index', '0']
);
});
it('with COUNT', () => {
assert.deepEqual(
transformArguments('index', 0, { COUNT: 1 }),
['FT.CURSOR', 'READ', 'index', '0', 'COUNT', '1']
);
});
});
testUtils.testWithClient('client.ft.cursorRead', async client => {
const [ ,, { cursor } ] = await Promise.all([
const [, , { cursor }] = await Promise.all([
client.ft.create('idx', {
field: {
type: SchemaFieldTypes.TEXT

View File

@@ -4,16 +4,27 @@ export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
interface CursorReadOptions {
COUNT?: number;
}
export function transformArguments(
index: RedisCommandArgument,
cursor: number
cursor: number,
options?: CursorReadOptions
): RedisCommandArguments {
return [
const args = [
'FT.CURSOR',
'READ',
index,
cursor.toString()
];
if (options?.COUNT) {
args.push('COUNT', options.COUNT.toString());
}
return args;
}
export { transformReply } from './AGGREGATE_WITHCURSOR';

View File

@@ -9,7 +9,7 @@ export function transformArguments(
query: string,
options?: ProfileOptions & SearchOptions
): RedisCommandArguments {
const args = ['FT.PROFILE', index, 'SEARCH'];
let args: RedisCommandArguments = ['FT.PROFILE', index, 'SEARCH'];
if (options?.LIMITED) {
args.push('LIMITED');
@@ -21,9 +21,9 @@ export function transformArguments(
type ProfileSearchRawReply = ProfileRawReply<SearchRawReply>;
export function transformReply(reply: ProfileSearchRawReply): ProfileReply {
export function transformReply(reply: ProfileSearchRawReply, withoutDocuments: boolean): ProfileReply {
return {
results: transformSearchReply(reply[0]),
results: transformSearchReply(reply[0], withoutDocuments),
profile: transformProfile(reply[1])
};
}

View File

@@ -233,6 +233,15 @@ describe('SEARCH', () => {
['FT.SEARCH', 'index', 'query', 'DIALECT', '1']
);
});
it('with TIMEOUT', () => {
assert.deepEqual(
transformArguments('index', 'query', {
TIMEOUT: 5
}),
['FT.SEARCH', 'index', 'query', 'TIMEOUT', '5']
);
});
});
describe('client.ft.search', () => {
@@ -267,7 +276,8 @@ describe('SEARCH', () => {
client.ft.create('index', {
field: SchemaFieldTypes.NUMERIC
}),
client.hSet('1', 'field', '1')
client.hSet('1', 'field', '1'),
client.hSet('2', 'field', '2')
]);
assert.deepEqual(
@@ -275,10 +285,13 @@ describe('SEARCH', () => {
RETURN: []
}),
{
total: 1,
total: 2,
documents: [{
id: '1',
value: Object.create(null)
}, {
id: '2',
value: Object.create(null)
}]
}
);

View File

@@ -6,7 +6,6 @@ export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export interface SearchOptions {
// NOCONTENT?: true; TODO
VERBATIM?: true;
NOSTOPWORDS?: true;
// WITHSCORES?: true;
@@ -55,6 +54,7 @@ export interface SearchOptions {
};
PARAMS?: Params;
DIALECT?: number;
TIMEOUT?: number;
}
export function transformArguments(
@@ -70,13 +70,13 @@ export function transformArguments(
export type SearchRawReply = Array<any>;
export function transformReply(reply: SearchRawReply): SearchReply {
export function transformReply(reply: SearchRawReply, withoutDocuments: boolean): SearchReply {
const documents = [];
let i = 1;
while (i < reply.length) {
documents.push({
id: reply[i++],
value: documentValue(reply[i++])
value: withoutDocuments ? Object.create(null) : documentValue(reply[i++])
});
}
@@ -88,7 +88,6 @@ export function transformReply(reply: SearchRawReply): SearchReply {
function documentValue(tuples: any) {
const message = Object.create(null);
if (tuples === undefined) return message;
let i = 0;
while (i < tuples.length) {

View File

@@ -0,0 +1,45 @@
import { strict as assert } from 'assert';
import { SchemaFieldTypes } from '.';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './SEARCH_NOCONTENT';
describe('SEARCH_NOCONTENT', () => {
describe('transformArguments', () => {
it('without options', () => {
assert.deepEqual(
transformArguments('index', 'query'),
['FT.SEARCH', 'index', 'query', 'NOCONTENT']
);
});
});
describe('transformReply', () => {
it('returns total and keys', () => {
assert.deepEqual(transformReply([3, '1', '2', '3']), {
total: 3,
documents: ['1', '2', '3']
})
});
});
describe('client.ft.searchNoContent', () => {
testUtils.testWithClient('returns total and keys', async client => {
await Promise.all([
client.ft.create('index', {
field: SchemaFieldTypes.TEXT
}),
client.hSet('1', 'field', 'field1'),
client.hSet('2', 'field', 'field2'),
client.hSet('3', 'field', 'field3')
]);
assert.deepEqual(
await client.ft.searchNoContent('index', '*'),
{
total: 3,
documents: ['1','2','3']
}
);
}, GLOBAL.SERVERS.OPEN);
});
});

View File

@@ -0,0 +1,30 @@
import { RedisCommandArguments } from "@redis/client/dist/lib/commands";
import { pushSearchOptions } from ".";
import { SearchOptions, SearchRawReply } from "./SEARCH";
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(
index: string,
query: string,
options?: SearchOptions
): RedisCommandArguments {
return pushSearchOptions(
['FT.SEARCH', index, query, 'NOCONTENT'],
options
);
}
export interface SearchNoContentReply {
total: number;
documents: Array<string>;
};
export function transformReply(reply: SearchRawReply): SearchNoContentReply {
return {
total: reply[0],
documents: reply.slice(1)
};
}

View File

@@ -20,6 +20,7 @@ import * as INFO from './INFO';
import * as PROFILESEARCH from './PROFILE_SEARCH';
import * as PROFILEAGGREGATE from './PROFILE_AGGREGATE';
import * as SEARCH from './SEARCH';
import * as SEARCH_NOCONTENT from './SEARCH_NOCONTENT';
import * as SPELLCHECK from './SPELLCHECK';
import * as SUGADD from './SUGADD';
import * as SUGDEL from './SUGDEL';
@@ -80,6 +81,8 @@ export default {
profileAggregate: PROFILEAGGREGATE,
SEARCH,
search: SEARCH,
SEARCH_NOCONTENT,
searchNoContent: SEARCH_NOCONTENT,
SPELLCHECK,
spellCheck: SPELLCHECK,
SUGADD,
@@ -182,28 +185,46 @@ export enum SchemaFieldTypes {
NUMERIC = 'NUMERIC',
GEO = 'GEO',
TAG = 'TAG',
VECTOR = 'VECTOR'
VECTOR = 'VECTOR',
GEOSHAPE = 'GEOSHAPE'
}
type CreateSchemaField<
T extends SchemaFieldTypes,
E = Record<PropertyKey, unknown>
> = T | ({
type: T;
AS?: string;
INDEXMISSING?: boolean;
} & E);
type CommonFieldArguments = {
SORTABLE?: boolean | 'UNF';
NOINDEX?: boolean;
};
type CreateSchemaCommonField<
T extends SchemaFieldTypes,
E = Record<PropertyKey, unknown>
> = CreateSchemaField<
T,
({
SORTABLE?: true | 'UNF';
NOINDEX?: true;
} & E)
(CommonFieldArguments & E)
>;
function pushCommonFieldArguments(args: RedisCommandArguments, fieldOptions: CommonFieldArguments) {
if (fieldOptions.SORTABLE) {
args.push('SORTABLE');
if (fieldOptions.SORTABLE === 'UNF') {
args.push('UNF');
}
}
if (fieldOptions.NOINDEX) {
args.push('NOINDEX');
}
}
export enum SchemaTextFieldPhonetics {
DM_EN = 'dm:en',
DM_FR = 'dm:fr',
@@ -216,6 +237,7 @@ type CreateSchemaTextField = CreateSchemaCommonField<SchemaFieldTypes.TEXT, {
WEIGHT?: number;
PHONETIC?: SchemaTextFieldPhonetics;
WITHSUFFIXTRIE?: boolean;
INDEXEMPTY?: boolean;
}>;
type CreateSchemaNumericField = CreateSchemaCommonField<SchemaFieldTypes.NUMERIC>;
@@ -226,6 +248,7 @@ type CreateSchemaTagField = CreateSchemaCommonField<SchemaFieldTypes.TAG, {
SEPARATOR?: string;
CASESENSITIVE?: true;
WITHSUFFIXTRIE?: boolean;
INDEXEMPTY?: boolean;
}>;
export enum VectorAlgorithms {
@@ -254,6 +277,17 @@ type CreateSchemaHNSWVectorField = CreateSchemaVectorField<VectorAlgorithms.HNSW
EF_RUNTIME?: number;
}>;
export const SCHEMA_GEO_SHAPE_COORD_SYSTEM = {
SPHERICAL: 'SPHERICAL',
FLAT: 'FLAT'
} as const;
export type SchemaGeoShapeFieldCoordSystem = typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM[keyof typeof SCHEMA_GEO_SHAPE_COORD_SYSTEM];
type CreateSchemaGeoShapeField = CreateSchemaCommonField<SchemaFieldTypes.GEOSHAPE, {
COORD_SYSTEM?: SchemaGeoShapeFieldCoordSystem;
}>;
export interface RediSearchSchema {
[field: string]:
CreateSchemaTextField |
@@ -261,7 +295,8 @@ export interface RediSearchSchema {
CreateSchemaGeoField |
CreateSchemaTagField |
CreateSchemaFlatVectorField |
CreateSchemaHNSWVectorField;
CreateSchemaHNSWVectorField |
CreateSchemaGeoShapeField;
}
export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema) {
@@ -297,11 +332,18 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema
args.push('WITHSUFFIXTRIE');
}
pushCommonFieldArguments(args, fieldOptions);
if (fieldOptions.INDEXEMPTY) {
args.push('INDEXEMPTY');
}
break;
// case SchemaFieldTypes.NUMERIC:
// case SchemaFieldTypes.GEO:
// break;
case SchemaFieldTypes.NUMERIC:
case SchemaFieldTypes.GEO:
pushCommonFieldArguments(args, fieldOptions);
break;
case SchemaFieldTypes.TAG:
if (fieldOptions.SEPARATOR) {
@@ -316,6 +358,12 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema
args.push('WITHSUFFIXTRIE');
}
pushCommonFieldArguments(args, fieldOptions);
if (fieldOptions.INDEXEMPTY) {
args.push('INDEXEMPTY');
}
break;
case SchemaFieldTypes.VECTOR:
@@ -357,19 +405,20 @@ export function pushSchema(args: RedisCommandArguments, schema: RediSearchSchema
}
});
continue; // vector fields do not contain SORTABLE and NOINDEX options
break;
case SchemaFieldTypes.GEOSHAPE:
if (fieldOptions.COORD_SYSTEM !== undefined) {
args.push('COORD_SYSTEM', fieldOptions.COORD_SYSTEM);
}
pushCommonFieldArguments(args, fieldOptions);
break;
}
if (fieldOptions.SORTABLE) {
args.push('SORTABLE');
if (fieldOptions.SORTABLE === 'UNF') {
args.push('UNF');
}
}
if (fieldOptions.NOINDEX) {
args.push('NOINDEX');
if (fieldOptions.INDEXMISSING) {
args.push('INDEXMISSING');
}
}
}
@@ -506,6 +555,14 @@ export function pushSearchOptions(
args.push('DIALECT', options.DIALECT.toString());
}
if (options?.RETURN?.length === 0) {
args.preserve = true;
}
if (options?.TIMEOUT !== undefined) {
args.push('TIMEOUT', options.TIMEOUT.toString());
}
return args;
}