-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Hironori Yamamoto
authored
Mar 9, 2023
1 parent
aea8aeb
commit e74a6c3
Showing
16 changed files
with
879 additions
and
1,277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
LOG_LEVEL=INFO | ||
REDASH__URL= | ||
REDASH__API_KEY= | ||
NEXT_PUBLIC_APP__URL=http://localhost:3000 | ||
NEXT_PUBLIC_REDASH__URL=${REDASH__URL} | ||
OPEN_SEARCH__URL=http://opensearch-node-main:9200 | ||
OPEN_SEARCH__URL=http://localhost:9200 | ||
OPEN_SEARCH__USERNAME=admin | ||
OPEN_SEARCH__PASSWORD=admin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { EuiPageTemplate, EuiTitle } from "@elastic/eui"; | ||
import React from "react"; | ||
import { | ||
RefinementList, | ||
SearchBox, | ||
InstantSearch, | ||
CurrentRefinements, | ||
Stats, | ||
InfiniteHits, | ||
InstantSearchProps, | ||
Hits, | ||
} from "react-instantsearch-dom"; | ||
import Hit from "../components/Hit"; | ||
|
||
interface CurrentRefinementItem { | ||
id: string; | ||
index: string; | ||
attribute: string; | ||
label: string; | ||
currentRefinement: string[]; | ||
} | ||
|
||
const reduceDuplicateRefinement = (items: CurrentRefinementItem[]) => { | ||
const seen = new Set(); | ||
return items.filter((item: any) => { | ||
if (seen.has(item.label)) { | ||
return false; | ||
} else { | ||
seen.add(item.label); | ||
return true; | ||
} | ||
}); | ||
}; | ||
|
||
export default function App(props: InstantSearchProps) { | ||
return ( | ||
<div className="ais-InstantSearch"> | ||
<InstantSearch {...props}> | ||
<EuiPageTemplate panelled={true}> | ||
<EuiPageTemplate.Sidebar> | ||
<EuiTitle size="l"> | ||
<h1>Redash Searcher</h1> | ||
</EuiTitle> | ||
<EuiTitle size="xs"> | ||
<h4>Data Source</h4> | ||
</EuiTitle> | ||
<RefinementList | ||
attribute="data_source_type" | ||
searchable={true} | ||
limit={10} | ||
/> | ||
<EuiTitle size="xs"> | ||
<h4>User Name</h4> | ||
</EuiTitle> | ||
<RefinementList | ||
attribute="user_name" | ||
searchable={true} | ||
limit={10} | ||
/> | ||
<EuiTitle size="xs"> | ||
<h4>User Email</h4> | ||
</EuiTitle> | ||
<RefinementList | ||
attribute="user_email" | ||
searchable={true} | ||
limit={10} | ||
/> | ||
<EuiTitle size="xs"> | ||
<h4>Tags</h4> | ||
</EuiTitle> | ||
<RefinementList attribute="tags" searchable={true} limit={10} /> | ||
</EuiPageTemplate.Sidebar> | ||
<EuiPageTemplate.Section> | ||
<SearchBox /> | ||
<Stats /> | ||
<div style={{ margin: "1rem 0" }}> | ||
<CurrentRefinements transformItems={reduceDuplicateRefinement} /> | ||
</div> | ||
</EuiPageTemplate.Section> | ||
<EuiPageTemplate.Section> | ||
<InfiniteHits hitComponent={Hit} /> | ||
</EuiPageTemplate.Section> | ||
</EuiPageTemplate> | ||
</InstantSearch> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,36 @@ | ||
import { EuiCallOut, EuiText } from "@elastic/eui"; | ||
import dompurify from "dompurify"; | ||
import { EuiCallOut } from "@elastic/eui"; | ||
import { connectHighlight } from "react-instantsearch-dom"; | ||
|
||
export interface HighlightedQueryProps { | ||
query: string; | ||
} | ||
|
||
export const HighlightedQuery: React.FC<HighlightedQueryProps> = ({ | ||
query, | ||
}: HighlightedQueryProps) => { | ||
const sanitizer = dompurify.sanitize; | ||
const HighlightQueryInner = ({ highlight, attribute, hit }: any) => { | ||
const parsedHit = highlight({ | ||
highlightProperty: "_highlightResult", | ||
attribute, | ||
hit, | ||
}); | ||
return ( | ||
<EuiCallOut> | ||
<p | ||
style={{ whiteSpace: "pre-wrap" }} | ||
dangerouslySetInnerHTML={{ | ||
__html: sanitizer(query), | ||
}} | ||
/> | ||
<EuiCallOut | ||
style={{ whiteSpace: "pre-wrap", maxWidth: "1000px" }} | ||
color={"success"} | ||
> | ||
<span className="ais-Highlight"> | ||
<span> | ||
{parsedHit.map((part: any, index: number) => | ||
part.isHighlighted ? ( | ||
<mark className="ais-Highlight-highlighted" key={index}> | ||
{part.value} | ||
</mark> | ||
) : ( | ||
<span className="ais-Highlight-nonHighlighted" key={index}> | ||
{part.value} | ||
</span> | ||
) | ||
)} | ||
</span> | ||
</span> | ||
</EuiCallOut> | ||
); | ||
}; | ||
|
||
const HighlightedQuery = connectHighlight(HighlightQueryInner); | ||
|
||
export default HighlightedQuery; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { Hit as HitCore } from "react-instantsearch-core"; | ||
import { IResultHitField } from "../pages/api/models"; | ||
import { | ||
EuiFlexGrid, | ||
EuiFlexItem, | ||
EuiCard, | ||
EuiSpacer, | ||
EuiDescriptionList, | ||
EuiFlexGroup, | ||
EuiAvatar, | ||
} from "@elastic/eui"; | ||
import HighlightedQuery from "./HighlightedQuery"; | ||
|
||
export interface HitProps { | ||
hit: HitCore<IResultHitField>; | ||
} | ||
|
||
const REDASH_URL = (process.env.NEXT_PUBLIC_REDASH__URL || "").replace( | ||
/\/$/, | ||
"" | ||
); | ||
|
||
const Hit: React.FC<HitProps> = ({ hit }: HitProps) => { | ||
return ( | ||
<EuiCard | ||
layout="horizontal" | ||
icon={ | ||
<EuiAvatar | ||
name={`logo-${hit.data_source_type}`} | ||
size="l" | ||
imageUrl={`${REDASH_URL}/static/images/db-logos/${hit.data_source_type}.png`} | ||
color="#e0e5ee" | ||
/> | ||
} | ||
title={hit.name} | ||
href={`${REDASH_URL}/queries/${hit.id}/source`} | ||
description={hit.description} | ||
> | ||
<EuiFlexGrid gutterSize="xl"> | ||
<EuiFlexGroup> | ||
<EuiFlexItem grow={5}> | ||
<EuiDescriptionList | ||
textStyle="reverse" | ||
listItems={[ | ||
{ | ||
title: "Data Source Type", | ||
description: hit.data_source_type, | ||
}, | ||
{ | ||
title: "Author", | ||
description: hit.user_name, | ||
}, | ||
{ | ||
title: "Tags", | ||
description: hit.tags.join(", ") || "No Tags", | ||
}, | ||
]} | ||
/> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={5}> | ||
<EuiDescriptionList | ||
textStyle="reverse" | ||
listItems={[ | ||
{ | ||
title: "Data Source Name", | ||
description: hit.data_source_name, | ||
}, | ||
{ | ||
title: "Created At", | ||
description: hit.created_at, | ||
}, | ||
{ | ||
title: "Updated At", | ||
description: hit.updated_at, | ||
}, | ||
]} | ||
/> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
{hit._highlightResult.query?.matchedWords.length !== 0 && ( | ||
<EuiFlexGroup> | ||
<EuiFlexItem grow={5}> | ||
<HighlightedQuery hit={hit} attribute="query"></HighlightedQuery> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
)} | ||
</EuiFlexGrid> | ||
</EuiCard> | ||
); | ||
}; | ||
|
||
export default Hit; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.