使用React和Elasticsearch构建GitHub Repo Explorer

柳珂
2023-12-01

Elasticsearch is one of the most popular full-text search engines which allows you to search huge volumes of data quickly, while React is arguably the best library for building user interfaces. During the past few months I’ve been co-authoring an open-source library, ReactiveSearch, which provides React components for Elasticsearch and simplifies the process of building a search User Interface (UI).

Elasticsearch是最受欢迎的全文搜索引擎之一,它使您可以快速搜索大量数据,而React无疑构建用户界面的最佳库 。 在过去的几个月中,我一直与他人合着一个开源库ReactiveSearch ,该库为Elasticsearch提供React组件,并简化了构建搜索用户界面(UI)的过程。

This is the app which I’ll be building in this story:

这是我将在此故事中构建的应用程序:

Elasticsearch的简要说明 (A brief idea of Elasticsearch)

Elasticsearch is a NoSQL database which can search through large amounts of data in a short time. It performs a full-text search on the data which is stored in the form of documents (like objects) by examining all the words in every document.

Elasticsearch是一个NoSQL数据库,可以在短时间内搜索大量数据。 它通过检查每个文档中的所有单词,对以文档(如对象)形式存储的数据执行全文搜索

Here’s what the Elasticsearch docs say:

这是Elasticsearch文档所说的:

Elasticsearch is a highly scalable open-source full-text search and analytics engine. It allows you to store, search, and analyze big volumes of data quickly and in near real time.
Elasticsearch是一个高度可扩展的开源全文本搜索和分析引擎。 它使您可以快速,近乎实时地存储,搜索和分析大量数据。

Even if you’ve never used Elasticsearch before you should be able to follow along with this story and build your very own Elasticsearch powered search using React and ReactiveSearch. ?

即使您从未使用过Elasticsearch,也应该能够跟随这个故事并使用React和ReactiveSearch构建自己的以Elasticsearch为动力的搜索。 ?

什么是ReactiveSearch? (What is ReactiveSearch?)

ReactiveSearch is a React UI components library for Elasticsearch. In order to search data in Elasticsearch, you need to write queries. Then you will need to format and render the JSON data in your UI. ReactiveSearch simplifies the entire process since you don’t need to worry about writing these queries. This makes it easier to focus on creating the UI.

ReactiveSearch是用于Elasticsearch的React UI组件库。 为了在Elasticsearch中搜索数据,您需要编写query 。 然后,您将需要在UI中格式化和呈现JSON数据。 由于您无需担心编写这些查询,因此ReactiveSearch简化了整个过程。 这样可以更轻松地专注于创建UI。

Here is an example that generates a search-box UI with category specific suggestions:

这是一个生成带有特定类别建议的搜索框用户界面的示例:

<CategorySearch
  componentId="repo"
  dataField={["name", "name.raw"]}
  categoryField="language.raw"
/>

This would likely have taken us 100+ lines without the library, and knowledge of Elasticsearch Query DSL to construct the query.

这可能需要我们100多行,而没有库,并且没有Elasticsearch Query DSL的知识来构建查询。

In this post, I’ll use different components from the library to build the final UI.

在本文中,我将使用库中的不同组件来构建最终的UI。

You should try out the final app before we deep-dive. Here’s the CodeSandbox link for the same.

在深入研究之前,您应该尝试最终的应用程序 。 这是相同的CodeSandbox链接

设置东西 (Setting things up)

Before we start building the UI, we’ll need the dataset containing GitHub repositories in Elasticsearch. ReactiveSearch works with any Elasticsearch index and you can easily use it with your own dataset.

在开始构建UI之前,我们需要Elasticsearch中包含GitHub存储库的数据集。 ReactiveSearch可与任何Elasticsearch索引一起使用,您可以轻松地将其与自己的数据集结合使用

For brevity, you can use my dataset or clone it for yourself by following this link and clicking on Clone this App button. This will let you make a copy of the dataset as your own app.

为简便起见,您可以使用我的数据集或通过单击此链接并单击“ 克隆此应用程序”按钮自行克隆它 。 这将使您可以将数据集的副本作为自己的应用程序进行复制。

After you enter an app name, the cloning process should start importing the 26K+ repos to your account.

输入应用程序名称后,克隆过程应开始将26K +存储库导入到您的帐户。

