[8.10] [Search] Include ingest pipeline in Getting Started code snippets (#165412) (#165695)

# Backport

This will backport the following commits from `main` to `8.10`:
- [[Search] Include ingest pipeline in Getting Started code snippets
(#165412)](https://github.com/elastic/kibana/pull/165412)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Rodney
Norris","email":"rodney.norris@elastic.co"},"sourceCommit":{"committedDate":"2023-09-05T13:20:39Z","message":"[Search]
Include ingest pipeline in Getting Started code snippets (#165412)\n\n##
Summary\r\n\r\nUpdated the LanguageDefinition to optionally take the
ingest pipeline\r\nand document keys in code snippet function
arguments.\r\n\r\nTo get the index pipeline info I added a new endpoint
that will check\r\nfor a custom pipeline\r\n\r\n###
Screenshots\r\ncURL\r\n\r\n![image](9f65fd8d-bb54-4035-adc6-f6767be59137)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"756599f7c2a63ed44fc5cad4c58084590ff56b84","branchLabelMapping":{"^v8.11.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["backport","release_note:skip","Team:EnterpriseSearch","v8.10.0","v8.11.0"],"number":165412,"url":"https://github.com/elastic/kibana/pull/165412","mergeCommit":{"message":"[Search]
Include ingest pipeline in Getting Started code snippets (#165412)\n\n##
Summary\r\n\r\nUpdated the LanguageDefinition to optionally take the
ingest pipeline\r\nand document keys in code snippet function
arguments.\r\n\r\nTo get the index pipeline info I added a new endpoint
that will check\r\nfor a custom pipeline\r\n\r\n###
Screenshots\r\ncURL\r\n\r\n![image](9f65fd8d-bb54-4035-adc6-f6767be59137)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"756599f7c2a63ed44fc5cad4c58084590ff56b84"}},"sourceBranch":"main","suggestedTargetBranches":["8.10"],"targetPullRequestStates":[{"branch":"8.10","label":"v8.10.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.11.0","labelRegex":"^v8.11.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/165412","number":165412,"mergeCommit":{"message":"[Search]
Include ingest pipeline in Getting Started code snippets (#165412)\n\n##
Summary\r\n\r\nUpdated the LanguageDefinition to optionally take the
ingest pipeline\r\nand document keys in code snippet function
arguments.\r\n\r\nTo get the index pipeline info I added a new endpoint
that will check\r\nfor a custom pipeline\r\n\r\n###
Screenshots\r\ncURL\r\n\r\n![image](9f65fd8d-bb54-4035-adc6-f6767be59137)\r\n\r\n\r\n###
Checklist\r\n\r\n- [x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"756599f7c2a63ed44fc5cad4c58084590ff56b84"}}]}]
BACKPORT-->

Co-authored-by: Rodney Norris <rodney.norris@elastic.co>
This commit is contained in:
Kibana Machine 2023-09-05 10:31:14 -04:00 committed by GitHub
parent b6688495fb
commit 9a39402e93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 558 additions and 67 deletions

View file

@ -24,6 +24,8 @@ export interface LanguageDefinitionSnippetArguments {
apiKey: string;
indexName?: string;
cloudId?: string;
ingestPipeline?: string;
extraIngestDocumentValues?: Record<string, boolean>;
}
type CodeSnippet = string | ((args: LanguageDefinitionSnippetArguments) => string);

View file

@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { mockHttpValues } from '../../../__mocks__/kea_logic';
import { nextTick } from '@kbn/test-jest-helpers';
import { fetchIndexPipelineParams } from './fetch_index_pipeline_parameters';
describe('FetchIndexPipelineParametersApiLogic', () => {
const { http } = mockHttpValues;
beforeEach(() => {
jest.clearAllMocks();
});
describe('fetchIndexPipelineParams', () => {
it('calls correct api', async () => {
const response = {
'pipeline-name': {},
};
const promise = Promise.resolve(response);
http.get.mockReturnValue(promise);
const result = fetchIndexPipelineParams({ indexName: 'index-name' });
await nextTick();
expect(http.get).toHaveBeenCalledWith(
'/internal/enterprise_search/indices/index-name/pipeline_parameters'
);
await expect(result).resolves.toEqual(response);
});
});
});

View file

@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IngestPipelineParams } from '../../../../../common/types/connectors';
import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { HttpLogic } from '../../../shared/http';
export interface FetchIndexPipelineParametersArgs {
indexName: string;
}
export type FetchIndexPipelineParametersResponse = IngestPipelineParams;
export const fetchIndexPipelineParams = async ({ indexName }: FetchIndexPipelineParametersArgs) => {
const route = `/internal/enterprise_search/indices/${indexName}/pipeline_parameters`;
return await HttpLogic.values.http.get<FetchIndexPipelineParametersResponse>(route);
};
export const FetchIndexPipelineParametersApiLogic = createApiLogic(
['fetch_index_pipeline_params_api_logic'],
fetchIndexPipelineParams,
{
showErrorFlash: false,
}
);
export type FetchIndexPipelineParametersApiLogicActions = Actions<
FetchIndexPipelineParametersArgs,
FetchIndexPipelineParametersResponse
>;

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { css } from '@emotion/react';
import dedent from 'dedent';
@ -29,6 +29,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';
import {
SelectClientPanel,
LanguageDefinition,
LanguageDefinitionSnippetArguments,
LanguageClientPanel,
InstallClientPanel,
OverviewPanel,
@ -50,6 +51,7 @@ import { IndexViewLogic } from '../../index_view_logic';
import { OverviewLogic } from '../../overview.logic';
import { GenerateApiKeyModal } from '../generate_api_key_modal/modal';
import { consoleDefinition } from './languages/console';
import { javascriptDefinition } from './languages/javascript';
import { languageDefinitions } from './languages/languages';
@ -57,17 +59,28 @@ const DEFAULT_URL = 'https://localhost:9200';
export const APIGettingStarted = () => {
const { http } = useValues(HttpLogic);
const { apiKey, isGenerateModalOpen } = useValues(OverviewLogic);
const { openGenerateModal, closeGenerateModal } = useActions(OverviewLogic);
const { apiKey, isGenerateModalOpen, indexPipelineParameters } = useValues(OverviewLogic);
const { fetchIndexPipelineParameters, openGenerateModal, closeGenerateModal } =
useActions(OverviewLogic);
const { indexName } = useValues(IndexViewLogic);
const { services } = useKibana<KibanaDeps>();
const cloudContext = useCloudDetails();
const codeArgs = {
useEffect(() => {
fetchIndexPipelineParameters({ indexName });
}, [indexName]);
const codeArgs: LanguageDefinitionSnippetArguments = {
apiKey,
cloudId: cloudContext.cloudId,
extraIngestDocumentValues: {
_extract_binary_content: indexPipelineParameters.extract_binary_content,
_reduce_whitespace: indexPipelineParameters.reduce_whitespace,
_run_ml_inference: indexPipelineParameters.run_ml_inference,
},
indexName,
ingestPipeline: indexPipelineParameters.name,
url: cloudContext.elasticsearchUrl || DEFAULT_URL,
};
const assetBasePath = http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/client_libraries/`);
@ -344,7 +357,11 @@ export const APIGettingStarted = () => {
<CodeBox
languages={languageDefinitions}
codeSnippet={getLanguageDefinitionCodeSnippet(selectedLanguage, 'ingestData', codeArgs)}
consoleRequest={getConsoleRequest('ingestData')}
consoleRequest={getLanguageDefinitionCodeSnippet(
consoleDefinition,
'ingestData',
codeArgs
)}
selectedLanguage={selectedLanguage}
setSelectedLanguage={setSelectedLanguage}
assetBasePath={assetBasePath}
@ -375,7 +392,11 @@ export const APIGettingStarted = () => {
'buildSearchQuery',
codeArgs
)}
consoleRequest={getConsoleRequest('buildSearchQuery')}
consoleRequest={getLanguageDefinitionCodeSnippet(
consoleDefinition,
'buildSearchQuery',
codeArgs
)}
selectedLanguage={selectedLanguage}
setSelectedLanguage={setSelectedLanguage}
assetBasePath={assetBasePath}

View file

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { LanguageDefinition } from '@kbn/search-api-panels';
import { ingestKeysToJSON } from './helpers';
export const consoleDefinition: Partial<LanguageDefinition> = {
buildSearchQuery: ({ indexName }) => `POST /${indexName ?? 'books'}/_search?pretty
{
"query": {
"query_string": {
"query": "snow"
}
}
}`,
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : '';
return `POST _bulk?pretty${ingestPipeline ? `&pipeline=${ingestPipeline}` : ''}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311${ingestDocumentKeys}}`;
},
};

View file

@ -10,6 +10,8 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToJSON } from './helpers';
export const curlDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `curl -X POST "\$\{ES_URL\}/${indexName}/_search?pretty" \\
-H "Authorization: ApiKey "\$\{API_KEY\}"" \\
@ -33,23 +35,28 @@ export API_KEY="${apiKey}"`,
},
iconType: 'curl.svg',
id: Languages.CURL,
ingestData: ({ indexName }) => `curl -X POST "\$\{ES_URL\}/_bulk?pretty" \\
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : '';
return `curl -X POST "\$\{ES_URL\}/_bulk?pretty${
ingestPipeline ? `&pipeline=${ingestPipeline}` : ''
}" \\
-H "Authorization: ApiKey "\$\{API_KEY\}"" \\
-H "Content-Type: application/json" \\
-d'
{ "index" : { "_index" : "${indexName}" } }
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470}
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585}
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328}
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227}
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268}
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268${ingestDocumentKeys}}
{ "index" : { "_index" : "${indexName}" } }
{"name": "The Handmaid'"'"'s Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311}
'`,
{"name": "The Handmaid'"'"'s Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311${ingestDocumentKeys}}
'`;
},
ingestDataIndex: '',
installClient: `# if cURL is not already installed on your system
# then install it with the package manager of your choice

View file

@ -10,6 +10,8 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToJSON } from './helpers';
export const goDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `searchResp, err := es.Search(
es.Search.WithContext(context.Background()),
@ -56,27 +58,32 @@ if err != nil {
},
iconType: 'go.svg',
id: Languages.GO,
ingestData: ({ indexName }) => `buf := bytes.NewBufferString(\`
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : '';
return `buf := bytes.NewBufferString(\`
{"index":{"_id":"9780553351927"}}
{"name":"Snow Crash","author":"Neal Stephenson","release_date":"1992-06-01","page_count": 470}
{"name":"Snow Crash","author":"Neal Stephenson","release_date":"1992-06-01","page_count": 470${ingestDocumentKeys}}
{ "index": { "_id": "9780441017225"}}
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585}
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585${ingestDocumentKeys}}
{ "index": { "_id": "9780451524935"}}
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328}
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328${ingestDocumentKeys}}
{ "index": { "_id": "9781451673319"}}
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227}
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227${ingestDocumentKeys}}
{ "index": { "_id": "9780060850524"}}
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268}
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268${ingestDocumentKeys}}
{ "index": { "_id": "9780385490818"}}
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311}
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311${ingestDocumentKeys}}
\`)
ingestResult, err := es.Bulk(
bytes.NewReader(buf.Bytes()),
es.Bulk.WithIndex("${indexName}"),
es.Bulk.WithIndex("${indexName}"),${
ingestPipeline ? `\n es.Bulk.WithPipeline("${ingestPipeline}"),` : ''
}
)
fmt.Println(ingestResult, err)`,
fmt.Println(ingestResult, err)`;
},
ingestDataIndex: '',
installClient: 'go get github.com/elastic/go-elasticsearch/v8@latest',
name: i18n.translate('xpack.enterpriseSearch.languages.go', {

View file

@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ingestKeysToJSON, ingestKeysToPHP, ingestKeysToRuby } from './helpers';
describe('getting started language helpers', () => {
describe('ingestKeysToJSON', () => {
it('return empty string when given undefined', () => {
expect(ingestKeysToJSON(undefined)).toEqual('');
});
it('return json keys with quotes when given expected data', () => {
expect(ingestKeysToJSON({ _foo: true, _bar: false })).toEqual(
', "_foo": true, "_bar": false'
);
});
});
describe('ingestKeysToPHP', () => {
it('return empty string when given undefined', () => {
expect(ingestKeysToPHP(undefined)).toEqual('');
});
it('return json keys with quotes when given expected data', () => {
expect(ingestKeysToPHP({ _foo: true, _bar: false })).toEqual(
`\n '_foo' => true,\n '_bar' => false,`
);
});
});
describe('ingestKeysToRuby', () => {
it('return empty string when given undefined', () => {
expect(ingestKeysToRuby(undefined)).toEqual('');
});
it('return json keys with quotes when given expected data', () => {
expect(ingestKeysToRuby({ _foo: true, _bar: false })).toEqual(', _foo: true, _bar: false');
});
});
});

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { LanguageDefinitionSnippetArguments } from '@kbn/search-api-panels';
export const ingestKeysToJSON = (
extraIngestDocumentValues: LanguageDefinitionSnippetArguments['extraIngestDocumentValues']
) =>
extraIngestDocumentValues
? Object.entries(extraIngestDocumentValues).reduce((result, value) => {
result += `, "${value[0]}": ${value[1]}`;
return result;
}, '')
: '';
export const ingestKeysToPHP = (
extraIngestDocumentValues: LanguageDefinitionSnippetArguments['extraIngestDocumentValues']
) =>
extraIngestDocumentValues
? Object.entries(extraIngestDocumentValues).reduce((result, value) => {
result += `\n '${value[0]}' => ${value[1]},`;
return result;
}, '')
: '';
export const ingestKeysToRuby = (
extraIngestDocumentValues: LanguageDefinitionSnippetArguments['extraIngestDocumentValues']
) =>
extraIngestDocumentValues
? Object.entries(extraIngestDocumentValues).reduce((result, value) => {
result += `, ${value[0]}: ${value[1]}`;
return result;
}, '')
: '';

View file

@ -10,11 +10,13 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToJSON } from './helpers';
export const javascriptDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `// Let's search!
const searchResult = await client.search({
index: '${indexName}',
q: '9HY9SWR'
q: 'snow'
});
console.log(searchResult.hits.hits)
@ -35,34 +37,38 @@ const client = new Client({
},
iconType: 'javascript.svg',
id: Languages.JAVASCRIPT,
ingestData: ({ indexName }) => `// Sample flight data
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : '';
return `// Sample data books
const dataset = [
{'flight': '9HY9SWR', 'price': 841.2656419677076, 'delayed': false},
{'flight': 'X98CCZO', 'price': 882.9826615595518, 'delayed': false},
{'flight': 'UFK2WIZ', 'price': 190.6369038508356, 'delayed': true},
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470${ingestDocumentKeys}},
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585${ingestDocumentKeys}},
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328${ingestDocumentKeys}},
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227${ingestDocumentKeys}},
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268${ingestDocumentKeys}},
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311${ingestDocumentKeys}},
];
// Index with the bulk helper
const result = await client.helpers.bulk({
datasource: dataset,
onDocument (doc) {
return { index: { _index: '${indexName}' }};
}
datasource: dataset,${ingestPipeline ? `\n pipeline: "${ingestPipeline}",` : ''}
onDocument: (doc) => ({ index: { _index: '${indexName}' }}),
});
console.log(result);
/**
{
total: 3,
total: 6,
failed: 0,
retry: 0,
successful: 3,
successful: 6,
noop: 0,
time: 421,
bytes: 293,
time: 82,
bytes: 1273,
aborted: false
}
*/`,
*/`;
},
ingestDataIndex: '',
installClient: 'npm install @elastic/elasticsearch@8',
name: i18n.translate('xpack.enterpriseSearch.languages.javascript', {
@ -80,10 +86,10 @@ console.log(resp);
version: {
build_flavor: 'default',
build_type: 'docker',
build_hash: 'c94b4700cda13820dad5aa74fae6db185ca5c304',
build_date: '2022-10-24T16:54:16.433628434Z',
build_snapshot: false,
lucene_version: '9.4.1',
build_hash: 'ca3dc3a882d76f14d2765906ce3b1cf421948d19',
build_date: '2023-08-28T11:24:16.383660553Z',
build_snapshot: true,
lucene_version: '9.7.0',
minimum_wire_compatibility_version: '7.17.0',
minimum_index_compatibility_version: '7.0.0'
},

View file

@ -10,6 +10,8 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToPHP } from './helpers';
export const phpDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `$params = [
'index' => '${indexName}',
@ -33,7 +35,9 @@ print_r($response->asArray());`,
},
iconType: 'php.svg',
id: Languages.PHP,
ingestData: ({ indexName }) => `$params = [
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToPHP(extraIngestDocumentValues) : '';
return `$params = [${ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : ''}
'body' => [
[
'index' => [
@ -45,7 +49,7 @@ print_r($response->asArray());`,
'name' => 'Snow Crash',
'author' => 'Neal Stephenson',
'release_date' => '1992-06-01',
'page_count' => 470,
'page_count' => 470,${ingestDocumentKeys}
],
[
'index' => [
@ -57,7 +61,7 @@ print_r($response->asArray());`,
'name' => 'Revelation Space',
'author' => 'Alastair Reynolds',
'release_date' => '2000-03-15',
'page_count' => 585,
'page_count' => 585,${ingestDocumentKeys}
],
[
'index' => [
@ -69,7 +73,7 @@ print_r($response->asArray());`,
'name' => '1984',
'author' => 'George Orwell',
'release_date' => '1985-06-01',
'page_count' => 328,
'page_count' => 328,${ingestDocumentKeys}
],
[
'index' => [
@ -81,7 +85,7 @@ print_r($response->asArray());`,
'name' => 'Fahrenheit 451',
'author' => 'Ray Bradbury',
'release_date' => '1953-10-15',
'page_count' => 227,
'page_count' => 227,${ingestDocumentKeys}
],
[
'index' => [
@ -93,7 +97,7 @@ print_r($response->asArray());`,
'name' => 'Brave New World',
'author' => 'Aldous Huxley',
'release_date' => '1932-06-01',
'page_count' => 268,
'page_count' => 268,${ingestDocumentKeys}
],
[
'index' => [
@ -102,17 +106,18 @@ print_r($response->asArray());`,
],
],
[
'name' => 'The Handmaid\'s Tale',
'name' => 'The Handmaid\\'s Tale',
'author' => 'Margaret Atwood',
'release_date' => '1985-06-01',
'page_count' => 311,
'page_count' => 311,${ingestDocumentKeys}
],
],
];
$response = $client->bulk($params);
echo $response->getStatusCode();
echo (string) $response->getBody();`,
echo (string) $response->getBody();`;
},
ingestDataIndex: '',
installClient: 'composer require elasticsearch/elasticsearch',
name: i18n.translate('xpack.enterpriseSearch.languages.php', {

View file

@ -10,6 +10,8 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToJSON } from './helpers';
export const pythonDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `client.search(index="${indexName}", q="snow")`,
configureClient: ({ url, apiKey }) => `from elasticsearch import Elasticsearch
@ -27,22 +29,25 @@ client = Elasticsearch(
},
iconType: 'python.svg',
id: Languages.PYTHON,
ingestData: ({ indexName }) => `documents = [
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : '';
return `documents = [
{ "index": { "_index": "${indexName}", "_id": "9780553351927"}},
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470},
{"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470${ingestDocumentKeys}},
{ "index": { "_index": "${indexName}", "_id": "9780441017225"}},
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585},
{"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585${ingestDocumentKeys}},
{ "index": { "_index": "${indexName}", "_id": "9780451524935"}},
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328},
{"name": "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328${ingestDocumentKeys}},
{ "index": { "_index": "${indexName}", "_id": "9781451673319"}},
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227},
{"name": "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227${ingestDocumentKeys}},
{ "index": { "_index": "${indexName}", "_id": "9780060850524"}},
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268},
{"name": "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268${ingestDocumentKeys}},
{ "index": { "_index": "${indexName}", "_id": "9780385490818"}},
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311},
{"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311${ingestDocumentKeys}},
]
client.bulk(operations=documents)`,
client.bulk(operations=documents${ingestPipeline ? `, pipeline="${ingestPipeline}"` : ''})`;
},
ingestDataIndex: '',
installClient: `python -m pip install elasticsearch

View file

@ -10,6 +10,8 @@ import { Languages, LanguageDefinition } from '@kbn/search-api-panels';
import { docLinks } from '../../../../../../shared/doc_links';
import { ingestKeysToRuby } from './helpers';
export const rubyDefinition: LanguageDefinition = {
buildSearchQuery: ({ indexName }) => `client.search(index: '${indexName}', q: 'snow')`,
configureClient: ({ url, apiKey, cloudId }) => `client = Elasticsearch::Client.new(
@ -26,15 +28,18 @@ export const rubyDefinition: LanguageDefinition = {
},
iconType: 'ruby.svg',
id: Languages.RUBY,
ingestData: ({ indexName }) => `documents = [
{ index: { _index: '${indexName}', data: {name: "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470} } },
{ index: { _index: '${indexName}', data: {name: "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585} } },
{ index: { _index: '${indexName}', data: {name: "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328} } },
{ index: { _index: '${indexName}', data: {name: "Fahrenheit 451", "author": "Ray Bradbury", "release_date": "1953-10-15", "page_count": 227} } },
{ index: { _index: '${indexName}', data: {name: "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268} } },
{ index: { _index: '${indexName}', data: {name: "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311} } }
ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => {
const ingestDocumentKeys = ingestPipeline ? ingestKeysToRuby(extraIngestDocumentValues) : '';
return `documents = [
{ index: { _index: '${indexName}', data: {name: "Snow Crash", author: "Neal Stephenson", release_date: "1992-06-01", page_count: 470${ingestDocumentKeys}} } },
{ index: { _index: '${indexName}', data: {name: "Revelation Space", author: "Alastair Reynolds", release_date: "2000-03-15", page_count: 585${ingestDocumentKeys}} } },
{ index: { _index: '${indexName}', data: {name: "1984", author: "George Orwell", release_date: "1985-06-01", page_count: 328${ingestDocumentKeys}} } },
{ index: { _index: '${indexName}', data: {name: "Fahrenheit 451", author: "Ray Bradbury", release_date: "1953-10-15", page_count: 227${ingestDocumentKeys}} } },
{ index: { _index: '${indexName}', data: {name: "Brave New World", author: "Aldous Huxley", release_date: "1932-06-01", page_count: 268${ingestDocumentKeys}} } },
{ index: { _index: '${indexName}', data: {name: "The Handmaid's Tale", author: "Margaret Atwood", release_date: "1985-06-01", page_count: 311${ingestDocumentKeys}} } }
]
client.bulk(body: documents)`,
client.bulk(body: documents${ingestPipeline ? `, pipeline: "${ingestPipeline}"` : ''})`;
},
ingestDataIndex: '',
installClient: `$ gem install elasticsearch`,
name: i18n.translate('xpack.enterpriseSearch.languages.ruby', {

View file

@ -7,7 +7,9 @@
import { kea, MakeLogicType } from 'kea';
import { DEFAULT_PIPELINE_VALUES } from '../../../../../common/constants';
import { Status } from '../../../../../common/types/api';
import { IngestPipelineParams } from '../../../../../common/types/connectors';
import { KibanaLogic } from '../../../shared/kibana';
import { GenerateApiKeyLogic } from '../../api/generate_api_key/generate_api_key_logic';
@ -15,6 +17,10 @@ import {
CachedFetchIndexApiLogic,
CachedFetchIndexApiLogicActions,
} from '../../api/index/cached_fetch_index_api_logic';
import {
FetchIndexPipelineParametersApiLogic,
FetchIndexPipelineParametersApiLogicActions,
} from '../../api/pipelines/fetch_index_pipeline_parameters';
import { SEARCH_INDICES_PATH } from '../../routes';
@ -22,6 +28,7 @@ interface OverviewLogicActions {
apiError: CachedFetchIndexApiLogicActions['apiError'];
apiReset: typeof GenerateApiKeyLogic.actions.apiReset;
closeGenerateModal: void;
fetchIndexPipelineParameters: FetchIndexPipelineParametersApiLogicActions['makeRequest'];
openGenerateModal: void;
toggleClientsPopover: void;
toggleManageApiKeyPopover: void;
@ -32,6 +39,8 @@ interface OverviewLogicValues {
apiKeyData: typeof GenerateApiKeyLogic.values.data;
apiKeyStatus: typeof GenerateApiKeyLogic.values.status;
indexData: typeof CachedFetchIndexApiLogic.values.indexData;
indexPipelineData: typeof FetchIndexPipelineParametersApiLogic.values.data;
indexPipelineParameters: IngestPipelineParams;
isClientsPopoverOpen: boolean;
isError: boolean;
isGenerateModalOpen: boolean;
@ -48,12 +57,21 @@ export const OverviewLogic = kea<MakeLogicType<OverviewLogicValues, OverviewLogi
toggleManageApiKeyPopover: true,
},
connect: {
actions: [CachedFetchIndexApiLogic, ['apiError'], GenerateApiKeyLogic, ['apiReset']],
actions: [
CachedFetchIndexApiLogic,
['apiError'],
GenerateApiKeyLogic,
['apiReset'],
FetchIndexPipelineParametersApiLogic,
['makeRequest as fetchIndexPipelineParameters'],
],
values: [
CachedFetchIndexApiLogic,
['indexData', 'status'],
GenerateApiKeyLogic,
['data as apiKeyData', 'status as apiKeyStatus'],
FetchIndexPipelineParametersApiLogic,
['data as indexPipelineData'],
],
},
listeners: ({ actions }) => ({
@ -95,6 +113,11 @@ export const OverviewLogic = kea<MakeLogicType<OverviewLogicValues, OverviewLogi
(apiKeyStatus, apiKeyData) =>
apiKeyStatus === Status.SUCCESS ? apiKeyData.apiKey.encoded : '',
],
indexPipelineParameters: [
() => [selectors.indexPipelineData],
(indexPipelineData: typeof FetchIndexPipelineParametersApiLogic.values.data) =>
indexPipelineData ?? DEFAULT_PIPELINE_VALUES,
],
isError: [() => [selectors.status], (status) => status === Status.ERROR],
isLoading: [
() => [selectors.status, selectors.indexData],

View file

@ -0,0 +1,164 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IScopedClusterClient } from '@kbn/core/server';
import { DEFAULT_PIPELINE_VALUES } from '../../../common/constants';
import { getIndexPipelineParameters } from './get_index_pipeline';
describe('getIndexPipelineParameters', () => {
const defaultMockClient = () => ({
asCurrentUser: {
get: jest.fn().mockResolvedValue({}),
indices: {
getMapping: jest.fn().mockResolvedValue({}),
},
ingest: {
getPipeline: jest.fn().mockRejectedValue('Pipeline not found'),
},
search: jest.fn().mockResolvedValue({
hits: {
hits: [],
},
}),
},
});
let mockClient = defaultMockClient();
let client: IScopedClusterClient;
beforeEach(() => {
jest.resetAllMocks();
mockClient = defaultMockClient();
client = mockClient as unknown as IScopedClusterClient;
});
it('returns default pipeline if custom not found', async () => {
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual(
DEFAULT_PIPELINE_VALUES
);
});
it('returns connector pipeline params if found', async () => {
mockClient.asCurrentUser.search = jest.fn().mockResolvedValue({
hits: {
hits: [
{
_id: 'unit-test',
_source: {},
},
],
},
});
mockClient.asCurrentUser.get = jest.fn().mockResolvedValue({
_id: 'unit-test',
_source: {
pipeline: {
extract_binary_content: false,
name: 'unit-test-pipeline',
reduce_whitespace: true,
run_ml_inference: true,
},
},
});
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual({
extract_binary_content: false,
name: 'unit-test-pipeline',
reduce_whitespace: true,
run_ml_inference: true,
});
});
it('returns default pipeline if fetch custom throws', async () => {
mockClient.asCurrentUser.ingest.getPipeline = jest.fn().mockRejectedValue('Boom');
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual(
DEFAULT_PIPELINE_VALUES
);
});
it('returns custom pipeline if found', async () => {
mockClient.asCurrentUser.ingest.getPipeline = jest.fn().mockResolvedValueOnce({
'my-index': {
fake: 'ingest-pipeline',
},
});
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual({
extract_binary_content: true,
name: 'my-index',
reduce_whitespace: true,
run_ml_inference: false,
});
});
it('returns default connector index pipeline if found in mapping', async () => {
mockClient.asCurrentUser.indices.getMapping = jest.fn().mockResolvedValueOnce({
'.elastic-connectors-v1': {
mappings: {
_meta: {
pipeline: {
default_extract_binary_content: false,
default_name: 'my-unit-test-index',
default_reduce_whitespace: false,
default_run_ml_inference: true,
},
},
},
},
});
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual({
extract_binary_content: false,
name: 'my-unit-test-index',
reduce_whitespace: false,
run_ml_inference: true,
});
});
it('returns connector params with custom pipeline name', async () => {
mockClient.asCurrentUser.indices.getMapping = jest.fn().mockResolvedValueOnce({
'.elastic-connectors-v1': {
mappings: {
_meta: {
pipeline: {
default_extract_binary_content: false,
default_name: 'my-unit-test-index',
default_reduce_whitespace: false,
default_run_ml_inference: true,
},
},
},
},
});
mockClient.asCurrentUser.ingest.getPipeline = jest.fn().mockResolvedValueOnce({
'my-index': {
fake: 'ingest-pipeline',
},
});
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual({
extract_binary_content: false,
name: 'my-index',
reduce_whitespace: false,
run_ml_inference: true,
});
});
it('returns defaults if get mapping fails with IndexNotFoundException', async () => {
mockClient.asCurrentUser.indices.getMapping = jest.fn().mockRejectedValue({
meta: {
body: {
error: {
type: 'index_not_found_exception',
},
},
},
});
await expect(getIndexPipelineParameters('my-index', client)).resolves.toEqual(
DEFAULT_PIPELINE_VALUES
);
});
it('throws if get mapping fails with non-IndexNotFoundException', async () => {
mockClient.asCurrentUser.indices.getMapping = jest.fn().mockRejectedValue('Boom');
await expect(getIndexPipelineParameters('my-index', client)).rejects.toEqual('Boom');
});
});

View file

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IScopedClusterClient } from '@kbn/core/server';
import { IngestPipelineParams } from '../../../common/types/connectors';
import { fetchConnectorByIndexName } from '../connectors/fetch_connectors';
import { getDefaultPipeline } from './get_default_pipeline';
export const getIndexPipelineParameters = async (
indexName: string,
client: IScopedClusterClient
): Promise<IngestPipelineParams> => {
// Get the default pipeline data and check for a custom pipeline in parallel
// we want to throw the error if getDefaultPipeline() fails so we're not catching it on purpose
const [defaultPipeline, connector, customPipelineResp] = await Promise.all([
getDefaultPipeline(client),
fetchConnectorByIndexName(client, indexName),
client.asCurrentUser.ingest
.getPipeline({
id: `${indexName}`,
})
.catch(() => null),
]);
if (connector && connector.pipeline) {
return connector.pipeline;
}
let pipelineName = defaultPipeline.name;
if (customPipelineResp && customPipelineResp[indexName]) {
pipelineName = indexName;
}
return {
...defaultPipeline,
name: pipelineName,
};
};

View file

@ -44,6 +44,7 @@ import { startMlModelDownload } from '../../lib/ml/start_ml_model_download';
import { createIndexPipelineDefinitions } from '../../lib/pipelines/create_pipeline_definitions';
import { deleteIndexPipelines } from '../../lib/pipelines/delete_pipelines';
import { getCustomPipelines } from '../../lib/pipelines/get_custom_pipelines';
import { getIndexPipelineParameters } from '../../lib/pipelines/get_index_pipeline';
import { getPipeline } from '../../lib/pipelines/get_pipeline';
import { getMlInferencePipelines } from '../../lib/pipelines/ml_inference/get_ml_inference_pipelines';
import { revertCustomPipeline } from '../../lib/pipelines/revert_custom_pipeline';
@ -354,6 +355,26 @@ export function registerIndexRoutes({
})
);
router.get(
{
path: '/internal/enterprise_search/indices/{indexName}/pipeline_parameters',
validate: {
params: schema.object({
indexName: schema.string(),
}),
},
},
elasticsearchErrorHandler(log, async (context, request, response) => {
const indexName = decodeURIComponent(request.params.indexName);
const { client } = (await context.core).elasticsearch;
const body = await getIndexPipelineParameters(indexName, client);
return response.ok({
body,
headers: { 'content-type': 'application/json' },
});
})
);
router.get(
{
path: '/internal/enterprise_search/indices/{indexName}/ml_inference/pipeline_processors',