<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Christophe Willemsen</title>
    <description>The latest articles on Forem by Christophe Willemsen (@ikwattro).</description>
    <link>https://forem.com/ikwattro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F226354%2F6aaed96c-be22-4986-bf4a-317d56fb1149.jpg</url>
      <title>Forem: Christophe Willemsen</title>
      <link>https://forem.com/ikwattro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ikwattro"/>
    <language>en</language>
    <item>
      <title>Write a kubectl plugin in Java with jbang and fabric8</title>
      <dc:creator>Christophe Willemsen</dc:creator>
      <pubDate>Sat, 26 Dec 2020 12:48:09 +0000</pubDate>
      <link>https://forem.com/ikwattro/write-a-kubectl-plugin-in-java-with-jbang-and-fabric8-566</link>
      <guid>https://forem.com/ikwattro/write-a-kubectl-plugin-in-java-with-jbang-and-fabric8-566</guid>
      <description>&lt;p&gt;If you use &lt;code&gt;kubectl&lt;/code&gt; frequently, you might be find yourself running the same sequence of commands over and over again.&lt;/p&gt;

&lt;p&gt;A frequent use case yielding such sequences is for &lt;code&gt;viewing a secret&lt;/code&gt; which requires to find the secret and decode its base64 form.&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll go through how to create a &lt;code&gt;kubectl&lt;/code&gt; plugin in Java. The plugin will do the following after we execute the &lt;code&gt;kubectl lp&lt;/code&gt; command : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List the pods with their name, status and a default empty message&lt;/li&gt;
&lt;li&gt;If the pod is not in running state, display a 🔥 icon in front of its line, otherwise display a ✅ icon&lt;/li&gt;
&lt;li&gt;Display a message why the pod is not running&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Jbang
&lt;/h2&gt;

&lt;p&gt;To write the plugin in Java, I will use &lt;a href="https://github.com/jbangdev/jbang"&gt;jbang&lt;/a&gt;. &lt;br&gt;
Jbang is a framework that brings scripting to the Java land.&lt;/p&gt;

&lt;p&gt;If you don't have Jbang yet installed, just run the following :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;jbangdev/tap/jbang
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or check the &lt;a href="https://github.com/jbangdev/jbang#installation"&gt;documentation&lt;/a&gt; to find the instructions for your operating system.&lt;/p&gt;

&lt;p&gt;The next step is to create a template with the initializer, I'll call the script &lt;code&gt;lp&lt;/code&gt; for &lt;code&gt;list pods&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jbang init &lt;span class="nt"&gt;--template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cli lp.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will create a fully functional script &lt;code&gt;lp.java&lt;/code&gt; with the following content :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;///usr/bin/env jbang "$0" "$@" ; exit $?&lt;/span&gt;
&lt;span class="c1"&gt;//DEPS info.picocli:picocli:4.5.0&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;picocli.CommandLine&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;picocli.CommandLine.Command&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;picocli.CommandLine.Parameters&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.Callable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Command&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mixinStandardHelpOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lp 0.1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lp made with jbang"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;lp&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Parameters&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"The greeting to print"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"World!"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CommandLine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;lp&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exitCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// your business logic goes here...&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is a simple template that greets you, try it with the following command :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;jbang lp.java Chris
Hello Chris
&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding dependencies to your scripts is as simple as adding a comment in the beginning of the file with the dependency artifact :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;//DEPS io.fabric8:kubernetes-client:4.13.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we added the &lt;code&gt;fabric8&lt;/code&gt; kubernetes client library. While we're at it, let's see how this client works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fabric8
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/fabric8io/kubernetes-client"&gt;fabric8&lt;/a&gt; kubernetes client is a library that provides access to the Kubernetes Rest API via a simple and human friendly DSL.&lt;/p&gt;