All the repos are structured in the following format:

所有存储库均采用以下格式构成:

{
  "name": "freeCodeCamp",
  "owner": "freeCodeCamp",
  "fullname": "freeCodeCamp~freeCodeCamp",
  "description": "The https://freeCodeCamp.org open source codebase and curriculum. Learn to code and help nonprofits.",
  "avatar": "https://avatars0.githubusercontent.com/u/9892522?v=4",
  "url": "https://github.com/freeCodeCamp/freeCodeCamp",
  "pushed": "2017-12-24T05:44:03Z",
  "created": "2014-12-24T17:49:19Z",
  "size": 31474,
  "stars": 291526,
  "forks": 13211,
  "topics": [
    "careers",
    "certification",
    "community",
    "curriculum",
    "d3",
    "education",
    "javascript",
    "learn-to-code",
    "math",
    "nodejs",
    "nonprofits",
    "programming",
    "react",
    "teachers"
  ],
  "language": "JavaScript",
  "watchers": 8462
}
  • We will use create-react-app to set up the project. You can install create-react-app by running the following command in your terminal:

    我们将使用create-react-app设置项目。 您可以通过在终端中运行以下命令来安装create-react-app:

npm install -g create-react-app
  • After it’s installed, you can create a new project by running:

    安装完成后,您可以运行以下命令创建一个新项目:
create-react-app gitxplore
  • After the project is set up you can change into the project directory and install ReactiveSearch dependency:

    设置项目后,您可以进入项目目录并安装ReactiveSearch依赖项:
cd gitxplore
npm install @appbaseio/reactivesearch
  • You may also add fontawesome CDN, which we’ll be using for some icons, by inserting the following lines in /public/index.html before the </body> tag ends:

    您还可以添加fontawesome CDN(我们将在其中使用某些图标),方法是在</body>标记结束之前在/public/index.html插入以下行:

<script defer         src="https://use.fontawesome.com/releases/v5.0.2/js/all.js"></script>

深入研究代码 (Diving into the code)

I’ll follow a simple directory structure for the app. Here are the important files:

我将为应用程序遵循一个简单的目录结构。 以下是重要文件:

src
├── App.css               // App styles
├── App.js                // App container
├── components
│   ├── Header.js         // Header component
│   ├── Results.js        // Results component
│   ├── SearchFilters.js  // Filters component
│   └── Topic.js          // rendered by Results
├── index.css             // styles
├── index.js              // ReactDOM render
└── theme.js              // colors and fonts
public
└── index.html

Here’s the link to final repo if you wish to reference anything at any point.

如果您想随时参考任何内容,请访问最终仓库

1.添加样式 (1. Adding styles)

I’ve written responsive styles for the app which you can copy into your app. Just fire up your favorite text editor and copy the styles for /src/index.css from here and /src/App.css from here respectively.

我已经为应用程序编写了响应样式,您可以将其复制到应用程序中。 刚刚火起来的文本编辑器和复制的样式/src/index.css这里/src/App.css这里分别。

Now, create a file /src/theme.js where we’ll add the colors and fonts for our app:

现在,创建一个文件/src/theme.js ,在其中添加应用程序的颜色和字体:

2.添加第一个ReactiveSearch组件 (2. Adding the first ReactiveSearch component)

All the ReactiveSearch components are wrapped around a container component ReactiveBase which provides data from Elasticsearch to the children ReactiveSearch components.

所有ReactiveSearch组件都包装在一个容器组件ReactiveBase周围,​​该组件将Elasticsearch中的数据提供给子级ReactiveSearch组件。

We’ll use this in /src/App.js:

我们将在/src/App.js使用它:

import React, { Component } from 'react';
import { ReactiveBase } from '@appbaseio/reactivesearch';
import theme from './theme';
import './App.css';
class App extends Component {
  render() {
    return (
      <section className="container">
        <ReactiveBase
          app="gitxplore-app"
          credentials="4oaS4Srzi:f6966181-1eb4-443c-8e0e-b7f38e7bc316"
          type="gitxplore-latest"
          theme={theme}
        >
          <nav className="navbar">
            <div className="title">GitXplore</div>
          </nav>
        </ReactiveBase>
      </section>
    );
  }
}
export default App;

