Explorar o código

Modified for search

Taichi Masuyama %!s(int64=4) %!d(string=hai) anos
pai
achega
7bfce125d3

+ 2 - 2
packages/app/src/components/SearchPage.jsx

@@ -173,9 +173,9 @@ class SearchPage extends React.Component {
     }
   }
 
-  selectPage= (pageId) => {
+  selectPage = (pageId) => {
     const index = this.state.searchedPages.findIndex((page) => {
-      return page._id === pageId;
+      return page.pageData._id === pageId;
     });
     this.setState({
       focusedPage: this.state.searchedPages[index],

+ 17 - 10
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -3,37 +3,44 @@ import React, { FC } from 'react';
 import RevisionLoader from '../Page/RevisionLoader';
 import AppContainer from '../../client/services/AppContainer';
 
+import { IPageSearchResultData } from '../../interfaces/search';
+
 
 type Props ={
   appContainer: AppContainer,
   searchingKeyword:string,
-  focusedPage : any,
+  focusedPage: IPageSearchResultData,
 }
 const SearchResultContent: FC<Props> = (props: Props) => {
   // Temporaly workaround for lint error
   // later needs to be fixed: RevisoinRender to typescriptcomponet
   const RevisionRenderTypeAny: any = RevisionLoader;
-  const renderPage = (page) => {
+  const renderPage = (page: IPageSearchResultData) => {
+    const { pageData } = page;
+    if (pageData == null) {
+      return null;
+    }
+
     const growiRenderer = props.appContainer.getRenderer('searchresult');
     let showTags = false;
-    if (page.tags != null && page.tags.length > 0) { showTags = true }
+    if (pageData.tags != null && pageData.tags.length > 0) { showTags = true }
     return (
-      <div key={page._id} className="search-result-page mb-5">
+      <div key={pageData._id} className="search-result-page mb-5">
         <h2>
-          <a href={page.path} className="text-break">
-            {page.path}
+          <a href={pageData.path} className="text-break">
+            {pageData.path}
           </a>
           {showTags && (
             <div className="mt-1 small">
-              <i className="tag-icon icon-tag"></i> {page.tags.join(', ')}
+              <i className="tag-icon icon-tag"></i> {pageData.tags.join(', ')}
             </div>
           )}
         </h2>
         <RevisionRenderTypeAny
           growiRenderer={growiRenderer}
-          pageId={page._id}
-          pagePath={page.path}
-          revisionId={page.revision}
+          pageId={pageData._id}
+          pagePath={pageData.path}
+          revisionId={pageData.revision}
           highlightKeywords={props.searchingKeyword}
         />
       </div>

+ 2 - 1
packages/app/src/components/SearchPage/SearchResultListItem.tsx

@@ -126,7 +126,8 @@ const SearchResultListItem: FC<Props> = (props:Props) => {
               <Clamp
                 lines={2}
               >
-                {pageMeta.elasticSearchResult && <div className="mt-1" dangerouslySetInnerHTML={{ __html: pageMeta.elasticSearchResult.snippet }}></div>}
+                {pageMeta.elasticSearchResult != null
+                && <div className="mt-1" dangerouslySetInnerHTML={{ __html: pageMeta.elasticSearchResult.snippet }}></div>}
               </Clamp>
             </div>
           </div>

+ 1 - 1
packages/app/src/server/interfaces/search.ts

@@ -29,7 +29,7 @@ export interface SearchDelegator<T = unknown> {
 }
 
 export type Result<T> = {
-  data: T
+  data: T[]
 }
 
 export type MetaData = {

+ 2 - 2
packages/app/src/server/service/search-delegator/private-legacy-pages.ts

@@ -8,7 +8,7 @@ import {
 } from '../../interfaces/search';
 
 
-class PrivateLegacyPagesDelegator implements SearchDelegator<IPage[]> {
+class PrivateLegacyPagesDelegator implements SearchDelegator<IPage> {
 
   name!: SearchDelegatorName.PRIVATE_LEGACY_PAGES
 
@@ -16,7 +16,7 @@ class PrivateLegacyPagesDelegator implements SearchDelegator<IPage[]> {
     this.name = SearchDelegatorName.PRIVATE_LEGACY_PAGES;
   }
 
-  async search(data: SearchableData | null, user, userGroups, option): Promise<Result<IPage[]> & MetaData> {
+  async search(_data: SearchableData | null, user, userGroups, option): Promise<Result<IPage> & MetaData> {
     const { offset, limit } = option;
 
     if (offset == null || limit == null) {

+ 51 - 22
packages/app/src/server/service/search.ts

@@ -13,6 +13,7 @@ import PrivateLegacyPagesDelegator from './search-delegator/private-legacy-pages
 import loggerFactory from '~/utils/logger';
 import { PageModel } from '../models/page';
 import { serializeUserSecurely } from '../models/serializers/user-serializer';
+import { IPage } from '~/interfaces/page';
 
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:service:search');
@@ -34,9 +35,25 @@ const normalizeQueryString = (_queryString: string): string => {
 };
 
 export type FormattedSearchResult = {
-  data: any
-  meta?: any
-  totalCount: any
+  data: {
+    pageData: IPage
+    pageMeta: {
+      bookmarkCount?: number
+      elasticsearchResult?: {
+        snippet?: string
+        matchedPath?: string
+        highlightedPath?: string
+      }
+    }
+  }[]
+
+  totalCount: number
+
+  meta?: {
+    total: number
+    took?: number
+    count?: number
+  }
 }
 
 class SearchService implements SearchQueryParser, SearchResolver {
@@ -339,18 +356,28 @@ class SearchService implements SearchQueryParser, SearchResolver {
     return terms;
   }
 
-  // TODO: optimize the way to check isReshapable e.g. check data schema of searchResult
+  // TODO: optimize the way to check isFormattable e.g. check data schema of searchResult
   // So far, it determines by delegatorName passed by searchService.searchKeyword
-  checkIsReshapable(searchResult, delegatorName): boolean {
+  checkIsFormattable(searchResult, delegatorName): boolean {
     return delegatorName === SearchDelegatorName.DEFAULT;
   }
 
   /**
    * formatting result
    */
-  async formatSearchResult(searchResult, delegatorName): Promise<FormattedSearchResult> {
-    if (!this.checkIsReshapable(searchResult, delegatorName)) {
-      return searchResult;
+  async formatSearchResult(searchResult: Result<any> & MetaData, delegatorName): Promise<FormattedSearchResult> {
+    if (!this.checkIsFormattable(searchResult, delegatorName)) {
+      const data = searchResult.data.map((page) => {
+        return {
+          pageData: page,
+          pageMeta: {},
+        };
+      });
+      return {
+        data,
+        totalCount: data.length,
+        meta: searchResult.meta,
+      };
     }
 
     const Page = this.crowi.model('Page') as PageModel;
@@ -388,30 +415,32 @@ class SearchService implements SearchQueryParser, SearchResolver {
           return pageData.id === data._id;
         });
 
+        // increment elasticSearchResult
+        let elasticSearchResult;
+        const highlightData = data._highlight;
+        if (highlightData != null) {
+          const snippet = highlightData['body.en'] || highlightData['body.ja'] || '';
+          const pathMatch = highlightData['path.en'] || highlightData['path.ja'] || '';
+
+          elasticSearchResult = {
+            snippet: filterXss.process(snippet),
+            // todo: use filter xss.process() for matchedPath;
+            matchedPath: pathMatch,
+          };
+        }
+
         const pageMeta = {
           bookmarkCount: data._source.bookmark_count || 0,
-          elasticSearchResult: data.elasticSearchResult,
+          elasticSearchResult,
         };
 
         return { pageData, pageMeta };
       })
       .sort((page1, page2) => {
         // note: this do not consider NaN
-        return scoreMap[page2._id] - scoreMap[page1._id];
+        return scoreMap[page2.pageData._id] - scoreMap[page1.pageData._id];
       });
 
-    result.data.forEach((data) => {
-      const highlightData = data._highlight;
-      const snippet = highlightData['body.en'] || highlightData['body.ja'] || '';
-      const pathMatch = highlightData['path.en'] || highlightData['path.ja'] || '';
-
-      data.elasticSearchResult = {
-        snippet: filterXss.process(snippet),
-        // todo: use filter xss.process() for matchedPath;
-        matchedPath: pathMatch,
-      };
-    });
-
     return result;
   }