&lt;p&gt;For eg, here is how you find only the pods that are not in a &lt;code&gt;RUNNING&lt;/code&gt; status :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;kc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pods&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nc"&gt;PodStatusUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRunning&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our scenario, we will use the client to retrieve all the pods and create a new domain model &lt;code&gt;PodInfo&lt;/code&gt; with the name, the state and an optional message :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PodInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getPods&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;KubernetesClient&lt;/span&gt; &lt;span class="n"&gt;kc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;kc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultKubernetesClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to create default Kubernetes client"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;kc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pods&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;PodInfoState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PodStatusUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isRunning&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNNING&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAILING&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PodInfoState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNNING&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PodStatusUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContainerStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getWaiting&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PodInfo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMetadata&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PodInfo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PodInfo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt; &lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;PodInfoState&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;RUNNING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;FAILING&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also add the &lt;code&gt;j-text-utils&lt;/code&gt; java library so we can display everything in a table&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//DEPS com.massisframework:j-text-utils:0.3.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;printTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PodInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[][]&lt;/span&gt; &lt;span class="n"&gt;tableData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;podInfo&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt;
                        &lt;span class="n"&gt;podInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PodInfoState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNNING&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="no"&gt;CHECK_MARK&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;FIRE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;podInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                        &lt;span class="n"&gt;podInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                        &lt;span class="n"&gt;podInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;})&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toArray&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[][]::&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;columnNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"state"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TextTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnNames&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tableData&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;printTable&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full code can be found here : &lt;a href="https://github.com/ikwattro/kubectl-plugin-java-jbang"&gt;https://github.com/ikwattro/kubectl-plugin-java-jbang&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we run our script, we can see it in action :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;jbang lp.java
&lt;span class="o"&gt;[&lt;/span&gt;jbang] Building jar...
SLF4J: Failed to load class &lt;span class="s2"&gt;"org.slf4j.impl.StaticLoggerBinder"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
SLF4J: Defaulting to no-operation &lt;span class="o"&gt;(&lt;/span&gt;NOP&lt;span class="o"&gt;)&lt;/span&gt; logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder &lt;span class="k"&gt;for &lt;/span&gt;further details.
________________________________________________________________________________________
|   | name                             | state  | message                               |
|&lt;span class="o"&gt;=======================================================================================&lt;/span&gt;|
| ✅ | neo4j-neo4j-core-0               | RUNNING|                                       |
| ✅ | nginx-app                        | RUNNING|                                       |
| 🔥| nginx-deployment-77fbdb7874-8gkcr| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| 🔥| nginx-deployment-77fbdb7874-8jps7| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| 🔥| nginx-deployment-77fbdb7874-vx27r| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| ✅ | postgres                         | RUNNING|                                       |
| ✅ | coredns-f9fd979d6-qwgfm          | RUNNING|                                       |
| ✅ | etcd-minikube                    | RUNNING|                                       |
| ✅ | kube-apiserver-minikube          | RUNNING|                                       |
| ✅ | kube-controller-manager-minikube | RUNNING|                                       |
| ✅ | kube-proxy-s5bcc                 | RUNNING|                                       |
| ✅ | kube-scheduler-minikube          | RUNNING|                                       |
| ✅ | storage-provisioner              | RUNNING|                                       |
&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Extending kubectl
&lt;/h2&gt;

&lt;p&gt;Extending &lt;code&gt;kubectl&lt;/code&gt; is very easy as it has mainly 3 conventions : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to place your script in your path&lt;/li&gt;
&lt;li&gt;The name of the script should start with &lt;code&gt;kubectl-&lt;/code&gt; and not have an extension&lt;/li&gt;
&lt;li&gt;The script should be executable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will need to pay attention, because when generating the &lt;code&gt;jbang&lt;/code&gt; template, the very first line was commented for playing nicely with IDEs when editing the script.&lt;/p&gt;