For the app and credentials prop you may use the ones I’ve provided here as it is. If you cloned the dataset in your own app earlier you can get them from the app’s credentials page. If you’re already familiar with Elasticsearch you may instead pass a url prop referring to your own Elasticsearch cluster URL.

对于appcredentials道具,您可以按原样使用我在此处提供的内容。 如果您早先在自己的应用程序中克隆了数据集,则可以从应用程序的凭据页面获取它们。 如果您已经熟悉Elasticsearch,则可以传递一个引用您自己的Elasticsearch集群URLurl 属性

Alternatively, you can also copy your app’s credentials from the apps dashboard. Hover over your app’s card and click on Copy Read Credentials.

或者,您也可以从应用程序仪表板复制应用程序的credentials 。 将鼠标悬停在您的应用卡上,然后点击复制已读凭据

After adding this you would see a basic layout like this:

添加后,您将看到如下基本布局:

3.添加数据搜索 (3. Adding a DataSearch)

Next, I’ll be adding a DataSearch component to search through repositories. It creates a search UI component and lets us search across one or more fields easily. The updated render function in /src/App.js would look like this:

接下来,我将添加一个DataSearch组件以搜索存储库。 它创建了一个搜索UI组件,使我们可以轻松地跨一个或多个字段进行搜索。 /src/App.js更新的render函数如下所示:

// importing DataSearch here
import { ReactiveBase, DataSearch } from '@appbaseio/reactivesearch';
...
<ReactiveBase ... >
// Adding the DataSearch here
    <div className="flex row-reverse app-container">
        <div className="results-container">
            <DataSearch
                componentId="repo"
                filterLabel="Search"
                dataField={['name', 'description', 'name.raw', 'fullname', 'owner', 'topics']}
                placeholder="Search Repos"
                autosuggest={false}
                iconPosition="left"
                URLParams
                className="data-search-container results-container"
                innerClass={{
                    input: 'search-input',
                }}
            />
        </div>
    </div>
</ReactiveBase>
...

The DataSearch component goes inside the ReactiveBase component and receives all the necessary data from it so we don’t have to write Elasticsearch queries ourselves. The surrounding divs add some className properties for styling. These just add a layout to the app. You can go through all the styles at /src/App.css which we created earlier. You might have noticed that we have passed some props to the DataSearch component.

DataSearch组件位于ReactiveBase组件内部,并从中接收所有必要的数据,因此我们不必自己编写Elasticsearch查询。 周围的div添加了一些className属性以进行样式设置。 这些只是将布局添加到应用程序。 您可以在我们之前创建的/src/App.css浏览所有样式。 您可能已经注意到我们已经将一些道具传递给了DataSearch组件。

Here’s how they work:

它们的工作方式如下:

  • componentId: a unique string identifier which we’ll use later to connect two different ReactiveSearch components.

    componentId :一个唯一的字符串标识符,我们稍后将使用它来连接两个不同的ReactiveSearch组件。

  • filterLabel: a string value which will show up in the filters menu later.

    filterLabel :一个字符串值,稍后将在过滤器菜单中显示。

  • dataField: an array of strings containing Elasticsearch fields on which search has to performed on. You can check the dataset and see that these fields also matches the column name. All fields specified here matches the structure of data, for example name refers to the name of repo, description refers to its description, but there is a field with a .raw added here, name.raw which is a multi-field of the name field. Elasticsearch can index the same data in different ways for different purposes, which we can use to get better search results.

    dataField :一个字符串数组,其中包含必须对其执行搜索的Elasticsearch字段。 您可以检查数据集,并查看这些字段是否也与列名匹配。 此处指定的所有字段的数据的结构,例如火柴name是指回购的名称, description指的是其描述,但存在与场.raw此处添加, name.raw这是一个多字段的的name领域。 Elasticsearch可以出于不同目的以不同方式为相同数据建立索引,我们可以使用它来获得更好的搜索结果。

  • placeholder: sets the placeholder value in the input box.

    placeholder :在输入框中设置占位符值。

  • autosuggest: setting a false value for the prop causes the results to update immediately in the results.

    autosuggest :为prop设置一个false值会使结果立即在结果中更新。

  • iconPosition: sets the position of the ? icon.

    iconPosition :设置位置? 图标。

  • URLParams: is a boolean which tells the component to save the search term in the browser’s URL so we can share a URL to a specific search query. For example, check this link to see all results related to “react”.

    URLParams :是一个boolean ,它告诉组件将搜索词保存在浏览器的URL中,以便我们可以将URL共享给特定的搜索查询。 例如,检查此链接以查看与“React”相关的所有结果。

  • className: adds a class for styling using CSS.

    className :添加一个用于使用CSS进行样式设置的class

  • innerClass: adds a class to different sections of a component for styling using CSS. Here, I’ve added a class to the input box for styling. A detailed description can be found in the docs.

    innerClass :向组件的不同部分添加一个class以使用CSS进行样式设置。 在这里,我将一个class添加到input框以进行样式设置。 详细说明可以在docs中找到。

