diff --git a/.eslintignore b/.eslintignore index 90bea7150..d849ec6cc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,8 +4,11 @@ src/**/*.js.map src/Kuzzle.js src/controllers/Auth.js +src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js src/utils/interfaces.js +src/core/searchResult/SearchResultBase.js +src/core/searchResult/Document.js diff --git a/.gitignore b/.gitignore index b22a32b2c..1a1af6994 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,11 @@ test-*.js index.js src/Kuzzle.js src/controllers/Auth.js +src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js src/utils/interfaces.js +src/core/searchResult/SearchResultBase.js +src/core/searchResult/Document.js diff --git a/doc/7/controllers/document/validate/index.md b/doc/7/controllers/document/validate/index.md index e274a21c1..b87199cda 100644 --- a/doc/7/controllers/document/validate/index.md +++ b/doc/7/controllers/document/validate/index.md @@ -7,7 +7,7 @@ description: Validate a document # validate -Validates data against existing validation rules. +Validates a document against existing validation rules. Note that if no validation specifications are set for the ``/``, the document will always be valid. diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 99e4b9aea..98a27138e 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -12,7 +12,7 @@ import { MemoryStorageController } from './controllers/MemoryStorage'; import { uuidv4 } from './utils/uuidv4'; import { proxify } from './utils/proxify'; -import { JSONObject } from './utils/interfaces'; +import { JSONObject, KuzzleRequest } from './utils/interfaces'; // Defined by webpack plugin declare const SDKVERSION: any; @@ -59,7 +59,7 @@ export class Kuzzle extends KuzzleEventEmitter { public auth: AuthController; public bulk: any; public collection: any; - public document: any; + public document: DocumentController; public index: any; public ms: any; public realtime: any; @@ -332,7 +332,7 @@ export class Kuzzle extends KuzzleEventEmitter { * Connects to a Kuzzle instance using the provided host name * @returns {Promise} */ - connect () { + connect (): Promise { if (this.protocol.isReady()) { return Promise.resolve(); } @@ -455,7 +455,7 @@ export class Kuzzle extends KuzzleEventEmitter { * @param {object} [options] - Optional arguments * @returns {Promise} */ - query (request: any = {}, options: any = {}) { + query (request: KuzzleRequest = {}, options: JSONObject = {}): Promise { if (typeof request !== 'object' || Array.isArray(request)) { throw new Error(`Kuzzle.query: Invalid request: ${JSON.stringify(request)}`); } diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index b23e6b9e8..2d8ef3ce6 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -67,9 +67,9 @@ export class AuthController extends BaseController { * * @param description API key description * @param options Additional options - * - "_id" API key unique ID - * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed - * - "expiresIn" Expiration duration + * - `_id` API key unique ID + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `expiresIn` Expiration duration * * @returns The created API key */ @@ -98,7 +98,7 @@ export class AuthController extends BaseController { * * @param id API key ID * @param options Additional options - * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed */ deleteApiKey(id: string, options: { refresh?: string } = {}): Promise { const request = { @@ -118,8 +118,8 @@ export class AuthController extends BaseController { * * @param query Search query * @param options Additional options - * - "from" Offset of the first document to fetch - * - "size" Maximum number of documents to retrieve per page + * - `from` Offset of the first document to fetch + * - `size` Maximum number of documents to retrieve per page * * @returns A search result object */ @@ -189,7 +189,7 @@ export class AuthController extends BaseController { * @param strategy New credentials * @param credentials Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the new credentials. * The content depends on the authentication strategy @@ -214,7 +214,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns A boolean indicating if the credentials exists */ @@ -236,7 +236,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again */ deleteMyCredentials ( strategy: string, @@ -255,7 +255,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-current-user * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ @@ -276,7 +276,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the credentials for the provided authentication strategy. * Its content depends on the authentication strategy. @@ -298,7 +298,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-my-rights * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array containing user rights objects */ @@ -338,7 +338,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-strategies * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array of available strategies names */ @@ -409,7 +409,7 @@ export class AuthController extends BaseController { * @param strategy Name of the strategy to use * @param credentials Updated credentials * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the updated credentials. * The content depends on the authentication strategy @@ -435,7 +435,7 @@ export class AuthController extends BaseController { * * @param {object} content - User custom information * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ @@ -461,7 +461,7 @@ export class AuthController extends BaseController { * @param strategy Name of the strategy to use * @param credentials Credentials to validate * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again */ validateMyCredentials ( strategy: string, @@ -482,8 +482,8 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/refresh-token * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again - * - "expiresIn" Expiration duration + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `expiresIn` Expiration duration * * @returns The refreshed token */ diff --git a/src/controllers/Document.js b/src/controllers/Document.js deleted file mode 100644 index cc88e76b8..000000000 --- a/src/controllers/Document.js +++ /dev/null @@ -1,254 +0,0 @@ -const { BaseController } = require('./Base'); -const { DocumentsSearchResult } = require('../core/searchResult/Document'); - -class DocumentController extends BaseController { - - /** - * @param {Kuzzle} kuzzle - */ - constructor (kuzzle) { - super(kuzzle, 'document'); - } - - count (index, collection, body, options = {}) { - const request = { - index, - collection, - body, - action: 'count' - }; - - return this.query(request, options) - .then(response => response.result.count); - } - - create (index, collection, document, _id = null, options = {}) { - const request = { - index, - collection, - _id, - body: document, - action: 'create' - }; - return this.query(request, options) - .then(response => response.result); - } - - createOrReplace (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'createOrReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - delete (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'delete' - }; - - return this.query(request, options) - .then(response => response.result._id); - } - - deleteByQuery(index, collection, body = {}, options = {}) { - const request = { - index, - collection, - body, - action: 'deleteByQuery' - }; - - return this.query(request, options) - .then(response => response.result.ids); - } - - exists (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'exists' - }; - - return this.query(request, options) - .then(response => response.result); - } - - get (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'get' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mCreate (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mCreate' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mCreateOrReplace (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mCreateOrReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mDelete (index, collection, ids, options = {}) { - const request = { - index, - collection, - body: {ids}, - action: 'mDelete' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mGet (index, collection, ids, options = {}) { - const request = { - index, - collection, - action: 'mGet', - body: {ids} - }; - - return this.query(request, options) - .then(response => response.result); - } - - mReplace (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mUpdate (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mUpdate' - }; - - return this.query(request, options) - .then(response => response.result); - } - - replace (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'replace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - search (index, collection, body = {}, options = {}) { - return this._search(index, collection, body, options) - .then(({ response, request, opts }) => ( - new DocumentsSearchResult(this.kuzzle, request, opts, response.result) - )); - } - - _search (index, collection, body = {}, options = {}) { - const request = { - index, - collection, - body: null, - action: 'search', - }; - if ( this.kuzzle.protocol.name === 'http' - && options.verb - && options.verb.toLowerCase() === 'get' - ) { - request.searchBody = body; - } - else { - request.body = body; - } - for (const opt of ['from', 'size', 'scroll']) { - request[opt] = options[opt]; - } - - const opts = { verb: options.verb || 'POST', ...options }; - return this.query(request, opts) - .then(response => ({ response, request, opts })); - } - - update (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'update', - retryOnConflict: options.retryOnConflict, - source: options.source - }; - - return this.query(request, options) - .then(response => response.result); - } - - updateByQuery(index, collection, searchQuery, changes, options = {}) { - const request = { - index, - collection, - body: {query: searchQuery, changes}, - action: 'updateByQuery', - source: options.source - }; - - return this.query(request, options) - .then(response => response.result); - } - - validate (index, collection, body, options = {}) { - return this.query({ - index, - collection, - body, - action: 'validate' - }, options) - .then(response => response.result); - } -} - -module.exports = { DocumentController }; diff --git a/src/controllers/Document.ts b/src/controllers/Document.ts new file mode 100644 index 000000000..c79d48e69 --- /dev/null +++ b/src/controllers/Document.ts @@ -0,0 +1,805 @@ +import { BaseController } from './Base'; +import { SearchResult } from '../core/searchResult/SearchResultBase'; +import { DocumentsSearchResult } from '../core/searchResult/Document'; +import { JSONObject, Document, DocumentHit } from '../utils/interfaces'; + +export class DocumentController extends BaseController { + constructor (kuzzle) { + super(kuzzle, 'document'); + } + + /** + * Counts documents in a collection. + * + * A query can be provided to alter the count result, + * otherwise returns the total number of documents in the collection. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/count/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns The number of matching documents + */ + count ( + index: string, + collection: string, + body: JSONObject = null, + options: { queuable?: boolean } = {} + ): Promise { + const request = { + index, + collection, + body, + action: 'count' + }; + + return this.query(request, options) + .then(response => response.result.count); + } + + /** + * Creates a new document in the persistent data storage. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/create/ + * + * @param index Index name + * @param collection Collection name + * @param content Document content + * @param _id Optional document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The created document + */ + create ( + index: string, + collection: string, + content: JSONObject, + _id: string = null, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'create' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates a new document in the persistent data storage, + * or replaces its content if it already exists. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/create-or-replace/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The created or replaced document + */ + createOrReplace ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'createOrReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Deletes a document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/delete/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The document ID + */ + delete ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'delete' + }; + + return this.query(request, options) + .then(response => response.result._id); + } + + /** + * Deletes documents matching the provided search query. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/delete-by-query/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The deleted documents IDs + */ + deleteByQuery( + index: string, + collection: string, + query: JSONObject = {}, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise> { + const request = { + index, + collection, + body: query, + action: 'deleteByQuery' + }; + + return this.query(request, options) + .then(response => response.result.ids); + } + + /** + * Checks if the given document exists. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/exists/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns True if the document exists + */ + exists ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'exists' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Gets a document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/get/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The document + */ + get ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'get' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-create/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mCreate ( + index: string, + collection: string, + documents: Array<{ + /** + * Optional document ID + */ + _id?: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully created documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mCreate' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates or replaces multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-create-or-replace/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mCreateOrReplace ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully created documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mCreateOrReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Deletes multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-delete/ + * + * @param index Index name + * @param collection Collection name + * @param ids Document IDs + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mDelete ( + index: string, + collection: string, + ids: Array, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully deleted documents IDS + */ + successes: Array; + /** + * Array of failed deletion + */ + errors: Array<{ + /** + * Document ID + */ + id: string; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: {ids}, + action: 'mDelete' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Gets multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-get/ + * + * @param index Index name + * @param collection Collection name + * @param ids Document IDs + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `verb` (HTTP only) Forces the verb of the route + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mGet ( + index: string, + collection: string, + ids: Array, + options: { queuable?: boolean, verb?: string } = {} + ): Promise<{ + /** + * Array of successfully retrieved documents + */ + successes: Array; + /** + * Array of the IDs of not found documents. + */ + errors: Array; + }> { + const request = { + index, + collection, + action: 'mGet', + body: { ids } + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Replaces multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-replace/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mReplace ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully replaced documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Updates multiple documents. + * + * Conflicts may occur if the same document gets updated multiple times + * within a short timespan in a database cluster. (See `retryOnConflict`) + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-update/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `retryOnConflict` Number of times the database layer should retry in case of version conflict + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mUpdate ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string, retryOnConflict?: number } = {} + ): Promise<{ + /** + * Array of successfully updated documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: {documents}, + action: 'mUpdate' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Replaces the content of an existing document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/replace/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The replaced document + */ + replace ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'replace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Searches documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/search/ + * + * @param index Index name + * @param collection Collection name + * @param query Search query + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `from` Offset of the first document to fetch + * - `size` Maximum number of documents to retrieve per page + * - `scroll` When set, gets a forward-only cursor having its ttl set to the given value (e.g. `30s`) + * - `verb` (HTTP only) Forces the verb of the route + * + * @returns A SearchResult + */ + search ( + index: string, + collection: string, + query: JSONObject = {}, + options: { + queuable?: boolean; + from?: number; + size?: number; + scroll?: string; + verb?: string; + } = {} + ): Promise> { + return this._search(index, collection, query, options) + .then(({ response, request, opts }) => ( + new DocumentsSearchResult(this.kuzzle, request, opts, response.result) + )); + } + + private _search ( + index: string, + collection: string, + body: JSONObject = {}, + options: JSONObject = {} + ) { + const request: any = { + index, + collection, + body: null, + action: 'search', + }; + if ( this.kuzzle.protocol.name === 'http' + && options.verb + && options.verb.toLowerCase() === 'get' + ) { + request.searchBody = body; + } + else { + request.body = body; + } + for (const opt of ['from', 'size', 'scroll']) { + request[opt] = options[opt]; + } + + const opts = { verb: options.verb || 'POST', ...options }; + return this.query(request, opts) + .then(response => ({ response, request, opts })); + } + + /** + * Updates the content of an existing document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/update/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `retryOnConflict` Number of times the database layer should retry in case of version conflict + * - `source` If true, returns the updated document inside the response + * + * @returns The replaced document + */ + update ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { + queuable?: boolean, + refresh?: string, + retryOnConflict?: number, + source?: boolean + } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'update', + retryOnConflict: options.retryOnConflict, + source: options.source + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Updates documents matching the provided search query. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/update-by-query/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param changes Partial changes to apply to the documents + * @param options Additional options + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `source` If true, returns the updated document inside the response + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + updateByQuery( + index: string, + collection: string, + query: JSONObject, + changes: JSONObject, + options: { refresh?: string, source?: boolean } = {} + ): Promise<{ + /** + * Array of successfully updated documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { query, changes }, + action: 'updateByQuery', + source: options.source + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Validates a document against existing validation rules. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/validate/ + * + * @param index Index name + * @param collection Collection name + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns True if the document is valid + */ + validate ( + index: string, + collection: string, + content: JSONObject, + options: { queuable?: boolean } = {} + ): Promise { + return this.query({ + index, + collection, + body: content, + action: 'validate' + }, options) + .then(response => response.result); + } +} + +module.exports = { DocumentController }; diff --git a/src/core/searchResult/Document.js b/src/core/searchResult/Document.ts similarity index 65% rename from src/core/searchResult/Document.js rename to src/core/searchResult/Document.ts index f4104eedc..9902d6864 100644 --- a/src/core/searchResult/Document.js +++ b/src/core/searchResult/Document.ts @@ -1,7 +1,7 @@ -const { SearchResultBase } = require('./SearchResultBase'); - -class DocumentsSearchResult extends SearchResultBase { +import { SearchResultBase } from './SearchResultBase'; +import { DocumentHit } from '../../utils/interfaces'; +export class DocumentsSearchResult extends SearchResultBase { /** * @param {Kuzzle} kuzzle * @param {object} query diff --git a/src/core/searchResult/SearchResultBase.js b/src/core/searchResult/SearchResultBase.ts similarity index 70% rename from src/core/searchResult/SearchResultBase.js rename to src/core/searchResult/SearchResultBase.ts index b9c8af7f2..a6c2da445 100644 --- a/src/core/searchResult/SearchResultBase.js +++ b/src/core/searchResult/SearchResultBase.ts @@ -1,4 +1,59 @@ -class SearchResultBase { +import { JSONObject, KuzzleRequest } from '../../utils/interfaces'; +import { Kuzzle } from '../../Kuzzle'; + +export interface SearchResult { + /** + * Search aggregations + */ + aggregations?: JSONObject; + + /** + * Page results + */ + hits: Array; + + /** + * Total number of items that can be retrieved + */ + total: number; + + /** + * Number of retrieved items so far + */ + fetched: number; + + /** + * Advances through the search results and returns the next page of items. + * + * @see https://docs.kuzzle.io/sdk/js/7/core-classes/search-result/next/ + * + * @example + * while (result) { + * // process result.hits here + * result = await result.next(); + * } + * + * @returns A SearchResult or null if no more pages + */ + next (): Promise | null>; +} + +export class SearchResultBase implements SearchResult { + protected _searchAction: string; + protected _scrollAction: string; + protected _controller: string; + protected _request: KuzzleRequest; + protected _kuzzle: Kuzzle; + protected _options: JSONObject; + protected _response: JSONObject; + + public aggregations?: JSONObject; + + public hits: Array; + + public total: number; + + public fetched: number; /** * @@ -7,7 +62,12 @@ class SearchResultBase { * @param {object} options * @param {object} response */ - constructor (kuzzle, request = {}, options = {}, response = {}) { + constructor ( + kuzzle: Kuzzle, + request: KuzzleRequest = {}, + options: any = {}, + response: any = {} + ) { Reflect.defineProperty(this, '_kuzzle', { value: kuzzle }); @@ -31,7 +91,7 @@ class SearchResultBase { this.total = response.total || 0; } - next () { + next (): Promise | null> { if (this.fetched >= this.total) { return Promise.resolve(null); } @@ -120,7 +180,9 @@ class SearchResultBase { } _buildNextSearchResult (response) { - const nextSearchResult = new this.constructor(this._kuzzle, this._request, this._options, response.result); + const Constructor: any = this.constructor; + + const nextSearchResult = new Constructor(this._kuzzle, this._request, this._options, response.result); nextSearchResult.fetched += this.fetched; return nextSearchResult; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 7732cdb03..6062231a3 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -129,3 +129,59 @@ export interface ApiKey { token: string; } } + +/** + * Kuzzle document + * + * @property _id + * @property _version + * @property _source + */ +export interface Document { + /** + * Document unique ID + */ + _id?: string; + /** + * Document Version (generated by Elasticsearch) + */ + _version?: number; + /** + * Document Content + */ + _source?: { + [key: string]: JSONObject | any, + /** + * Kuzzle metadata + * @see https://docs.kuzzle.io/core/2/guides/essentials/document-metadata/ + */ + _kuzzle_info?: { + /** + * Kuid of the user who created the document + */ + author: string, + /** + * Creation date in micro-timestamp + */ + createdAt: number, + /** + * Kuid of the user who last updated the document + */ + updater: string | null, + /** + * Update date in micro-timestamp + */ + updatedAt: number | null + } + }; +} + +/** + * Document retrieved from a search + */ +export interface DocumentHit extends Document { + /** + * Relevance score + */ + _score: number; +} diff --git a/tsconfig.json b/tsconfig.json index 87d3ed6ac..1ea049850 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "target": "es2020", "moduleResolution": "node", "sourceMap": true, - "baseUrl": "." + "baseUrl": ".", + "resolveJsonModule": true }, "rootDir": "src/", "include": [