&lt;p&gt;Just uncomment the line :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="o"&gt;!/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;jbang&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, we will copy the script to our path :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;lp.java /usr/local/bin/kubectl-lp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And make it executable :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/kubectl-lp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now run &lt;code&gt;kubectl lp&lt;/code&gt; for listing your pods :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl lp
SLF4J: Failed to load class &lt;span class="s2"&gt;"org.slf4j.impl.StaticLoggerBinder"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
SLF4J: Defaulting to no-operation &lt;span class="o"&gt;(&lt;/span&gt;NOP&lt;span class="o"&gt;)&lt;/span&gt; logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder &lt;span class="k"&gt;for &lt;/span&gt;further details.
________________________________________________________________________________________
|   | name                             | state  | message                               |
|&lt;span class="o"&gt;=======================================================================================&lt;/span&gt;|
| ✅ | neo4j-neo4j-core-0               | RUNNING|                                       |
| ✅ | nginx-app                        | RUNNING|                                       |
| 🔥| nginx-deployment-77fbdb7874-8gkcr| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| 🔥| nginx-deployment-77fbdb7874-8jps7| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| 🔥| nginx-deployment-77fbdb7874-vx27r| FAILING| Back-off pulling image &lt;span class="s2"&gt;"nginx2:1.16.1"&lt;/span&gt;|
| ✅ | postgres                         | RUNNING|                                       |
| ✅ | coredns-f9fd979d6-qwgfm          | RUNNING|                                       |
| ✅ | etcd-minikube                    | RUNNING|                                       |
| ✅ | kube-apiserver-minikube          | RUNNING|                                       |
| ✅ | kube-controller-manager-minikube | RUNNING|                                       |
| ✅ | kube-proxy-s5bcc                 | RUNNING|                                       |
| ✅ | kube-scheduler-minikube          | RUNNING|                                       |
| ✅ | storage-provisioner              | RUNNING|                                       |
&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The Kubectl tool is easily extensible and Jbang allows to write scripts in Java in no time, where we would probably have turned to Python before that tool existed.&lt;/p&gt;

&lt;h3&gt;
  
  
  References :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The code from this blog post : &lt;a href="https://github.com/ikwattro/kubectl-plugin-java-jbang"&gt;https://github.com/ikwattro/kubectl-plugin-java-jbang&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl&lt;/code&gt; : plugins documentation : &lt;a href="https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/"&gt;https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jbang&lt;/code&gt; : &lt;a href="https://github.com/jbangdev/jbang#installation"&gt;https://github.com/jbangdev/jbang#installation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fabric8&lt;/code&gt; : &lt;a href="https://github.com/fabric8io/kubernetes-client"&gt;https://github.com/fabric8io/kubernetes-client&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>java</category>
    </item>
    <item>
      <title>Handling synonyms in Neo4j Full Text Search</title>
      <dc:creator>Christophe Willemsen</dc:creator>
      <pubDate>Mon, 11 Nov 2019 11:44:19 +0000</pubDate>
      <link>https://forem.com/ikwattro/handling-synonyms-in-neo4j-full-text-search-232p</link>
      <guid>https://forem.com/ikwattro/handling-synonyms-in-neo4j-full-text-search-232p</guid>
      <description>&lt;p&gt;So you have followed the &lt;a href="https://graphaware.com/neo4j/2019/01/11/neo4j-full-text-search-deep-dive.html"&gt;Deep Dive into Neo4j's Full Text Search tutorial&lt;/a&gt;, learned even how to &lt;a href="https://graphaware.com/neo4j/2019/09/06/custom-fulltext-analyzer.html"&gt;create custom analyzers&lt;/a&gt; and finally watched the &lt;a href="https://graphaware.com/resources/#video-full-text-search-nodes-19"&gt;Full Text Search tips and tricks&lt;/a&gt; talk at the &lt;a href="https://graphaware.com/neo4j-consultancy/"&gt;Neo4j&lt;/a&gt; Nodes19 online conference?&lt;/p&gt;

&lt;p&gt;Still, searching for &lt;code&gt;boat&lt;/code&gt; does not yield results containing &lt;code&gt;yacht&lt;/code&gt; or &lt;code&gt;ship&lt;/code&gt;, and you're wondering how to make your search engine a bit more relevant for your users?  &lt;/p&gt;