With this, our app should get a working search bar:

有了这个,我们的应用程序应该会得到一个正常的搜索栏:

4.添加结果视图 (4. Adding the Results view)

Next, we’ll be adding the Results component at /src/components/Results.js and importing it in /src/App.js.

接下来,我们将在/src/components/Results.js添加Results组件,并将其导入/src/App.js

Here’s how you can write the Results component:

这是编写“ Results组件的方法:

import React from 'react';
import { SelectedFilters, ReactiveList } from '@appbaseio/reactivesearch';
const onResultStats = (results, time) => (
  <div className="flex justify-end">
    {results} results found in {time}ms
  </div>
);
const onData = (data) => (
  <div className="result-item" key={data.fullname}>
    {data.owner}/{data.name}
  </div>
);
const Results = () => (
  <div className="result-list">
    <SelectedFilters className="m1" />
    <ReactiveList
      componentId="results"
      dataField="name"
      onData={onData}
      onResultStats={onResultStats}
      react={{
        and: ['repo'],
      }}
      pagination
      innerClass={{
        list: 'result-list-container',
        pagination: 'result-list-pagination',
        resultsInfo: 'result-list-info',
        poweredBy: 'powered-by',
      }}
      size={6}
    />
  </div>
);
export default Results;

I’ve imported two new components from ReactiveSearch, SelectedFilters and ReactiveList. SelectedFilters will render the filters for our ReactiveSearch components at one place:

我从ReactiveSearch导入了两个新组件, SelectedFiltersReactiveListSelectedFilters将为我们的ReactiveSearch组件提供一个过滤器:

ReactiveList renders the search results. Here’s how its props work:

ReactiveList呈现搜索结果。 其道具的工作原理如下:

  • dataField: orders the results using name field here.

    dataField :在此处使用name字段对结果进行dataField

  • onData: accepts a function which returns a JSX. The function is passed each result individually. Here we’re generating a basic UI which we’ll modify later.

    onData :接受一个返回JSX的函数。 该函数分别传递每个结果。 在这里,我们生成了一个基本的UI,稍后将对其进行修改。

  • onResultStats: similar to onData but for the result stats. The function is passed the number of results found and time taken.

    onResultStats :类似于onData但用于结果统计。 该函数将传递找到的results数和time

  • react: the react prop tells the ReactiveList to listen to changes made byCategorySearch component, we’ve provided the componentId of the CategorySearch component here called repo. Later we’ll add more components here.

    react :该react道具告诉ReactiveList听由所做的更改CategorySearch组件,我们提供的componentId中的CategorySearch这里称为组件repo 。 稍后,我们将在此处添加更多组件。

  • pagination: a boolean which tells the ReactiveList to split the results into pages, each page containing the number of results specified in the size prop.

    pagination :一个boolean ,它告诉ReactiveList将结果分成页面,每个页面包含size属性中指定的结果数。

Now we can import and use the Results component in /src/App.js. Just add it inside the div with results-container class.

现在,我们可以在/src/App.js import并使用Results组件。 只需将它添加到带有results-container类的div

...
import Results from './components/Results';
...
render() {
  return(
    ...
    <div className="results-container">
      <DataSearch ... />
      <Results />
    </div>
    ...
  )
}

With this component, a basic version of our search UI should start coming together:

有了这个组件,我们的搜索UI的基本版本应该开始组合在一起:

5.添加页眉组件 (5. Adding a Header component)

Lets create a Header component at /src/components/Header.js which we’ll use to render more search filters.

