m's blog

備忘録とかメモとか

【Neo4j】Dockerで試すNeo4j【第4回/JavaScript編 その1】

【Neo4j】Dockerで試すNeo4j【第4回/JavaScript編 その1】

今回からは、JavaScript(TypeScript)を使って指定のPersonのリレーションを検索・表示する簡易アプリを作成していきたいと思います。

アプリの作成を通して、JavaScriptとNeo4jの連携について解説していきたいと思います。

今回の記事では、JavaScriptとNeo4jの連携環境のセットアップし、ターゲット選択用のセレクタを実装していきます。

ターゲット選択用のセレクタには、対象候補の自動補完機能も実装したいと思います。

↓ 「Dockerで試すNeo4j」シリーズ記事一覧はこちら

目次

はじめに

今回の記事では、以下の記事で追加したデータを使用します。

データを追加していない場合は、上記の記事を参考にデータを追加してください。

注意事項

トライアルという事で、クライアントからNeo4jにアクセスしていますが、アプリを公開する場合などはサーバ経由でアクセスするようにするなど、調整してください。

構成

セットアップを進める前に、最終的なファイル構成を紹介しておきたいと思います。

最終的には、以下のようなファイル構成となります。

【Neo4j】Dockerで試すNeo4j【第4回/JavaScript編 その1】

ここで、次回以降に追加するファイルも含んでいます。

また、特にファイルの追加や変更をしない、publicなどのディレクトリの中身は表示していません。

React+TypeScript環境をインストール

それでは、早速、JavaScriptとNeo4jの連携環境のセットアップしていきます。

JavaScriptの環境のベースとしてReact+TypeScript環境を使用したいと思います。

以下のコマンドを実行して、React+TypeScript環境をインストールしてください。

npx create-react-app . --template typescript

React+TypeScript環境の詳細については、以下のページを参照してください。

静的型チェック - React #TypeScript

必要なパッケージをインストール

次に、必要になるパッケージをインストールしたいと思います。

Neo4jにアクセスするためのパッケージとしてneo4j-driver、UI用にmaterial-uiをインストールします。

yarn add @material-ui/core @material-ui/lab neo4j-driver

環境変数を定義

ここからは、ターゲット選択用のセレクタの実装を進めて行きます。

まずは、アプリ中で使用する環境変数を定義します。

プロジェクトルートに.envファイルを追加して、以下のように記述してください。

REACT_APP_NEO4J_URL=bolt://localhost:57687
REACT_APP_NEO4J_USER=test
REACT_APP_NEO4J_PW=example

ここでは、Neo4jへのアクセス情報を設定しています。

以下の記事で追加したアプリ用ユーザをアクセスに使用します。

型定義

次に、コード中で使用する型をまとめて定義しておきます。

src/packages/@typesディレクトリを作成してApp.d.tsファイルを追加してください。

ファイルを追加したら、以下のような記述を追加してください。

import { Node as Neo4jNode } from 'neo4j-driver'

export type Node<T> =
  | (Neo4jNode<T> & {
      properties: { id: string; name: string; label: string; color: string }
    })
  | null

export type Edge = {
  data: { source: string; target: string; relationship: string }
}

export type Elements = {
  nodes: { data: Node['properties'] }[]
  edges: Edge[]
}

ここで、Node型はPersonノードを、Edge型はグラフ表示する際のリレーションを、Elements型はグラフ表示用のデータの型を定義しています。

Neo4jドライバを取得

続いて、Neo4jドライバを取得する関数を定義します。

src/packages/Search/createDriver.tsファイルを追加して、以下のようにコードを記述してください。

import { Driver } from "neo4j-driver/types/driver";
import neo4j from "neo4j-driver";

export const createDriver = async (): Promise<Driver> =>
    await neo4j.driver(
        process.env.REACT_APP_NEO4J_URL || '',
        neo4j.auth.basic(
            process.env.REACT_APP_NEO4J_USER || '',
            process.env.REACT_APP_NEO4J_PW || ''
        )
    )

Neo4jへのアクセス情報には、先ほど.envで定義した環境変数を使用しています。

全ての「Person」ノードを取得

次に、ターゲットの自動補完用に、全てのPersonノードを取得する関数を追加しておきます。

