Răsfoiți Sursa

Merge pull request #7592 from weseek/support/elasticsearch8

support: Elasticsearch8
Yuki Takei 2 ani în urmă
părinte
comite
ae345b0c76

+ 2 - 2
.devcontainer/docker-compose.yml

@@ -49,7 +49,7 @@ services:
       context: ../../growi-docker-compose/elasticsearch
       dockerfile: ./Dockerfile
       args:
-        - version=7.17.9
+        - version=8.7.0
     restart: unless-stopped
     ports:
       - 9200:9200
@@ -67,7 +67,7 @@ services:
 
   #need to adjust kibana version based on elasticsearch version (use same version as elasticsearch version)
   kibana:
-    image: docker.elastic.co/kibana/kibana:7.17.9
+    image: docker.elastic.co/kibana/kibana:8.7.0
     restart: unless-stopped
     environment:
       ELASTICSEARCH_HOSTS: 'http://elasticsearch:9200'

+ 1 - 1
apps/app/package.json

@@ -55,8 +55,8 @@
     "@aws-sdk/client-s3": "^3.58.0",
     "@aws-sdk/s3-request-presigner": "^3.58.0",
     "@browser-bunyan/console-formatted-stream": "^1.8.0",
-    "@elastic/elasticsearch6": "npm:@elastic/elasticsearch@^6.8.8",
     "@elastic/elasticsearch7": "npm:@elastic/elasticsearch@^7.17.0",
+    "@elastic/elasticsearch8": "npm:@elastic/elasticsearch@^8.7.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
     "@growi/core": "^6.1.0-RC.0",

+ 0 - 120
apps/app/resource/search/mappings-es6.json

@@ -1,120 +0,0 @@
-{
-  "settings": {
-    "analysis": {
-      "filter": {
-        "english_stop": {
-          "type":       "stop",
-          "stopwords":  "_english_"
-        }
-      },
-      "tokenizer": {
-        "edge_ngram_tokenizer": {
-          "type": "edge_ngram",
-          "min_gram": 2,
-          "max_gram": 20,
-          "token_chars": ["letter", "digit"]
-        }
-      },
-      "analyzer": {
-        "japanese": {
-          "tokenizer": "kuromoji_tokenizer",
-          "char_filter" : ["icu_normalizer"]
-        },
-        "english_edge_ngram": {
-          "tokenizer": "edge_ngram_tokenizer",
-          "filter": [
-            "lowercase",
-            "english_stop"
-          ]
-        }
-      }
-    }
-  },
-  "mappings": {
-    "pages": {
-      "properties" : {
-        "path": {
-          "type": "text",
-          "fields": {
-            "raw": {
-              "type": "text",
-              "analyzer": "keyword"
-            },
-            "ja": {
-              "type": "text",
-              "analyzer": "japanese"
-            },
-            "en": {
-              "type": "text",
-              "analyzer": "english_edge_ngram",
-              "search_analyzer": "standard"
-            }
-          }
-        },
-        "body": {
-          "type": "text",
-          "fields": {
-            "ja": {
-              "type": "text",
-              "analyzer": "japanese"
-            },
-            "en": {
-              "type": "text",
-              "analyzer": "english_edge_ngram",
-              "search_analyzer": "standard"
-            }
-          }
-        },
-        "comments": {
-          "type": "text",
-          "fields": {
-            "ja": {
-              "type": "text",
-              "analyzer": "japanese"
-            },
-            "en": {
-              "type": "text",
-              "analyzer": "english_edge_ngram",
-              "search_analyzer": "standard"
-            }
-          }
-        },
-        "username": {
-          "type": "keyword"
-        },
-        "comment_count": {
-          "type": "integer"
-        },
-        "bookmark_count": {
-          "type": "integer"
-        },
-        "seenUsers_count":{
-          "type": "integer"
-        },
-        "like_count": {
-          "type": "integer"
-        },
-        "grant": {
-          "type": "integer"
-        },
-        "granted_users": {
-          "type": "keyword"
-        },
-        "granted_group": {
-          "type": "keyword"
-        },
-        "created_at": {
-          "type": "date",
-          "format": "dateOptionalTime"
-        },
-        "updated_at": {
-          "type": "date",
-          "format": "dateOptionalTime"
-        },
-        "tag_names": {
-          "type": "keyword"
-        }
-      }
-    }
-  }
-}