&lt;p&gt;Don't go any further, you'll learn how to do it, now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Synonyms
&lt;/h2&gt;

&lt;p&gt;A synonym is a word or phrase that means exactly or nearly the same as another word or phrase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why synonyms ?
&lt;/h2&gt;

&lt;p&gt;It's all about recall! In other words, to help your users find the content they're interested in without them having to know specific terms.&lt;/p&gt;

&lt;p&gt;A user searching for &lt;code&gt;coffee&lt;/code&gt; should probably be seeing results containing &lt;code&gt;latte macchiato&lt;/code&gt;, &lt;code&gt;espresso&lt;/code&gt; or even &lt;code&gt;ristretto&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
   Lists of synonyms
&lt;/h2&gt;

&lt;p&gt;You can find 3rd party word lists for synonyms, such as WordNet or ConceptNet5, howeveer, appropriate word lists are domain/application/use-case dependent, and the best fit is generally a self-curated synonyms word list.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use them ?
&lt;/h2&gt;

&lt;p&gt;The first thing to do, is to create a word list with the following format :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coffee,latte macchiato,espresso,ristretto
boat,yacht,sailing vessel,ship
fts,full text search, fulltext search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to create a custom analyzer using the synonym filter. Since we're using an analyzer the first question that might come to mind is :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do I have to reindex all the documents when my synonyms list change ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is yes, using a query time synonym filter is &lt;em&gt;very bad(TM)&lt;/em&gt;, for the following reasons :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The QueryParser tokenizes before giving the text to the analyzer, so if a user searches for &lt;code&gt;sailing vessel&lt;/code&gt;, the analyzer will be given the words &lt;code&gt;sailing&lt;/code&gt; and &lt;code&gt;vessel&lt;/code&gt; separately, and will not know they match a synonym&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Multi-Word synonyms will also not work in phrase queries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The IDF of rare synonyms will be boosted&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More information can be found in the &lt;a href="https://cwiki.apache.org/confluence/display/solr/AnalyzersTokenizersTokenFilters#solr.SynonymFilterFactory"&gt;Solr documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's create our custom analyzer for synonyms then :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AnalyzerProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SynonymAnalyzer&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AnalyzerProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;ANALYZER_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"synonym-custom"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SynonymAnalyzer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ANALYZER_NAME&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Analyzer&lt;/span&gt; &lt;span class="nf"&gt;createAnalyzer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;synFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"synonyms.txt"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;Analyzer&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomAnalyzer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTokenizer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StandardTokenizerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTokenFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StandardFilterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTokenFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SynonymFilterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"synonyms"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;synFile&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTokenFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LowerCaseFilterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to create analyzer"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;description&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"The default, standard analyzer with a synonyms file. This is an example analyzer for educational purposes."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;em&gt;very&lt;/em&gt; important note is that the &lt;code&gt;LowerCaseFilter&lt;/code&gt; comes after the &lt;code&gt;SynonymFilter&lt;/code&gt;, in some use cases it causes synonyms to not be recognized, for example with the following list :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GB,gibabyte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the lowercase filter is applied before synonyms, then the tokens will not match.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;synonyms.txt&lt;/code&gt; file with your synonyms list in the &lt;code&gt;conf/&lt;/code&gt; directory of your Neo4j instance :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;conf/synonyms.txt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coffee,latte macchiato,espresso,ristretto
boat,yacht,sailing vessel,ship
fts,full text search, fulltext search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build your analyzer jar and put it in the &lt;code&gt;plugins&lt;/code&gt; directory of Neo4j and restart the database if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Index
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.index.fulltext.createNodeIndex&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'syndemo'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; 
  &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Article'&lt;/span&gt;&lt;span class="ss"&gt;],&lt;/span&gt; 
  &lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="ss"&gt;],&lt;/span&gt; 
  &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;analyzer:&lt;/span&gt;&lt;span class="s1"&gt;'synonym-custom'&lt;/span&gt;&lt;span class="ss"&gt;}&lt;/span&gt;
