diff --git a/.changeset/chilled-ways-design.md b/.changeset/chilled-ways-design.md new file mode 100644 index 0000000..cfc5814 --- /dev/null +++ b/.changeset/chilled-ways-design.md @@ -0,0 +1,9 @@ +--- +'@rushdb/javascript-sdk': minor +'rushdb-dashboard': minor +'rushdb-core': minor +'rushdb-website': minor +'rushdb-docs': minor +--- + +Update query for fetching property values and minor fixes & cleanups diff --git a/docs/docs/quick-start/creating-and-retrieving-records.md b/docs/docs/quick-start/creating-and-retrieving-records.md index f8eb65f..abfa6d4 100644 --- a/docs/docs/quick-start/creating-and-retrieving-records.md +++ b/docs/docs/quick-start/creating-and-retrieving-records.md @@ -9,21 +9,28 @@ In this section, we'll learn how to use the RushDB SDK to create and retrieve si Ensure that you have initialized the RushDB SDK in your project as follows: +### TypeScript / Javascript ```typescript import RushDB from '@rushdb/javascript-sdk'; const db = new RushDB('API_TOKEN'); ``` +### Python + +```bash +from rushdb import RushDB + +db = RushDB("API_TOKEN") +``` + Replace `API_TOKEN` with your actual API token. ## Creating Records The `create` method allows you to create a single record without registering a model. -### Example - -Creating an author record directly: +### TypeScript / Javascript ```typescript const newAuthor = await db.records.create('author', { name: 'Alice Smith', @@ -35,31 +42,49 @@ const newAuthor = await db.records.create('author', { }); ``` +### Python + +```python +newAuthor = db.records.create( + "author", + { + "name": "Alice Smith", + "email": "alice.smith@example.com", + "jobTitle": "writer", + "age": 28, + "married": True, + "dateOfBirth": "1993-05-15T00:00:00Z" + } +) +``` + + ## Reading Records -The `find`, `findOne`, and `findById` methods let you read records from the database without predefining models. +The `find` method let you read records from the database without predefining models. -### Example +### TypeScript / Javascript -Finding records with specific criteria: ```typescript const authors = await db.records.find('author', { - where: { - jobTitle: { $contains: 'writer' }, - age: { $gte: 25 } - } + where: { + jobTitle: { $contains: 'writer' }, + age: { $gte: 25 } + } }); ``` -### Example +### Python -Finding a single record: -```typescript -const author = await db.records.findOne('author', { - where: { - email: { $contains: 'alice.smith@' } +```python +authors = db.records.find({ + "labels": ["author"] + "where": { + "jobTitle": { "$contains": "writer" }, + "age": { "$gte": 25 } } -}); +}) ``` + This simple flow demonstrates how to create and retrieve records using the RushDB SDK. By defining models and utilizing the SDK's methods, you can easily manage your application's data. Feel free to adapt these examples to fit the specific needs of your project. diff --git a/docs/docs/quick-start/installation.md b/docs/docs/quick-start/installation.md index a3a5638..9e309b2 100644 --- a/docs/docs/quick-start/installation.md +++ b/docs/docs/quick-start/installation.md @@ -6,7 +6,9 @@ Getting started with RushDB SDK is straightforward. This section will guide you ## Step 1: Install the Package -To begin, you need to add the RushDB SDK to your project. You can do this using either npm or yarn: +To begin, you need to add the RushDB SDK to your project. + +### TypeScript / JavaScript Using npm: @@ -19,17 +21,39 @@ Using yarn: yarn add @rushdb/javascript-sdk ``` -### Note on SDK Size -The RushDB SDK is lightweight, coming in at just 5.1kB gzipped. Learn more about the package size [here](https://pkg-size.dev/@rushdb%2Fjavascript-sdk). +Using pnpm: +```bash +pnpm add @rushdb/javascript-sdk +``` + +The RushDB SDK is lightweight, coming in at just 6.9KB gzipped. Learn more about the package size [here](https://pkg-size.dev/@rushdb%2Fjavascript-sdk). + +### Python + +```bash +pip install rushdb +``` ## Step 2: Initialize the SDK Once the package is installed, you can create an instance of the RushDB SDK in your project. + +### TypeScript / JavaScript + ```typescript import RushDB from '@rushdb/javascript-sdk'; const db = new RushDB('API_TOKEN'); ``` -Replace `API_TOKEN` with your actual API token, which you can obtain from the RushDB Dashboard. + +### Python + +```bash +from rushdb import RushDB + +db = RushDB("API_TOKEN") +``` + +Replace `API_TOKEN` with your actual API token, which you can obtain from the [RushDB Dashboard](https://app.rushdb.com/). ## Next steps To make full use of the SDK, you'll need a valid API token. In the [next section](/quick-start/configuring-dashboard), Configuring RushDB Dashboard, we'll guide you through the process of registering on the dashboard, creating a project, and generating your API token. diff --git a/packages/javascript-sdk/src/api/api.ts b/packages/javascript-sdk/src/api/api.ts index 5054167..93d76b7 100644 --- a/packages/javascript-sdk/src/api/api.ts +++ b/packages/javascript-sdk/src/api/api.ts @@ -7,7 +7,8 @@ import type { SearchQuery, Schema, InferSchemaTypesWrite, - MaybeArray + MaybeArray, + PropertyValuesOptions } from '../types/index.js' import type { ApiResponse, RecordsApi } from './types.js' @@ -428,8 +429,8 @@ export class RestAPI { findById: async (id: string, transaction?: Transaction | string) => { return this.api?.properties.findById(id, transaction) }, - values: async (id: string, transaction?: Transaction | string) => { - return this.api?.properties.values(id, {}, transaction) + values: async (id: string, options?: PropertyValuesOptions, transaction?: Transaction | string) => { + return this.api?.properties.values(id, options, transaction) } } diff --git a/packages/javascript-sdk/src/api/create-api.ts b/packages/javascript-sdk/src/api/create-api.ts index f16574b..5c53218 100644 --- a/packages/javascript-sdk/src/api/create-api.ts +++ b/packages/javascript-sdk/src/api/create-api.ts @@ -14,14 +14,14 @@ import type { SearchQuery, Schema, MaybeArray, - OrderDirection + PropertyValuesOptions } from '../types/index.js' import type { ApiResponse } from './types.js' import { isArray } from '../common/utils.js' import { NonUniqueResultError } from '../sdk/errors.js' import { DBRecordsBatchDraft, DBRecordDraft } from '../sdk/record.js' -import { buildTransactionHeader, pickRecordId, pickTransactionId } from './utils.js' +import { buildTransactionHeader, generateRandomId, pickRecordId, pickTransactionId } from './utils.js' import type { Logger } from '../sdk/types.js' export const createApi = (fetcher: ReturnType, logger?: Logger) => ({ @@ -35,10 +35,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response } @@ -51,10 +52,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo headers: Object.assign({}, buildTransactionHeader(txId)), method: 'DELETE' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -69,10 +71,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -83,10 +86,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo headers: Object.assign({}, buildTransactionHeader(txId)), method: 'GET' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -96,20 +100,17 @@ export const createApi = (fetcher: ReturnType, logger?: Lo // updateValues: (id: string, transaction?: Transaction | string) => { // // @TODO // }, - values: async ( - id: string, - options?: { sort?: OrderDirection; skip?: number; limit?: number }, - transaction?: Transaction | string - ) => { + values: async (id: string, options?: PropertyValuesOptions, transaction?: Transaction | string) => { const txId = pickTransactionId(transaction) const path = `/properties/${id}/values` - const { sort, skip, limit } = options ?? {} + const { sort, skip, limit, query } = options ?? {} const queryParams = new URLSearchParams() - if (sort) queryParams.append('sort', sort) + if (sort !== undefined) queryParams.append('sort', sort) if (skip !== undefined) queryParams.append('skip', skip.toString()) if (limit !== undefined) queryParams.append('limit', limit.toString()) + if (query !== undefined) queryParams.append('query', query) const queryString = queryParams.toString() const fullPath = queryString ? `${path}?${queryString}` : path @@ -118,10 +119,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo headers: Object.assign({}, buildTransactionHeader(txId)), method: 'GET' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(fullPath, payload) - logger?.({ path: fullPath, ...payload, responseData: response.data }) + logger?.({ requestId, path: fullPath, ...payload, responseData: response.data }) return response } @@ -145,10 +147,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo ...(options?.direction && { direction: options.direction }) } } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -164,10 +167,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: data instanceof DBRecordDraft ? data.toJson() : data } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher | undefined>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -183,10 +187,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: data instanceof DBRecordsBatchDraft ? data.toJson() : data } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher[]>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -199,10 +204,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'PUT', requestData: searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -216,10 +222,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: multipleTargets ? 'PUT' : 'DELETE', requestData: multipleTargets ? { limit: 1000, where: { $id: { $in: idOrIds } } } : undefined } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -241,10 +248,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo ...(options?.direction && { direction: options.direction }) } } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -256,10 +264,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -274,10 +283,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: params?.searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher[]>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -289,10 +299,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: isArray(idOrIds) ? 'POST' : 'GET', requestData: isArray(idOrIds) ? { ids: idOrIds } : undefined } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher[] | DBRecord>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -307,10 +318,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: { ...searchParams, limit: 1, skip: 0 } } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher[]>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) const [record] = response.data return { ...response, data: record } as ApiResponse> }, @@ -325,10 +337,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: { ...searchParams, limit: 1, skip: 0 } } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher[]>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) if (typeof response.total !== 'undefined' && response.total > 1) { throw new NonUniqueResultError(response.total, searchParams) @@ -345,10 +358,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo headers: Object.assign({}, buildTransactionHeader(txId)), method: 'GET' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -360,10 +374,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo headers: Object.assign({}, buildTransactionHeader(txId)), method: 'GET' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -384,10 +399,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'PUT', requestData: data instanceof DBRecordDraft ? data.toJson() : data } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -404,10 +420,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'PATCH', requestData: data instanceof DBRecordDraft ? data.toJson() : data } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response } @@ -435,10 +452,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: searchParams } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response } @@ -450,10 +468,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: config } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -463,10 +482,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: {} } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -475,10 +495,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo const payload = { method: 'GET' } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) return response }, @@ -488,10 +509,11 @@ export const createApi = (fetcher: ReturnType, logger?: Lo method: 'POST', requestData: {} } - logger?.({ path, ...payload }) + const requestId = typeof logger === 'function' ? generateRandomId() : '' + logger?.({ requestId, path, ...payload }) const response = await fetcher>(path, payload) - logger?.({ path, ...payload, responseData: response.data }) + logger?.({ requestId, path, ...payload, responseData: response.data }) } } }) diff --git a/packages/javascript-sdk/src/api/utils.ts b/packages/javascript-sdk/src/api/utils.ts index c56df80..c455125 100644 --- a/packages/javascript-sdk/src/api/utils.ts +++ b/packages/javascript-sdk/src/api/utils.ts @@ -193,3 +193,15 @@ export const buildUrl = (props: UserProvidedConfig): string => { return `${protocol}://${host}${portString}${basePath}` } + +export const generateRandomId = (size: number = 8): string => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + let id = '' + + for (let i = 0; i < size; i++) { + const randomIndex = Math.floor(Math.random() * characters.length) + id += characters[randomIndex] + } + + return id +} diff --git a/packages/javascript-sdk/src/types/value.ts b/packages/javascript-sdk/src/types/value.ts index c7264ab..8415cb6 100644 --- a/packages/javascript-sdk/src/types/value.ts +++ b/packages/javascript-sdk/src/types/value.ts @@ -6,6 +6,7 @@ import type { PROPERTY_TYPE_STRING } from '../common/constants.js' import type { MaybeArray } from './utils.js' +import type { OrderDirection } from './query' // DATETIME export type DatetimeObject = { @@ -57,6 +58,8 @@ export type PropertyValuesData = { values: PropertyValue[] } +export type PropertyValuesOptions = { sort?: OrderDirection; skip?: number; limit?: number; query?: string } + export type PropertySingleValue = TType extends typeof PROPERTY_TYPE_DATETIME ? DatetimeValue : TType extends typeof PROPERTY_TYPE_NUMBER ? NumberValue diff --git a/platform/core/src/core/property/property-query.service.ts b/platform/core/src/core/property/property-query.service.ts index 7e142fe..872fc7f 100755 --- a/platform/core/src/core/property/property-query.service.ts +++ b/platform/core/src/core/property/property-query.service.ts @@ -162,15 +162,13 @@ export class PropertyQueryService { getPropertyValues({ sort, - paginationParams + query, + paginationParams = { skip: 0, limit: 100 } }: { sort?: TSearchSortDirection + query?: string paginationParams?: Pick }) { - const pagination = buildPagination( - toBoolean(paginationParams) ? paginationParams : { skip: 0, limit: 100 } - ) - const sortPart = sort ? `record[property.name] ${sort}` : `record.${RUSHDB_KEY_ID}` const queryBuilder = new QueryBuilder() @@ -180,12 +178,26 @@ export class PropertyQueryService { `MATCH (record:${RUSHDB_LABEL_RECORD})<-[value:${RUSHDB_RELATION_VALUE}]-(property:${RUSHDB_LABEL_PROPERTY} { id: $id })` ) .append(`WHERE record[property.name] IS NOT NULL`) - .append(`ORDER BY ${sortPart} ${pagination}`) + .append(`WITH record[property.name] AS propValue, property.type AS propType`) + + if (query) { + queryBuilder.append(`any(value IN record[property.name] WHERE value =~ "(?i).*${query}.*")`) + } + + queryBuilder + .append(`ORDER BY ${sortPart}`) + .append( + `WITH apoc.coll.toSet(apoc.coll.flatten(collect(DISTINCT propValue))) AS values, propType as type` + ) + .append(`UNWIND values AS v`) + .append( + `WITH values, type, min(CASE type WHEN 'datetime' THEN datetime(v) ELSE toFloatOrNull(v) END) AS minValue, max(CASE type WHEN 'datetime' THEN datetime(v) ELSE toFloatOrNull(v) END) AS maxValue` + ) .append(`RETURN {`) - .append(`values: collect(DISTINCT record[property.name]),`) - .append(`min: toFloatOrNull(min(toFloatOrNull(record[property.name]))),`) - .append(`max: toFloatOrNull(max(toFloatOrNull(record[property.name]))),`) - .append(`type: collect(DISTINCT property.type)[0]`) + .append(`values: values[${paginationParams.skip}..${paginationParams.skip + paginationParams.limit}],`) + .append(`min: CASE type WHEN 'datetime' THEN toString(minValue) ELSE minValue END,`) + .append(`max: CASE type WHEN 'datetime' THEN toString(maxValue) ELSE maxValue END,`) + .append(`type: type`) .append(`} AS result`) return queryBuilder.getQuery() diff --git a/platform/core/src/core/property/property.controller.ts b/platform/core/src/core/property/property.controller.ts index 38e3e22..8d11f6c 100755 --- a/platform/core/src/core/property/property.controller.ts +++ b/platform/core/src/core/property/property.controller.ts @@ -107,12 +107,14 @@ export class PropertyController { @Param('propertyId') propertyId: string, @TransactionDecorator() transaction: Transaction, @Query('sort') sort?: TSearchSortDirection, + @Query('query') query?: string, @Query('skip', new DefaultValuePipe(0), ParseIntPipe) skip?: number, @Query('limit', new DefaultValuePipe(100), ParseIntPipe) limit?: number ): Promise { return await this.propertyService.getPropertyValues({ propertyId, sort, + query, pagination: pagination(skip, limit), transaction }) diff --git a/platform/core/src/core/property/property.service.ts b/platform/core/src/core/property/property.service.ts index ab1f094..0fd4b59 100755 --- a/platform/core/src/core/property/property.service.ts +++ b/platform/core/src/core/property/property.service.ts @@ -26,12 +26,14 @@ export class PropertyService { async getPropertyValues({ propertyId, sort, + query, pagination, transaction, queryRunner }: { propertyId: string sort?: TSearchSortDirection + query?: string pagination?: Pick transaction: Transaction queryRunner?: QueryRunner @@ -42,6 +44,7 @@ export class PropertyService { .run( this.propertyQueryService.getPropertyValues({ paginationParams: pagination, + query, sort }), { diff --git a/platform/core/src/dashboard/user/user.module.ts b/platform/core/src/dashboard/user/user.module.ts index 41e71f3..666d6a5 100755 --- a/platform/core/src/dashboard/user/user.module.ts +++ b/platform/core/src/dashboard/user/user.module.ts @@ -62,7 +62,7 @@ export class UserModule implements OnApplicationBootstrap { console.log('Initializing user failed.', error) await transaction.rollback() } finally { - console.log('Initializing user successfully finished.') + console.log('Initializing user finished.') if (transaction.isOpen()) { await transaction.commit() await transaction.close() diff --git a/platform/core/src/database/database.module.ts b/platform/core/src/database/database.module.ts index 59e7d24..b2893a6 100755 --- a/platform/core/src/database/database.module.ts +++ b/platform/core/src/database/database.module.ts @@ -69,7 +69,7 @@ export class DatabaseModule implements OnModuleInit { console.log('Initializing RushDB failed.', error) await transaction.rollback() } finally { - console.log('Initializing RushDB successfully finished.') + console.log('Initializing RushDB finished.') if (transaction.isOpen()) { await transaction.commit() await transaction.close() diff --git a/platform/dashboard/src/features/labels/components/LabelColorIcon.tsx b/platform/dashboard/src/features/labels/components/LabelColorIcon.tsx index 6cf7a36..73b07ea 100644 --- a/platform/dashboard/src/features/labels/components/LabelColorIcon.tsx +++ b/platform/dashboard/src/features/labels/components/LabelColorIcon.tsx @@ -1,8 +1,7 @@ -import { UNLABELED } from '../constants' import { getLabelColor } from '../utils' import { filterLabel } from './FilterLabel' -export function LabelColorIcon({ label = UNLABELED, idx = 0 }: { idx?: number; label: string }) { +export function LabelColorIcon({ label, idx = 0 }: { idx?: number; label: string }) { return (
- {text} + {label}
) } diff --git a/platform/dashboard/src/features/labels/constants.ts b/platform/dashboard/src/features/labels/constants.ts deleted file mode 100644 index b8cd179..0000000 --- a/platform/dashboard/src/features/labels/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const UNLABELED = 'null' diff --git a/platform/dashboard/src/features/labels/utils.ts b/platform/dashboard/src/features/labels/utils.ts index 70a2e57..d160643 100644 --- a/platform/dashboard/src/features/labels/utils.ts +++ b/platform/dashboard/src/features/labels/utils.ts @@ -7,19 +7,11 @@ import type { filterLabel } from '~/features/labels/components/FilterLabel' import { variants } from '~/features/labels/components/FilterLabel' import { getFromIndex } from '~/lib/utils' -import { UNLABELED } from './constants' - type LabelColor = NonNullable['variant']> -const $assignedColors = persistentMap>('label:colors', { - [UNLABELED]: 'blank' -}) +const $assignedColors = persistentMap>('label:colors', {}) export const getLabelColor = (label: string, idx: number): LabelColor => { - if (label === UNLABELED || !label) { - return 'blank' - } - const assignedColors = $assignedColors.get() if (label in assignedColors) { @@ -27,7 +19,7 @@ export const getLabelColor = (label: string, idx: number): LabelColor => { } // assign new color - const array = Object.keys(variants).filter((v) => v !== 'blank') as Array + const array = Object.keys(variants) as Array const color = getFromIndex(array, idx, 0) $assignedColors.setKey(label, color) return color diff --git a/platform/dashboard/src/features/onboarding/components/steps/UseSdkStep.tsx b/platform/dashboard/src/features/onboarding/components/steps/UseSdkStep.tsx index 8f7a0d3..a663a8e 100644 --- a/platform/dashboard/src/features/onboarding/components/steps/UseSdkStep.tsx +++ b/platform/dashboard/src/features/onboarding/components/steps/UseSdkStep.tsx @@ -1,6 +1,6 @@ import { useStore } from '@nanostores/react' import { Book } from 'lucide-react' -import { Fragment } from 'react' +import { Fragment, useEffect, useMemo, useState } from 'react' import { Button } from '~/elements/Button' import { Card, CardBody } from '~/elements/Card' import { Editor } from '~/elements/Editor' @@ -25,42 +25,54 @@ import { cn, getNumberOfLines } from '~/lib/utils' const TOKEN_FALLBACK = 'TOKEN' const getCreateFirstRecordSteps = ({ language }: { language: AvailableSdkLanguage }) => { - const javascriptSteps = { - defineModel: { - title: 'Define your first model', - code: `import { Model } from '@rushdb/javascript-sdk'; - -export const UserRepo = new Model( - 'user', - { - name: { type: 'string' }, - }, - db -); -` - }, - createRecord: { - title: 'Create your first record', - code: `await UserRepo.create({ - name: 'John Doe', -});` + const typescriptSteps = { + pushAndQueryDataTs: { + title: 'Push and query data', + code: `await db.records.createMany('COMPANY', {...jsonData}) + +await db.records.find({ + labels: ['COMPANY'], + where: { + rating: { $gte: 4 }, + name: { $contains: 'AI' } + } +})` + } + } + + const pythonSteps = { + pushAndQueryDataPy: { + title: 'Push and query data', + code: `db.records.create_many("COMPANY", jsonData) + +db.records.find({ + "labels": ["COMPANY"], + "where": { + "rating": { "$gte": 4 }, + "name": { "$contains": "AI" } + } +})` } } return { - javascript: javascriptSteps, - typescript: javascriptSteps + typescript: typescriptSteps, + python: pythonSteps }[language] } const getInstallationCode = ({ token, language }: { token: string; language: AvailableSdkLanguage }) => { - const jsCode = (token: string) => `import RushDB from '@rushdb/javascript-sdk'; + const jsCode = (token: string) => `import RushDB from '@rushdb/javascript-sdk' + +const db = new RushDB('${token}')` -const db = new RushDB("${token}");` + const pyCode = (token: string) => `from rushdb import RushDB + +db = RushDB("${token}")` return { - javascript: jsCode(token), - typescript: jsCode(token) + typescript: jsCode(token), + python: pyCode(token) }[language] } @@ -174,6 +186,17 @@ function InstallSdk({ className }: { className?: string }) { ) + case 'python': { + return ( + + + + + + + + ) + } default: return null } @@ -208,9 +231,26 @@ function InitializeSdk({ className }: { className?: string }) { function StartBuilding({ className }: { className?: string }) { const { sdkLanguage } = useStore($settings) - const steps = getCreateFirstRecordSteps({ - language: sdkLanguage - }) + const [editorVisible, setEditorVisible] = useState(true) + + const steps = useMemo( + () => + getCreateFirstRecordSteps({ + language: sdkLanguage + }), + [sdkLanguage] + ) + + // Dirty hack to reset Editor when language has changed + useEffect(() => { + setEditorVisible(false) + const timeout = setTimeout(() => { + setEditorVisible(true) + }, 0) + return () => { + clearTimeout(timeout) + } + }, [sdkLanguage]) return ( <> @@ -219,13 +259,15 @@ function StartBuilding({ className }: { className?: string }) { - + {editorVisible && ( + + )} ))} diff --git a/platform/dashboard/src/features/onboarding/constants.ts b/platform/dashboard/src/features/onboarding/constants.ts index 7debf38..e0e1d3c 100644 --- a/platform/dashboard/src/features/onboarding/constants.ts +++ b/platform/dashboard/src/features/onboarding/constants.ts @@ -13,16 +13,10 @@ export const docsUrls = { logo: typescriptLogo }, python: { - installation: undefined, - usage: undefined, - github: undefined, + installation: 'https://docs.rushdb.com/quick-start/installation/', + usage: 'https://docs.rushdb.com/python-sdk/records-api', + github: 'https://github.com/rush-db/rushdb-python', logo: pythonLogo - }, - ruby: { - installation: undefined, - usage: undefined, - github: undefined, - logo: rubyLogo } }, dashboard: { @@ -43,6 +37,6 @@ export const docsUrls = { > } -export const SDK_LANGUAGES = ['typescript', 'python', 'ruby'] as const +export const SDK_LANGUAGES = ['typescript', 'python'] as const -export const AVAILABLE_SDK_LANGUAGES = ['typescript'] as const satisfies readonly SdkLanguage[] +export const AVAILABLE_SDK_LANGUAGES = ['typescript', 'python'] as const satisfies readonly SdkLanguage[] diff --git a/platform/dashboard/src/layout/ProjectRecordLayout.tsx b/platform/dashboard/src/layout/ProjectRecordLayout.tsx index 242291b..3aedf22 100644 --- a/platform/dashboard/src/layout/ProjectRecordLayout.tsx +++ b/platform/dashboard/src/layout/ProjectRecordLayout.tsx @@ -49,10 +49,7 @@ export function ProjectRecordLayout() { return ( <> - + - + diff --git a/website/src/sections/HowItWorks/index.tsx b/website/src/sections/HowItWorks/index.tsx index 5d4f851..4a7cd23 100644 --- a/website/src/sections/HowItWorks/index.tsx +++ b/website/src/sections/HowItWorks/index.tsx @@ -316,7 +316,7 @@ export const HowItWorks = () => {
- ~2ms{' '} + ~0.25ms{' '}

Batch write speed per Record