+ 2 - 2
apps/app/resource/search/mappings-es7-for-ci.json → apps/app/resource/search/mappings-es8-for-ci.json

@@ -104,11 +104,11 @@
       },
       "created_at": {
         "type": "date",
-        "format": "dateOptionalTime"
+        "format": "date_optional_time"
       },
       "updated_at": {
         "type": "date",
-        "format": "dateOptionalTime"
+        "format": "date_optional_time"
       },
       "tag_names": {
         "type": "keyword"

+ 115 - 0
apps/app/resource/search/mappings-es8.json

@@ -0,0 +1,115 @@
+{
+  "settings": {
+    "analysis": {
+      "filter": {
+        "english_stop": {
+          "type":       "stop",
+          "stopwords":  "_english_"
+        }
+      },
+      "tokenizer": {
+        "edge_ngram_tokenizer": {
+          "type": "edge_ngram",
+          "min_gram": 2,
+          "max_gram": 20,
+          "token_chars": ["letter", "digit"]
+        }
+      },
+      "analyzer": {
+        "japanese": {
+          "tokenizer": "kuromoji_tokenizer",
+          "char_filter" : ["icu_normalizer"]
+        },
+        "english_edge_ngram": {
+          "tokenizer": "edge_ngram_tokenizer",
+          "filter": [
+            "lowercase",
+            "english_stop"
+          ]
+        }
+      }
+    }
+  },
+  "mappings": {
+    "properties" : {
+      "path": {
+        "type": "text",
+        "fields": {
+          "raw": {
+            "type": "text",
+            "analyzer": "keyword"
+          },
+          "ja": {
+            "type": "text",
+            "analyzer": "japanese"
+          },
+          "en": {
+            "type": "text",
+            "analyzer": "english_edge_ngram",
+            "search_analyzer": "standard"
+          }
+        }
+      },
+      "body": {
+        "type": "text",
+        "fields": {
+          "ja": {
+            "type": "text",
+            "analyzer": "japanese"
+          },
+          "en": {
+            "type": "text",
+            "analyzer": "english_edge_ngram",
+            "search_analyzer": "standard"
+          }
+        }
+      },
+      "comments": {
+        "type": "text",
+        "fields": {
+          "ja": {
+            "type": "text",
+            "analyzer": "japanese"
+          },
+          "en": {
+            "type": "text",
+            "analyzer": "english_edge_ngram",
+            "search_analyzer": "standard"
+          }
+        }
+      },
+      "username": {
+        "type": "keyword"
+      },
+      "comment_count": {
+        "type": "integer"
+      },
+      "bookmark_count": {
+        "type": "integer"
+      },
+      "like_count": {
+        "type": "integer"
+      },
+      "grant": {
+        "type": "integer"
+      },
+      "granted_users": {
+        "type": "keyword"
+      },
+      "granted_group": {
+        "type": "keyword"
+      },
+      "created_at": {
+        "type": "date",
+        "format": "date_optional_time"
+      },
+      "updated_at": {
+        "type": "date",
+        "format": "date_optional_time"
+      },
+      "tag_names": {
+        "type": "keyword"
+      }
+    }
+  }
+}

+ 1 - 1
apps/app/src/server/service/config-loader.ts

@@ -275,7 +275,7 @@ const ENV_VAR_NAME_TO_CONFIG_INFO = {
     ns:      'crowi',
     key:     'app:elasticsearchVersion',
     type:    ValueType.NUMBER,
-    default: 7,
+    default: 8,
   },
   ELASTICSEARCH_URI: {
     ns:      'crowi',

+ 58 - 37
apps/app/src/server/service/search-delegator/elasticsearch-client.ts

@@ -1,7 +1,8 @@
 /* eslint-disable implicit-arrow-linebreak */
 /* eslint-disable no-confusing-arrow */
-import { Client as ES6Client, ApiResponse as ES6ApiResponse, RequestParams as ES6RequestParams } from '@elastic/elasticsearch6';
 import { Client as ES7Client, ApiResponse as ES7ApiResponse, RequestParams as ES7RequestParams } from '@elastic/elasticsearch7';
+import { Client as ES8Client, estypes } from '@elastic/elasticsearch8';
+
 import {
   BulkResponse,
   CatAliasesResponse,
@@ -16,68 +17,88 @@ import {
   ReindexResponse,
 } from './elasticsearch-client-types';
 
-type ApiResponse<T = any, C = any> = ES6ApiResponse<T, C> | ES7ApiResponse<T, C>
-
 export default class ElasticsearchClient {
 
-  client: ES6Client | ES7Client;
+  client: ES7Client | ES8Client;
 
-  constructor(client: ES6Client | ES7Client) {
+  constructor(client: ES7Client | ES8Client) {
     this.client = client;
   }
 
-  bulk(params: ES6RequestParams.Bulk & ES7RequestParams.Bulk): Promise<ApiResponse<BulkResponse>> {
-    return this.client instanceof ES6Client ? this.client.bulk(params) : this.client.bulk(params);
+  async bulk(params: ES7RequestParams.Bulk & estypes.BulkRequest): Promise<BulkResponse | estypes.BulkResponse> {
+    return this.client instanceof ES7Client ? (await this.client.bulk(params)).body as BulkResponse : this.client.bulk(params);
   }
 
   // TODO: cat is not used in current Implementation, remove cat?
   cat = {
-    aliases: (params: ES6RequestParams.CatAliases & ES7RequestParams.CatAliases): Promise<ApiResponse<CatAliasesResponse>> =>
-      this.client instanceof ES6Client ? this.client.cat.aliases(params) : this.client.cat.aliases(params),
-    indices: (params: ES6RequestParams.CatIndices & ES7RequestParams.CatIndices): Promise<ApiResponse<CatIndicesResponse>> =>
-      this.client instanceof ES6Client ? this.client.cat.indices(params) : this.client.cat.indices(params),
+    aliases: (params: ES7RequestParams.CatAliases & estypes.CatAliasesRequest): Promise<ES7ApiResponse<CatAliasesResponse> | estypes.CatAliasesResponse> =>
+      this.client instanceof ES7Client ? this.client.cat.aliases(params) : this.client.cat.aliases(params),
+
+    indices: (params: ES7RequestParams.CatIndices & estypes.CatIndicesRequest): Promise<ES7ApiResponse<CatIndicesResponse> | estypes.CatAliasesResponse> =>
+      this.client instanceof ES7Client ? this.client.cat.indices(params) : this.client.cat.indices(params),
   };
 
   cluster = {
-    health: (params: ES6RequestParams.ClusterHealth & ES7RequestParams.ClusterHealth): Promise<ApiResponse<ClusterHealthResponse>> =>
-      this.client instanceof ES6Client ? this.client.cluster.health(params) : this.client.cluster.health(params),
+    health: (params: ES7RequestParams.ClusterHealth & estypes.ClusterHealthRequest)
+    : Promise<ES7ApiResponse<ClusterHealthResponse> | estypes.ClusterHealthResponse> =>
+      this.client instanceof ES7Client ? this.client.cluster.health(params) : this.client.cluster.health(params),
   };
 
   indices = {
-    create: (params: ES6RequestParams.IndicesCreate & ES7RequestParams.IndicesCreate) =>
-      this.client instanceof ES6Client ? this.client.indices.create(params) : this.client.indices.create(params),
-    delete: (params: ES6RequestParams.IndicesDelete & ES7RequestParams.IndicesDelete) =>
-      this.client instanceof ES6Client ? this.client.indices.delete(params) : this.client.indices.delete(params),
-    exists: (params: ES6RequestParams.IndicesExists & ES7RequestParams.IndicesExists): Promise<ApiResponse<IndicesExistsResponse>> =>
-      this.client instanceof ES6Client ? this.client.indices.exists(params) : this.client.indices.exists(params),
-    existsAlias: (params: ES6RequestParams.IndicesExistsAlias & ES7RequestParams.IndicesExistsAlias): Promise<ApiResponse<IndicesExistsAliasResponse>> =>
-      this.client instanceof ES6Client ? this.client.indices.existsAlias(params) : this.client.indices.existsAlias(params),
-    putAlias: (params: ES6RequestParams.IndicesPutAlias & ES7RequestParams.IndicesPutAlias) =>
-      this.client instanceof ES6Client ? this.client.indices.putAlias(params) : this.client.indices.putAlias(params),
-    getAlias: (params: ES6RequestParams.IndicesGetAlias & ES7RequestParams.IndicesGetAlias) =>
-      this.client instanceof ES6Client ? this.client.indices.getAlias(params) : this.client.indices.getAlias(params),
-    updateAliases: (params: ES6RequestParams.IndicesUpdateAliases & ES7RequestParams.IndicesUpdateAliases) =>
-      this.client instanceof ES6Client ? this.client.indices.updateAliases(params) : this.client.indices.updateAliases(params),
-    validateQuery: (params: ES6RequestParams.IndicesValidateQuery & ES7RequestParams.IndicesValidateQuery): Promise<ApiResponse<ValidateQueryResponse>> =>
-      this.client instanceof ES6Client ? this.client.indices.validateQuery(params) : this.client.indices.validateQuery(params),
-    stats: (params: ES6RequestParams.IndicesStats & ES7RequestParams.IndicesStats): Promise<ApiResponse<IndicesStatsResponse>> =>
-      this.client instanceof ES6Client ? this.client.indices.stats(params) : this.client.indices.stats(params),
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    create: (params: ES7RequestParams.IndicesCreate & estypes.IndicesCreateRequest) =>
+      this.client instanceof ES7Client ? this.client.indices.create(params) : this.client.indices.create(params),
+
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    delete: (params: ES7RequestParams.IndicesDelete & estypes.IndicesDeleteRequest) =>
+      this.client instanceof ES7Client ? this.client.indices.delete(params) : this.client.indices.delete(params),
+
+    exists: async(params: ES7RequestParams.IndicesExists & estypes.IndicesExistsRequest)
+    : Promise<IndicesExistsResponse | estypes.IndicesExistsResponse> =>
+      this.client instanceof ES7Client ? (await this.client.indices.exists(params)).body as IndicesExistsResponse : this.client.indices.exists(params),
+
+    existsAlias: (params: ES7RequestParams.IndicesExistsAlias & estypes.IndicesExistsAliasRequest)
+    : Promise<ES7ApiResponse<IndicesExistsAliasResponse> | estypes.IndicesExistsAliasResponse> =>
+      this.client instanceof ES7Client ? this.client.indices.existsAlias(params) : this.client.indices.existsAlias(params),
+
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    putAlias: (params: ES7RequestParams.IndicesPutAlias & estypes.IndicesPutAliasRequest) =>
+      this.client instanceof ES7Client ? this.client.indices.putAlias(params) : this.client.indices.putAlias(params),
+
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    getAlias: async(params: ES7RequestParams.IndicesGetAlias & estypes.IndicesGetAliasRequest) =>
+      this.client instanceof ES7Client ? (await this.client.indices.getAlias(params)).body : this.client.indices.getAlias(params),
+
+    // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+    updateAliases: (params: ES7RequestParams.IndicesUpdateAliases & estypes.IndicesUpdateAliasesRequest) =>
+      this.client instanceof ES7Client ? this.client.indices.updateAliases(params) : this.client.indices.updateAliases(params),
+
+    validateQuery: async(params: ES7RequestParams.IndicesValidateQuery & estypes.IndicesValidateQueryRequest)
+    : Promise<ValidateQueryResponse | estypes.IndicesValidateQueryResponse> =>
+      // eslint-disable-next-line max-len
+      this.client instanceof ES7Client ? (await this.client.indices.validateQuery(params)).body as ValidateQueryResponse : this.client.indices.validateQuery(params),
+
+    stats: async(params: ES7RequestParams.IndicesStats & estypes.IndicesStatsRequest)
+    : Promise<IndicesStatsResponse | estypes.IndicesStatsResponse> =>
+      this.client instanceof ES7Client ? (await this.client.indices.stats(params)).body as IndicesStatsResponse : this.client.indices.stats(params),
   };
 
   nodes = {
-    info: (): Promise<ApiResponse<NodesInfoResponse>> => (this.client instanceof ES6Client ? this.client.nodes.info() : this.client.nodes.info()),
+    info: (): Promise<ES7ApiResponse<NodesInfoResponse> | estypes.NodesInfoResponse> =>
+      (this.client instanceof ES7Client ? this.client.nodes.info() : this.client.nodes.info()),
   };
 
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
   ping() {
-    return this.client instanceof ES6Client ? this.client.ping() : this.client.ping();
+    return this.client instanceof ES7Client ? this.client.ping() : this.client.ping();
   }
 
-  reindex(params: ES6RequestParams.Reindex & ES7RequestParams.Reindex): Promise<ApiResponse<ReindexResponse>> {
-    return this.client instanceof ES6Client ? this.client.reindex(params) : this.client.reindex(params);
+  reindex(params: ES7RequestParams.Reindex & estypes.ReindexRequest): Promise<ES7ApiResponse<ReindexResponse> | estypes.ReindexResponse> {
+    return this.client instanceof ES7Client ? this.client.reindex(params) : this.client.reindex(params);
   }
 
-  search(params: ES6RequestParams.Search & ES7RequestParams.Search): Promise<ApiResponse<SearchResponse>> {
-    return this.client instanceof ES6Client ? this.client.search(params) : this.client.search(params);
+  async search(params: ES7RequestParams.Search & estypes.SearchRequest): Promise<SearchResponse | estypes.SearchResponse> {
+    return this.client instanceof ES7Client ? (await this.client.search(params)).body as SearchResponse : this.client.search(params);
   }
 
 }

+ 46 - 65
apps/app/src/server/service/search-delegator/elasticsearch.ts

@@ -1,8 +1,8 @@
 import { Writable, Transform } from 'stream';
 import { URL } from 'url';
 
-import elasticsearch6 from '@elastic/elasticsearch6';
 import elasticsearch7 from '@elastic/elasticsearch7';
+import elasticsearch8 from '@elastic/elasticsearch8';
 import gc from 'expose-gc/function';
 import mongoose from 'mongoose';
 import streamToPromise from 'stream-to-promise';
@@ -54,7 +54,7 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
 
   socketIoService!: any;
 
-  isElasticsearchV6: boolean;
+  isElasticsearchV7: boolean;
 
   isElasticsearchReindexOnBoot: boolean;
 
@@ -75,13 +75,13 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
 
     const elasticsearchVersion: number = this.configManager.getConfig('crowi', 'app:elasticsearchVersion');
 
-    if (elasticsearchVersion !== 6 && elasticsearchVersion !== 7) {
+    if (elasticsearchVersion !== 7 && elasticsearchVersion !== 8) {
       throw new Error('Unsupported Elasticsearch version. Please specify a valid number to \'ELASTICSEARCH_VERSION\'');
     }
 
-    this.isElasticsearchV6 = elasticsearchVersion === 6;
+    this.isElasticsearchV7 = elasticsearchVersion === 7;
 
-    this.elasticsearch = this.isElasticsearchV6 ? elasticsearch6 : elasticsearch7;
+    this.elasticsearch = this.isElasticsearchV7 ? elasticsearch7 : elasticsearch8;
     this.isElasticsearchReindexOnBoot = this.configManager.getConfig('crowi', 'app:elasticsearchReindexOnBoot');
     this.client = null;
 
@@ -129,7 +129,7 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
   }
 
   getType() {
-    return this.isElasticsearchV6 ? 'pages' : '_doc';
+    return this.isElasticsearchV7 ? '_doc' : undefined;
   }
 
   /**
@@ -232,8 +232,8 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
     const tmpIndexName = `${indexName}-tmp`;
 
     // check existence
-    const { body: isExistsMainIndex } = await client.indices.exists({ index: indexName });
-    const { body: isExistsTmpIndex } = await client.indices.exists({ index: tmpIndexName });
+    const isExistsMainIndex = await client.indices.exists({ index: indexName });
+    const isExistsTmpIndex = await client.indices.exists({ index: tmpIndexName });
 
     // create indices name list
     const existingIndices: string[] = [];
@@ -249,9 +249,10 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
       };
     }
 
-    const { body: indicesBody } = await client.indices.stats({ index: existingIndices, metric: ['docs', 'store', 'indexing'] });
-    const { indices } = indicesBody;
-    const { body: aliases } = await client.indices.getAlias({ index: existingIndices });
+    const indicesStats = await client.indices.stats({ index: existingIndices, metric: ['docs', 'store', 'indexing'] });
+    const { indices } = indicesStats;
+
+    const aliases = await client.indices.getAlias({ index: existingIndices });
 
     const isMainIndexHasAlias = isExistsMainIndex && aliases[indexName].aliases != null && aliases[indexName].aliases[aliasName] != null;
     const isTmpIndexHasAlias = isExistsTmpIndex && aliases[tmpIndexName].aliases != null && aliases[tmpIndexName].aliases[aliasName] != null;
@@ -263,6 +264,7 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
       aliases,
       isNormalized,
     };
+
   }
 
   /**
@@ -273,16 +275,24 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
 
     const tmpIndexName = `${indexName}-tmp`;
 
-    try {
-      // reindex to tmp index
-      await this.createIndex(tmpIndexName);
-      await client.reindex({
+    const reindexRequest = this.isElasticsearchV7
+      ? {
         waitForCompletion: false,
         body: {
           source: { index: indexName },
           dest: { index: tmpIndexName },
         },
-      });
+      }
+      : {
+        wait_for_completion: false,
+        source: { index: indexName },
+        dest: { index: tmpIndexName },
+      };
+
+    try {
+      // reindex to tmp index
+      await this.createIndex(tmpIndexName);
+      await client.reindex(reindexRequest);
 
       // update alias
       await client.indices.updateAliases({
@@ -323,13 +333,13 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
     const tmpIndexName = `${indexName}-tmp`;
 
     // remove tmp index
-    const { body: isExistsTmpIndex } = await client.indices.exists({ index: tmpIndexName });
+    const isExistsTmpIndex = await client.indices.exists({ index: tmpIndexName });
     if (isExistsTmpIndex) {
       await client.indices.delete({ index: tmpIndexName });
     }
 
     // create index
-    const { body: isExistsIndex } = await client.indices.exists({ index: indexName });
+    const isExistsIndex = await client.indices.exists({ index: indexName });
     if (!isExistsIndex) {
       await this.createIndex(indexName);
     }
@@ -345,12 +355,12 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
   }
 
   async createIndex(index) {
-    let mappings = this.isElasticsearchV6
-      ? require('^/resource/search/mappings-es6.json')
-      : require('^/resource/search/mappings-es7.json');
+    let mappings = this.isElasticsearchV7
+      ? require('^/resource/search/mappings-es7.json')
+      : require('^/resource/search/mappings-es8.json');
 
     if (process.env.CI) {
-      mappings = require('^/resource/search/mappings-es7-for-ci.json');
+      mappings = require('^/resource/search/mappings-es8-for-ci.json');
     }
 
     return this.client.indices.create({
@@ -575,14 +585,14 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
         batch.forEach(doc => prepareBodyForCreate(body, doc));
 
         try {
-          const { body: res } = await bulkWrite({
+          const bulkResponse = await bulkWrite({
             body,
             // requestTimeout: Infinity,
           });
 
-          count += (res.items || []).length;
+          count += (bulkResponse.items || []).length;
 
-          logger.info(`Adding pages progressing: (count=${count}, errors=${res.errors}, took=${res.took}ms)`);
+          logger.info(`Adding pages progressing: (count=${count}, errors=${bulkResponse.errors}, took=${bulkResponse.took}ms)`);
 
           if (shouldEmitProgress) {
             socket?.emit('addPageProgress', { totalCount, count, skipped });
@@ -649,7 +659,7 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
     if (process.env.NODE_ENV === 'development') {
       logger.debug('query: ', JSON.stringify(query, null, 2));
 
-      const { body: result } = await this.client.indices.validateQuery({
+      const validateQueryResponse = await this.client.indices.validateQuery({
         index: query.index,
         type: query.type,
         explain: true,
@@ -657,21 +667,20 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
           query: query.body.query,
         },
       });
+
       // for debug
-      logger.debug('ES result: ', result);
+      logger.debug('ES result: ', validateQueryResponse);
     }
 
-    const { body: result } = await this.client.search(query);
-
-    const totalValue = this.isElasticsearchV6 ? result.hits.total : result.hits.total.value;
+    const searchResponse = await this.client.search(query);
 
     return {
       meta: {
-        took: result.took,
-        total: totalValue,
-        hitsCount: result.hits.hits.length,
+        took: searchResponse.took,
+        total: searchResponse.hits.total.value,
+        hitsCount: searchResponse.hits.hits.length,
       },
-      data: result.hits.hits.map((elm) => {
+      data: searchResponse.hits.hits.map((elm) => {
         return {
           _id: elm._id,
           _score: elm._score,
@@ -704,10 +713,6 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
       },
     };
 
-    if (this.isElasticsearchV6) {
-      Object.assign(query, { type: 'pages' });
-    }
-
     return query;
   }
 
@@ -769,7 +774,6 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
     }
 
     if (parsedKeywords.phrase.length > 0) {
-      const phraseQueries: any[] = [];
       parsedKeywords.phrase.forEach((phrase) => {
         const phraseQuery = {
           multi_match: {
@@ -783,21 +787,11 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
             ],
           },
         };
-        if (this.isElasticsearchV6) {
-          phraseQueries.push(phraseQuery);
-        }
-        else {
-          query.body.query.bool.must.push(phraseQuery);
-        }
+        query.body.query.bool.must.push(phraseQuery);
       });
-
-      if (this.isElasticsearchV6) {
-        query.body.query.bool.must.push(phraseQueries);
-      }
     }
 
     if (parsedKeywords.not_phrase.length > 0) {
-      const notPhraseQueries: any[] = [];
       parsedKeywords.not_phrase.forEach((phrase) => {
         const notPhraseQuery = {
           multi_match: {
@@ -810,18 +804,8 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
             ],
           },
         };
-
-        if (this.isElasticsearchV6) {
-          notPhraseQueries.push(notPhraseQuery);
-        }
-        else {
-          query.body.query.bool.must_not.push(notPhraseQuery);
-        }
+        query.body.query.bool.must_not.push(notPhraseQuery);
       });
-
-      if (this.isElasticsearchV6) {
-        query.body.query.bool.must_not.push(notPhraseQueries);
-      }
     }
 
     if (parsedKeywords.prefix.length > 0) {
@@ -956,11 +940,8 @@ class ElasticsearchDelegator implements SearchDelegator<Data, ESTermsKey, ESQuer
           number_of_fragments: 0,
         },
       },
+      max_analyzed_offset: 1000000 - 1, // Set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them.
     };
-
-    if (!this.isElasticsearchV6) {
-      query.body.highlight.max_analyzed_offset = 1000000 - 1; // Set the query parameter [max_analyzed_offset] to a value less than index setting [1000000] and this will tolerate long field values by truncating them.
-    }
   }
 
   async search(data: SearchableData<ESQueryTerms>, user, userGroups, option?): Promise<ISearchResult<unknown>> {

+ 34 - 48
yarn.lock

@@ -1335,19 +1335,6 @@
     debug "^3.1.0"
     lodash.once "^4.1.1"
 
-"@elastic/elasticsearch6@npm:@elastic/elasticsearch@^6.8.8":
-  version "6.8.8"
-  resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-6.8.8.tgz#363d332d4de3a3ee5420ac0ced2eb4bfadf04548"
-  integrity sha512-51Jp3ZZ0oPqYPNlPG58XJ773MqJBx91rGNWCgVvy2UtxjxHsExAJooesOyLcoADnW0Dhyxu6yB8tziHnmyl8Vw==
-  dependencies:
-    debug "^4.1.1"
-    decompress-response "^4.2.0"
-    into-stream "^5.1.0"
-    ms "^2.1.1"
-    once "^1.4.0"
-    pump "^3.0.0"
-    secure-json-parse "^2.1.0"
-
 "@elastic/elasticsearch7@npm:@elastic/elasticsearch@^7.17.0":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.17.0.tgz#589fb219234cf1b0da23744e82b1d25e2fe9a797"
@@ -1358,6 +1345,26 @@
     ms "^2.1.3"
     secure-json-parse "^2.4.0"
 
+"@elastic/elasticsearch8@npm:@elastic/elasticsearch@^8.7.0":
+  version "8.7.0"
+  resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-8.7.0.tgz#eb0396f6899e0000109af55cffee514811e571ee"
+  integrity sha512-0u12N7gvSpv99XiiYE7OlOtVy4oFx7KNYd+/SSt0GqIcpj4X8bcILjpkQqK3HZcvkvwc8bFL9J11EMmUlg6FYw==
+  dependencies:
+    "@elastic/transport" "^8.3.1"
+    tslib "^2.4.0"
+
+"@elastic/transport@^8.3.1":
+  version "8.3.1"
+  resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.3.1.tgz#e7569d7df35b03108ea7aa886113800245faa17f"
+  integrity sha512-jv/Yp2VLvv5tSMEOF8iGrtL2YsYHbpf4s+nDsItxUTLFTzuJGpnsB/xBlfsoT2kAYEnWHiSJuqrbRcpXEI/SEQ==
+  dependencies:
+    debug "^4.3.4"
+    hpagent "^1.0.0"
+    ms "^2.1.3"
+    secure-json-parse "^2.4.0"
+    tslib "^2.4.0"
+    undici "^5.5.1"
+
 "@esbuild/android-arm64@0.17.17":
   version "0.17.17"
   resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.17.tgz#164b054d58551f8856285f386e1a8f45d9ba3a31"
@@ -4473,7 +4480,7 @@ bunyan@^1.8.12, bunyan@^1.8.15:
     mv "~2"
     safe-json-stringify "~1"
 
-busboy@1.6.0:
+busboy@1.6.0, busboy@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
   integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
@@ -5734,13 +5741,6 @@ decode-uri-component@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
 
-decompress-response@^4.2.0:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
-  integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
-  dependencies:
-    mimic-response "^2.0.0"
-
 dedent@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
@@ -7304,14 +7304,6 @@ fresh@0.5.2:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
 
-from2@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
-  integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
-  dependencies:
-    inherits "^2.0.1"
-    readable-stream "^2.0.0"
-
 fs-constants@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -8242,6 +8234,11 @@ hpagent@^0.1.1:
   resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.2.tgz#cab39c66d4df2d4377dbd212295d878deb9bdaa9"
   integrity sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==
 
+hpagent@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903"
+  integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==
+
 html-escaper@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491"
@@ -8570,14 +8567,6 @@ interpret@^1.0.0:
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
   integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
 
-into-stream@^5.1.0:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-5.1.1.tgz#f9a20a348a11f3c13face22763f2d02e127f4db8"
-  integrity sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==
-  dependencies:
-    from2 "^2.3.0"
-    p-is-promise "^3.0.0"
-
 invariant@^2.2.1, invariant@^2.2.4:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@@ -11128,11 +11117,6 @@ mimic-fn@^2.1.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
-mimic-response@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
-  integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
-
 min-indent@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256"
@@ -12069,11 +12053,6 @@ p-is-promise@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5"
 
-p-is-promise@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971"
-  integrity sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==
-
 p-limit@^1.1.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
@@ -14010,7 +13989,7 @@ scroll-into-view-if-needed@^2.2.20:
   dependencies:
     compute-scroll-into-view "^1.0.17"
 
-secure-json-parse@^2.1.0, secure-json-parse@^2.4.0:
+secure-json-parse@^2.4.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.4.0.tgz#5aaeaaef85c7a417f76271a4f5b0cc3315ddca85"
   integrity sha512-Q5Z/97nbON5t/L/sH6mY2EacfjVGwrCcSi5D3btRO2GZ8pf1K1UN7Z9H5J57hjVU2Qzxr1xO+FmBhOvEkzCMmg==
@@ -15730,6 +15709,13 @@ uncontrollable@^7.2.1:
     invariant "^2.2.4"
     react-lifecycles-compat "^3.0.4"
 
+undici@^5.5.1:
+  version "5.21.2"
+  resolved "https://registry.yarnpkg.com/undici/-/undici-5.21.2.tgz#329f628aaea3f1539a28b9325dccc72097d29acd"
+  integrity sha512-f6pTQ9RF4DQtwoWSaC42P/NKlUjvezVvd9r155ohqkwFNRyBKM3f3pcty3ouusefNRyM25XhIQEbeQ46sZDJfQ==
+  dependencies:
+    busboy "^1.6.0"
+
 unified@^10.0.0, unified@~10.1.1:
   version "10.1.2"
   resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df"