&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an Article node with some text :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;n:&lt;/span&gt;&lt;span class="n"&gt;Article&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;text:&lt;/span&gt; &lt;span class="s2"&gt;"This is an article about Full Text Search and Neo4j, let's go !"&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Query the index :
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.index.fulltext.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'syndemo'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fts'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;╒══════════════════════════════════════════════════════════════════════╤══════════════════╕
│"node"                                                                │"score"           │
╞══════════════════════════════════════════════════════════════════════╪══════════════════╡
│{"text":"This is an article about Full Text Search and Neo4j, let's go│1.2616268396377563│
│ !"}                                                                  │                  │
└──────────────────────────────────────────────────────────────────────┴──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly, a search for &lt;code&gt;fulltext&lt;/code&gt; will return the result as well. But let's get fancy, heuu &lt;code&gt;fuzzy !&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.index.fulltext.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'syndemo'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fullt*'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prefix and synonyms ?
&lt;/h3&gt;

&lt;p&gt;There is one limitation : prefix,fuzzy,.. queries do not use the analyzer, they produce term or multiterm queries instead.&lt;/p&gt;

&lt;p&gt;But there is a trick you can use, add an &lt;code&gt;NgramFilter&lt;/code&gt; to your analyzer and use a phrase query, so fts and its synonyms will have their ngrams tokenized and stored/retrieved in the index :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Analyzer&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomAnalyzer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="c1"&gt;//...&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTokenFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NGramFilterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"minGramSize"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"maxGramSize"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;analyzer&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NgramTokenFilter&lt;/code&gt; will tokenize the inputs into n-grams of the given sizes, here min 3 and max 5. So for the following input :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fulltext search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The index will contain the n-grams &lt;code&gt;ful, full, fullt, ull, ullt, ullte, lte, ltex, ltext&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also use the &lt;code&gt;EdgeNgramFilter&lt;/code&gt; will will produce n-grams only from the beginnig of the token, for the same example as above the n-grams will be &lt;code&gt;ful, full, fullt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Re-deploy your plugin, restart the database, drop and recreate the index and now :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.index.fulltext.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'syndemo'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'"fullt*"'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;╒══════════════════════════════════════════════════════════════════════╤═══════════════════╕&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;                                                                &lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="s2"&gt;"score"&lt;/span&gt;            &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;╞══════════════════════════════════════════════════════════════════════╪═══════════════════╡&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"This is an article about Full Text Search and Neo4j, let's go│0.04872262850403786│
│ !"&lt;/span&gt;&lt;span class="ss"&gt;}&lt;/span&gt;                                                                  &lt;span class="err"&gt;│&lt;/span&gt;                   &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;└──────────────────────────────────────────────────────────────────────┴───────────────────┘&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finalize, let's try some other phrase queries :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;db.index.fulltext.queryNodes&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'syndemo'&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'"article fullte*"~2'&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;╒══════════════════════════════════════════════════════════════════════╤══════════════════╕&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;                                                                &lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="s2"&gt;"score"&lt;/span&gt;           &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;╞══════════════════════════════════════════════════════════════════════╪══════════════════╡&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"This is an article about Full Text Search and Neo4j, let's go│2.3429081439971924│
│ !"&lt;/span&gt;&lt;span class="ss"&gt;}&lt;/span&gt;                                                                  &lt;span class="err"&gt;│&lt;/span&gt;                  &lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;└──────────────────────────────────────────────────────────────────────┴──────────────────┘&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Synonyms are a valuable asset when building search engines, offering a better recall and thus a better user experience. &lt;/p&gt;

&lt;p&gt;You can find all the code from this blog post on this example &lt;a href="https://github.com/graphaware/neo4j-full-text-search-extra"&gt;Github repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>neo4j</category>
      <category>lucene</category>
      <category>search</category>
      <category>synonym</category>
    </item>
  </channel>
</rss>