让我们在/src/components/Header.js创建一个Header组件,该组件将用于呈现更多搜索过滤器。

Here’s how to create a simple Header component:

以下是创建简单的Header组件的方法:

import React, { Component } from 'react';

import SearchFilters from './SearchFilters';

class Header extends Component {
	constructor(props) {
		super(props);
		this.state = {
			visible: false,
		};
	}

	toggleVisibility = () => {
		const visible = !this.state.visible;
		this.setState({
			visible,
		});
	}

	render() {
		return (
			<nav className={`navbar ${this.state.visible ? 'active' : ''}`}>
				<div className="title">GitXplore</div>
				<div className="btn toggle-btn" onClick={this.toggleVisibility}>Toggle Filters</div>
				<SearchFilters {...this.props} visible={this.state.visible} />
			</nav>
		);
	}
}

export default Header;

I’ve moved the navigation code in <nav>..</nav> from /src/App.js here. The Header component has a method which toggles visible in the state. We’re using this to add a class which would make it take up the entire screen size on mobile layout. I’ve also added a toggle button which calls the toggleVisibility method.

我已将导航代码从/src/App.js移至<nav>..</nav> 。 Header组件具有在状态中切换可见状态的方法。 我们正在使用它来添加一个类,该类将使其占据移动版式上的整个屏幕尺寸。 我还添加了一个切换按钮,该按钮调用toggleVisibility方法。

It also renders another component called SearchFilters and passes all the props from the parent App component. Let’s create this component to see things in action.

它还呈现另一个名为SearchFilters组件,并传递来自父App组件的所有道具。 让我们创建此组件以查看实际情况。

Create a new file /src/components/SearchFilters.js:

创建一个新文件/src/components/SearchFilters.js

import React from 'react';
const SearchFilters = () => (
    <div>
        Search filters go here!
    </div>
);
export default SearchFilters;

Next, I’ll update the App component to use the Header component that we created just now.

接下来,我将更新App组件以使用我们刚才创建的Header组件。

6.更新状态的App组件并处理主题 (6. Updating App component and handling topics in state)

We’ll add a state variable in App component called currentTopics which would be an array of currently selected topics in the app.

我们将在名为currentTopics App组件中添加一个state变量,该变量将是应用程序中当前选定主题的数组。

We’ll then use the currentTopics and pass them to the Header and Results components:

然后,我们将使用currentTopics并将它们传递给HeaderResults组件:

The setTopics method will set whichever topics are passed to it, which we’ll pass to the Header component. The toggleTopic method will remove a topic from the state in currentTopics if it’s already present and add the topic if it is not present.

setTopics方法将设置传递给它的主题,然后将其传递给Header组件。 toggleTopic方法将在currentTopics中的某个state中删除该主题(如果已经存在),并添加该主题(如果不存在)。

We’ll pass the toggleTopic method to the Results component:

我们将把toggleTopic方法传递给Results组件:

7.添加更多过滤器 (7. Adding more filters)

Lets add more filters to the UI in /src/components/SearchFilters.js. I’ll be using three new components from ReactiveSearch here, MultiDropdownList, SingleDropdownRange and RangeSlider. The components are used in a similar fashion as we used the DataSearch component earlier.

让我们在/src/components/SearchFilters.js向UI添加更多过滤器。 我将在这里使用来自ReactiveSearch的三个新组件: MultiDropdownListSingleDropdownRangeRangeSlider 。 这些组件的使用方式与我们之前使用DataSearch组件的方式类似。

Here’s the code:

这是代码:

import React from 'react';
import PropTypes from 'prop-types';
import {
	MultiDropdownList,
	SingleDropdownRange,
	RangeSlider,
} from '@appbaseio/reactivesearch';