src/packages/Search/getAllPersons.tsファイルを追加して、以下のようにコードを記述してください。

import { Node } from "../@types/App";
import { QueryResult } from "neo4j-driver";
import { createDriver } from "./createDriver";

export const getAllPersons = async (): Promise<Node<any>[]> => {
    const driver = await createDriver()
    const session = await driver.session()

    const result: QueryResult | void = await session
        .run('MATCH (n:Person) RETURN n')
        .catch((error) => {
            console.log(error)
        })

    session.close()
    await driver.close()

    return result ? result.records?.map((record) => record.get('n')) : []
}

ここで、先ほど定義したcreateDriver関数を使用して、driverを取得し、sessionをスタートしています。

スタートしたsessionで、Neo4jクエリMATCH (n:Person) RETURN nを実行して、全てのPersonノードを取得しています。

ターゲット選択用のセレクタを追加

続いて、今回の記事の本丸である、ターゲット選択用のセレクタを実装します。

ファイルsrc/packages/Search/Search.tsxを追加して、以下のようなコードを追加してください。

import Autocomplete from "@material-ui/lab/Autocomplete";
import { Node } from "../@types/App";
import { TextField } from "@material-ui/core";
import React, { ChangeEvent, useState } from "react";

type Props = {
    persons: Node<any>[] | []
}

export default function Search({ persons }: Props) {
    const [target, setTarget] = useState<Node<any>>(null)

    const handleTargetChange = (event: ChangeEvent<{}>, newTarget: Node<any>) => setTarget(newTarget)

    return <div>
        <Autocomplete
            id="target"
            value={target}
            onChange={handleTargetChange}
            options={persons}
            getOptionLabel={(person: Node<any>) => person?.properties?.id || ''}
            style={{ width: 300 }}
            renderInput={(params) => (
                <TextField {...params} label="target" variant="outlined"/>
            )}
        />
    </div>
}

Searchコンポーネントでは、material-uiAutocompleteコンポーネントを使用しています。

これにより、プロパティpersonsの内容がテキストフィールドで選択候補として表示され、検索または選択によりターゲットを選ぶことができます。

仕上げ

最後に、ここまでに作成したコードを統合したいと思います。

src/App.tsxを以下のように書き換えてください。

import React, { useEffect, useState } from 'react';
import { Node } from "./packages/@types/App";
import { getAllPersons } from "./packages/Search/getAllPersons";
import Search from "./packages/Search/Search";

function App() {
    const [persons, setPersons] = useState<Node<any>[] | []>([])

    useEffect(() => {
        (async () => setPersons(await getAllPersons()))()
    }, [])

    return (
        <div>
            <div>
                <Search persons={persons}/>
            </div>
        </div>
    )
}

export default App;

以下の部分で、ロード時にNeo4jから全てのPersonを取得して、personsステートに設定しています。

useEffect(() => {
    (async () => setPersons(await getAllPersons()))()
}, [])

設定されたpersonsSearchコンポーネントの自動補完の候補に追加されます。

動作確認

ターゲット選択用セレクタのコードが全て用意できたので、実際に動かして動作を確認してみます。

以下のコマンドで、Reactアプリをスタートしてください。

yarn start

アプリを起動したら、http://localhost:3000/にアクセスしてください。

ターゲット選択用のセレクタが表示されていると思うので、クリックしてみてください。

以下のようにターゲット候補のPersonが表示されていれば実装成功です。

【Neo4j】Dockerで試すNeo4j【第4回/JavaScript編 その1】

サンプルコード

以下のリポジトリに「Dockerで試すNeo4j」シリーズ全話分のコードを設置しています。

うまく動かない場合は、上記のリポジトリをクローンして試してみてください。

まとめ

今回の記事では、JavaScriptとNeo4jの連携環境のセットアップし、ターゲット選択用のセレクタを実装してみました。

また、アプリの作成を通して、JavaScriptとNeo4jの連携についても解説してみました。

次回は、今回実装したターゲット選択用のセレクタを使って、特定のPersonFOLLOW関係があるPersonを検索するコードを追加したいと思います。

↓ 「Dockerで試すNeo4j」シリーズ記事一覧はこちら