const SearchFilters = ({ currentTopics, setTopics, visible }) => (
	<div className={`flex column filters-container ${!visible ? 'hidden' : ''}`}>
		<div className="child m10">
			<MultiDropdownList
				componentId="language"
				dataField="language.raw"
				placeholder="Select languages"
				title="Language"
				filterLabel="Language"
			/>
		</div>
		<div className="child m10">
			<MultiDropdownList
				componentId="topics"
				dataField="topics.raw"
				placeholder="Select topics"
				title="Repo Topics"
				filterLabel="Topics"
				size={1000}
				queryFormat="and"
				defaultSelected={currentTopics}
				onValueChange={setTopics}
			/>
		</div>
		<div className="child m10">
			<SingleDropdownRange
				componentId="pushed"
				dataField="pushed"
				placeholder="Repo last active"
				title="Last Active"
				filterLabel="Last Active"
				data={[
					{ start: 'now-1M', end: 'now', label: 'Last 30 days' },
					{ start: 'now-6M', end: 'now', label: 'Last 6 months' },
					{ start: 'now-1y', end: 'now', label: 'Last year' },
				]}
			/>
		</div>
		<div className="child m10">
			<SingleDropdownRange
				componentId="created"
				dataField="created"
				placeholder="Repo created"
				title="Created"
				filterLabel="Created"
				data={[
					{
						start: '2017-01-01T00:00:00Z',
						end: '2017-12-31T23:59:59Z',
						label: '2017',
					},
					{
						start: '2016-01-01T00:00:00Z',
						end: '2016-12-31T23:59:59Z',
						label: '2016',
					},
					{
						start: '2015-01-01T00:00:00Z',
						end: '2015-12-31T23:59:59Z',
						label: '2015',
					},
					{
						start: '2014-01-01T00:00:00Z',
						end: '2014-12-31T23:59:59Z',
						label: '2014',
					},
					{
						start: '2013-01-01T00:00:00Z',
						end: '2013-12-31T23:59:59Z',
						label: '2013',
					},
					{
						start: '2012-01-01T00:00:00Z',
						end: '2012-12-31T23:59:59Z',
						label: '2012',
					},
					{
						start: '2011-01-01T00:00:00Z',
						end: '2011-12-31T23:59:59Z',
						label: '2011',
					},
					{
						start: '2010-01-01T00:00:00Z',
						end: '2010-12-31T23:59:59Z',
						label: '2010',
					},
					{
						start: '2009-01-01T00:00:00Z',
						end: '2009-12-31T23:59:59Z',
						label: '2009',
					},
					{
						start: '2008-01-01T00:00:00Z',
						end: '2008-12-31T23:59:59Z',
						label: '2008',
					},
					{
						start: '2007-01-01T00:00:00Z',
						end: '2007-12-31T23:59:59Z',
						label: '2007',
					},
				]}
			/>
		</div>
		<div className="child m10">
			<RangeSlider
				componentId="stars"
				title="Repo Stars"
				dataField="stars"
				range={{ start: 0, end: 300000 }}
				showHistogram={false}
				rangeLabels={{
					start: '0 Stars',
					end: '300K Stars',
				}}
				innerClass={{
					label: 'range-label',
				}}
			/>
		</div>
		<div className="child m10">
			<RangeSlider
				componentId="forks"
				title="Repo Forks"
				dataField="forks"
				range={{ start: 0, end: 180500 }}
				showHistogram={false}
				rangeLabels={{
					start: '0 Forks',
					end: '180K Forks',
				}}
				innerClass={{
					label: 'range-label',
				}}
			/>
		</div>
	</div>
);

SearchFilters.propTypes = {
	currentTopics: PropTypes.arrayOf(PropTypes.string),
	setTopics: PropTypes.func,
	visible: PropTypes.bool,
};

export default SearchFilters;

The SearchFilters component we’ve created above takes in three props from the Header component, currentTopics, setTopics and visible. The visible prop is just used to add a className for styling.

我们上面创建的SearchFilters组件从Header组件中获取了三个道具, currentTopicssetTopicsvisiblevisible道具仅用于添加className以进行样式设置。

The first component we’ve used here is a MultiDropdownList which renders a dropdown component to select multiple options. The first MultiDropdownList has a dataField of language.raw. It’ll populate itself with all the languages available in the repositories dataset.

我们在这里使用的第一个组件是MultiDropdownList ,它呈现一个下拉组件以选择多个选项。 第一个MultiDropdownList具有language.rawdataField 。 它将使用存储库数据集中的所有可用语言填充自身。

We’ve used another MultiDropdownList to render a list of topics:

我们使用了另一个MultiDropdownList来呈现主题列表:

<MultiDropdownList
    componentId="topics"
    dataField="topics.raw"
    placeholder="Select languages"
    title="Repo Topics"
    filterLabel="Topics"
    size={1000}
    queryFormat="and"
    defaultSelected={currentTopics}
    onValueChange={setTopics}
/>

Here’s how the props work here:

道具在这里的工作方式如下:

  • componentId: similar to the previous ReactiveSearch components, this is a unique identifier which we’ll later associate in the Results component that we created to get search results.

    componentId :类似于先前的ReactiveSearch组件,这是一个唯一的标识符,我们稍后将其与我们创建的Results组件相关联以获取搜索结果。

  • dataField: maps the component to the topics.raw field in Elasticsearch.

    dataField :将组件映射到Elasticsearch中的topics.raw字段。

  • placeholder: sets the placeholder value when nothing is selected.

    placeholder :在未选择任何内容时设置占位符值。

  • title: adds a title for the component in the UI.

    title :在UI中为组件添加标题。

  • filterLabel: sets the label of the components in the removable filters (the SelectedFilters which we used in the Results component).

    filterLabel :设置可移动过滤器(在“ Results组件中使用的SelectedFilters组件的标签。

  • size: tells the component to render a maximum of 1000 items in the list.

    size :告诉组件在列表中最多渲染1000项目。

  • queryFormat: when set to 'and' as we’ve used here, it gives results which matches all the selected tags (exactly like intersection).

    queryFormat :如我们在此处使用'and'那样设置为'and' ,它给出的结果与所有选定标签匹配(完全像intersection )。

  • defaultSelected: sets the selected items in the component. Here we’re passing currentTopics which we’ve stored in the state at /src/App.js.

    defaultSelected :设置组件中的选定项目。 在这里,我们传递了currentTopics ,该state已存储在state/src/App.js

  • onValueChange: is a function that will be called by the component when we make a change in its value. Here we call the setTopics function which we received in the props. Therefore, whenever we select or deselect a value in the component it would update the currentTopics in the state of main App component.

    onValueChange :是一个函数,当我们更改其值时,它将由组件调用。 在这里,我们将调用道具中收到的setTopics函数。 因此,无论何时在组件中选择或取消选择一个值,都会在App主组件state下更新currentTopics

The next ReactiveSearch component we’ve used here is a SingleDropdownRange. It uses a new prop called data.

我们在这里使用的下一个ReactiveSearch组件是SingleDropdownRange 。 它使用了一个称为data的新道具。

Here’s how it works:

运作方式如下:

<SingleDropdownRange
    ...
    data={[
        { start: 'now-1M', end: 'now', label: 'Last 30 days' },
        { start: 'now-6M', end: 'now', label: 'Last 6 months' },
        { start: 'now-1y', end: 'now', label: 'Last year' },
    ]}
/>

The data prop accepts an array of objects with start and end values and shows the specified label in the dropdown. It’s mapped to the pushed field in the dataset which is a date type in Elasticsearch. One cool way to specify date range in Elasticsearch is using the now keyword. now refers to the current time, now-1M refers to one month before, now-6M to six month before and now-1y to a year before now.

data道具接受带有startend值的对象数组,并在下拉列表中显示指定的label 。 它被映射到数据集中的pushed字段,该字段是Elasticsearch中日期类型 。 在Elasticsearch中指定日期范围的一种很酷的方法是使用now关键字。 now指的是当前的时间, now-1M指的是前一个月, now-6M到六月份前和now-1y一年之前, now

I’ve used another SingleDropdownRange component for the created field in the dataset.

我为数据集中的created字段使用了另一个SingleDropdownRange组件。

Here I’ve specified year ranges in datetime for different years:

在这里,我指定了不同年份的日期时间范围:

<SingleDropdownRange
    ...
    data={[
        {
            start: '2017-01-01T00:00:00Z',
            end: '2017-12-31T23:59:59Z',
            label: '2017',
        },
        {
            start: '2016-01-01T00:00:00Z',
            end: '2016-12-31T23:59:59Z',
            label: '2016',
        },
       ...
    ]}
/>

The third component I’ve used is a RangeSlider which renders a slider UI. I’ve used to RangeSlider components, one for the stars field and the other for forks.

我使用的第三个组件是RangeSlider ,它呈现滑块UI。 我已经习惯了RangeSlider组件,其中一个用于stars字段,另一个用于forks

Two main props that this component introduces are range and rangeLabels:

此组件引入的两个主要rangeLabelsrangerangeLabels

<RangeSlider
    ...
    showHistogram={false}
    range={{ start: 0, end: 300000 }}
    rangeLabels={{
        start: '0 Stars',
        end: '300K Stars',
    }}
/>
  • range: prop specifies a range for the data with a start and end value.

    range :prop用startend值指定数据的范围。

  • rangeLabels: prop takes the labels to show below the slider.

    rangeLabels :prop将标签显示在滑块下方。

  • showHistogram: is a boolean prop which shows a histogram with the distribution of data. Here I’ve set it to false since it’s not needed.

    showHistogram :是一个boolean道具,它显示带有数据分布的直方图。 在这里,由于不需要它,所以将其设置为false

Now we just need to connect these filters to the Results component. We just have to update one line in the ReactiveList rendered by the Results component to include the componentIds of these components.

现在,我们只需要将这些过滤器连接到“ Results组件即可。 我们只需要更新的一条线ReactiveList通过渲染Results组件包括componentId这些组件秒。

Update the react prop in the ReactiveList that we rendered in the Results component:

更新我们在“ Results组件中呈现的ReactiveList中的react prop:

const Results = () => (
  <div className="result-list">
    <SelectedFilters className="m1" />
    <ReactiveList
      ... // updating the react prop here
      react={{
        and: ['language', 'topics', 'pushed', 'created', 'stars', 'forks', 'repo'],
      }}
    />
  </div>
);

That should make your results update for all the filters ?

那应该使所有过滤器的结果都更新吗?

8.更新结果视图 (8. Updating the results view)

Up until now, we’ve been seeing only a basic version of the results. As the final piece of this app, lets add some flair to the results ✌️

到目前为止,我们只看到了结果的基本版本。 作为此应用程序的最后一块,让我们为结果添加一些天赋✌️

We’ll be using another component inside our Results components to render different topics.

我们将在“ Results组件内使用另一个组件来呈现不同的主题。

Here’s how you can create your own at /src/components/Topic. Feel free to add your own taste ?

您可以在/src/components/Topic创建自己的方法。 随意添加自己的口味?

This component renders its children and adds a click handler to toggle the topics which updates the currentTopics inside the main App component’s state.

该组件呈现其children并添加单击处理程序以切换主题,从而更新主App组件状态内的currentTopics

Next, we just need to update our Results component at /src/components/Results.js:

接下来,我们只需要在/src/components/Results.js更新Results组件:

I’ve updated the onData function to render more detailed results. You’ll also notice a new sortOptions prop in the ReactiveList. This prop accepts an array of objects which renders a dropdown menu to select how you wish to sort the results. Each object contains a label to display as the list item, a dataField to sort the results on and a sortBy key which can either be asc (ascending) or desc (descending).

我已经更新了onData函数以呈现更详细的结果。 您还会在ReactiveList注意到一个新的sortOptions道具。 该道具接受一个对象数组,该对象呈现一个下拉菜单以选择您希望对结果进行排序的方式。 每个对象都包含一个要显示为列表项的label ,一个用于对结果进行排序的dataField以及一个sortBy键,该键可以是asc (升序)或desc (降序)。

That’s it, your very own GitHub repository explorer should be live!

就是这样,您自己的GitHub存储库浏览器应该已经上线了!

  1. GitXplore app demo, CodeSandbox and source code

    GitXplore应用程序演示CodeSandbox源代码

  2. ReactiveSearch GitHub repo

    ReactiveSearch GitHub存储库

  3. ReactiveSearch docs

    ReactiveSearch 文档

Hope you enjoyed this story. If you have any thoughts or suggestions, please let me know and do share your version of the app in comments!

希望您喜欢这个故事。 如果您有任何想法或建议,请告诉我,并在评论中分享您的应用版本!



You may follow me on twitter for latest updates. I've also started posting more recent posts on my personal blog.

您可以在Twitter上关注我以获取最新更新。 我还开始在我的个人博客上发布更多最新帖子。

翻译自: https://www.freecodecamp.org/news/building-a-github-repo-explorer-with-react-and-elasticsearch-8e1190e59c13/

 类似资料: