<?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>DEV Community: GiBoOw</title>
    <description>The latest articles on DEV Community by GiBoOw (@giboow).</description>
    <link>https://dev.to/giboow</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%2F617140%2F3009dc7f-cc9b-48e3-9e91-7d8facb5ae65.jpeg</url>
      <title>DEV Community: GiBoOw</title>
      <link>https://dev.to/giboow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/giboow"/>
    <language>en</language>
    <item>
      <title>PostgreSQL - How to boost your geolocation queries</title>
      <dc:creator>GiBoOw</dc:creator>
      <pubDate>Thu, 24 Mar 2022 16:34:14 +0000</pubDate>
      <link>https://dev.to/giboow/postgresql-how-to-boost-your-geolocation-point-searches-fn3</link>
      <guid>https://dev.to/giboow/postgresql-how-to-boost-your-geolocation-point-searches-fn3</guid>
      <description>&lt;p&gt;Recently I wanted to create a small mobile app that would search for mailboxes that are around the user's geolocation. Other apps exist, but their performance wasn't really up to scratch, so I wanted to try and find some solutions!&lt;/p&gt;

&lt;p&gt;To start with, I managed to retrieve a dataset from the &lt;a href="http://data.gouv.fr/" rel="noopener noreferrer"&gt;data.gouv.fr&lt;/a&gt; website: &lt;a href="https://www.data.gouv.fr/fr/datasets/liste-des-boites-aux-lettres-de-rue-france-metropolitaine-et-dom-avec-heure-limite-de-depot-1/" rel="noopener noreferrer"&gt;https://www.data.gouv.fr/fr/datasets/liste-des-boites-aux-lettres-de-rue-france-metropolitaine-et-dom-avec-heure-limite-de-depot-1/&lt;/a&gt; . This dataset contains 140 000 entries, of course the user who wants to display the mailboxes close to him, will not need to display all the mailboxes available in the database, but only those close to him, for example 10km.&lt;/p&gt;

&lt;p&gt;For my project I use a SpringBoot API, but that's not what you'll be interested in today. The real problem is how to retrieve all the data, stored in a PostgreSQL database, that are geolocated near a point in a convenient time!&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;My goal is to find a set of points that are within a 10km radius, in record time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F648r2vgf6lhv4zpaiosm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F648r2vgf6lhv4zpaiosm.png" alt="The goal graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PostGIS to the rescue
&lt;/h2&gt;

&lt;p&gt;PostGIS is an extension of PostgreSQL, it offers new functions and data types that facilitate geolocation searches.&lt;/p&gt;

&lt;p&gt;To install PostGIS on your server :&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;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgis


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

&lt;/div&gt;

&lt;p&gt;It must then be activated in PosgresSQL, by connecting to the database and adding the &lt;strong&gt;postgis&lt;/strong&gt; extension. It will then be active only in the selected database.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="n"&gt;psql&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ubuntu&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;ubuntu0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;Type&lt;/span&gt; &lt;span class="nv"&gt;"help"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;postgis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can now create a table that contains an id, latitude, longitude and a GPS point. The SRID 4326 value is used to declare a point that uses the geographic reference system.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;bigserial&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lat&lt;/span&gt; &lt;span class="nb"&gt;real&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;long&lt;/span&gt; &lt;span class="nb"&gt;real&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;geography&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can then easily add a point using the following query:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;long&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Optimization
&lt;/h2&gt;

&lt;p&gt;To validate my optimisation, I added 140,000 rows of geolocated data to my table.&lt;/p&gt;

&lt;p&gt;Stupidly, I wanted to search for all the points that were within a radius of less than 10km with the &lt;strong&gt;st_distancesphere&lt;/strong&gt; function.&lt;/p&gt;

&lt;p&gt;Here is the query I run to retrieve all GPS points within a 10km radius:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYSE&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BUFFERS&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;st_distancesphere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
&lt;span class="n"&gt;st_distancesphere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The PostgeSQL query analysis shows that no index is used and that the query takes 463ms to return 21 points that match the conditions.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="n"&gt;Gather&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2776968&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;47062&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;851&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;461&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;391&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;115&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;Workers&lt;/span&gt; &lt;span class="n"&gt;Planned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;Workers&lt;/span&gt; &lt;span class="n"&gt;Launched&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="n"&gt;Buffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1759&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;Parallel&lt;/span&gt; &lt;span class="n"&gt;Seq&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2771261&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;27684&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;218&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;887&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;415&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;046&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;"        Filter: (st_distance(geography((location)::geometry), '0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, false) &amp;lt; '10000'::double precision)"&lt;/span&gt;
        &lt;span class="k"&gt;Rows&lt;/span&gt; &lt;span class="n"&gt;Removed&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;70535&lt;/span&gt;
        &lt;span class="n"&gt;Buffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1759&lt;/span&gt;
&lt;span class="n"&gt;Planning&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;376&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="n"&gt;JIT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;Functions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="nv"&gt;"  Options: Inlining true, Optimization true, Expressions true, Deforming true"&lt;/span&gt;
&lt;span class="nv"&gt;"  Timing: Generation 2.485 ms, Inlining 178.841 ms, Optimization 117.797 ms, Emission 56.983 ms, Total 356.106 ms"&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;463&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;431&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We are therefore going to create an index that will allow us to speed up the query (see &lt;a href="http://postgis.net/workshops/postgis-intro/indexing.html" rel="noopener noreferrer"&gt;http://postgis.net/workshops/postgis-intro/indexing.html&lt;/a&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;test_position_geography_index&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GIST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geography&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;But we are in for a nasty surprise, as the index still doesn't work. After a quick search, I found out that only certain PostGIS functions can use indexing (see the doc here: &lt;a href="http://postgis.net/workshops/postgis-intro/indexing.html#spatially-indexed-functions" rel="noopener noreferrer"&gt;http://postgis.net/workshops/postgis-intro/indexing.html#spatially-indexed-functions&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So I decided to use the ST_DWithin function which returns true if two points are at the distance given in the 3rd parameter.&lt;/p&gt;

&lt;p&gt;Here is the query followed by the analysis:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;

&lt;span class="k"&gt;EXPLAIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYSE&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BUFFERS&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;st_distancesphere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
   &lt;span class="n"&gt;ST_DWithin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ST_SetSRID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ST_MakePoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;548462&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0779799&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;geography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&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 sql"&gt;&lt;code&gt;

&lt;span class="n"&gt;Bitmap&lt;/span&gt; &lt;span class="n"&gt;Heap&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2390&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;205&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;115&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;"  Filter: st_dwithin(location, '0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, '10000'::double precision, true)"&lt;/span&gt;
  &lt;span class="k"&gt;Rows&lt;/span&gt; &lt;span class="n"&gt;Removed&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="n"&gt;Heap&lt;/span&gt; &lt;span class="n"&gt;Blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;exact&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;
  &lt;span class="n"&gt;Buffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;188&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;Bitmap&lt;/span&gt; &lt;span class="k"&gt;Index&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;test_position_geography_index&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;288&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;288&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;315&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;"        Index Cond: (location &amp;amp;&amp;amp; _st_expand('0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, '10000'::double precision))"&lt;/span&gt;
        &lt;span class="n"&gt;Buffers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt;
&lt;span class="n"&gt;Planning&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;446&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;288&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can see that the &lt;strong&gt;test_position_geography_index&lt;/strong&gt; is used and that it allows to recover the 21 points in &lt;strong&gt;2.288ms&lt;/strong&gt; 🤯. The gain is huge, it allows to reduce considerably the execution time and the processor usage time.&lt;/p&gt;

&lt;p&gt;In my mailbox geolocation API, this allowed me to cut the request time by a factor of 10 and to relieve the server when simulating a server load with the Apache &lt;strong&gt;ab&lt;/strong&gt; performance testing tool (&lt;a href="https://httpd.apache.org/docs/2.4/fr/programs/ab.html" rel="noopener noreferrer"&gt;https://httpd.apache.org/docs/2.4/fr/programs/ab.html&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Have fun with PostGIS, it's a great tool ;)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SpringBoot - Contextualizing serialization</title>
      <dc:creator>GiBoOw</dc:creator>
      <pubDate>Mon, 28 Feb 2022 15:21:20 +0000</pubDate>
      <link>https://dev.to/giboow/springboot-contextualizing-serialization-29mp</link>
      <guid>https://dev.to/giboow/springboot-contextualizing-serialization-29mp</guid>
      <description>&lt;p&gt;Every time I developed an API, I faced the same problem: how to filter the data according to my API calls. &lt;/p&gt;

&lt;p&gt;Indeed, some calls that are available for example for a "user" call will not return the same data as a call available for an "administrator".&lt;/p&gt;

&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;To start, we will build a REST API for vehicle management, so we will have a "&lt;strong&gt;Vehicle "&lt;/strong&gt; object that will be made up as follows: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;id&lt;/strong&gt;: Long&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;brand&lt;/strong&gt;: String ⇒ The brand, for example (Renault)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;model&lt;/strong&gt;: String ⇒ The model, for example (Megane)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;registrationPlate&lt;/strong&gt;: String ⇒ The registration plate of the car, for example (SP-800-TT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;serialNumber&lt;/strong&gt;: String ⇒ The serial number of the vehicle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This data can be read by any user. In addition, we will define data that the user does not need, but that the administrator can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CreatedAt&lt;/strong&gt; : LocalDatetime ⇒ Date of creation of the object in the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UpdatedAt&lt;/strong&gt; : LocalDatetime ⇒ Date of modification of the object in database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To save time during my developments, I define an abstract Class which is used as a base for all my entities:&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;@MappedSuperclass&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@SuperBuilder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityAbstract&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&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;Integer&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@CreatedDate&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columnDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"timestamp default now()"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;READ_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;JsonFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd'T'HH:mm:ss'Z'"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@LastModifiedDate&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columnDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"timestamp default now()"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;READ_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;JsonFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd'T'HH:mm:ss'Z'"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;updatedAt&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;Then I define my Vehicle class:&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;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Data&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;Vehicle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;EntityAbstract&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;


    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;registrationPlate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;brand&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;serialNumber&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;Finally we will define a Controller that will allow access to the data using a data retrieval service with a user route and an Admin route:&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;@RestController&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;VehicleController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;VehicleService&lt;/span&gt; &lt;span class="n"&gt;vehicleService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/vehicle/list"&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;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listVehicles&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listVehicles&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin/vehicle/list"&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;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;adminListVehicles&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listVehicles&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;For the moment, the rendering of the data is exactly the same, but we will see later how to filter the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jackson to the rescue!
&lt;/h2&gt;

&lt;p&gt;To filter the data, we will use a feature of the Jackson library, the JsonView!&lt;br&gt;
We need to define a class containing interfaces that correspond to the filter levels of our API:&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="cm"&gt;/**
 * Json view filter
 */&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;JsonViews&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Create&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Update&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Create&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Summary&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Update&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;interface&lt;/span&gt; &lt;span class="nc"&gt;Admin&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Summary&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 "&lt;strong&gt;Create "&lt;/strong&gt; level corresponds to the attributes visible for the creation of an object, &lt;strong&gt;"Update"&lt;/strong&gt; extends the previous filter and will allow to filter only the attributes that can be updated. &lt;strong&gt;"Summary"&lt;/strong&gt; extends the properties of -&lt;strong&gt;"Create"&lt;/strong&gt; and &lt;strong&gt;"Update".&lt;/strong&gt; Finally &lt;strong&gt;"Admin"&lt;/strong&gt; allows you to view or update attributes that are only accessible to Administrators.&lt;/p&gt;

&lt;p&gt;This feature is not only used to render a JSON, but also when the API consumes the body of a request.&lt;/p&gt;

&lt;p&gt;We will update the entity so that the attributes are filtered correctly by the API:&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;@MappedSuperclass&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@SuperBuilder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityAbstract&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUTO&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Update&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;private&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@CreatedDate&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columnDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"timestamp default CURRENT_TIMESTAMP"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;READ_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;JsonFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd'T'HH:mm:ss'Z'"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Admin&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="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@LastModifiedDate&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columnDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"timestamp default CURRENT_TIMESTAMP"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;READ_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;JsonFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Shape&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd'T'HH:mm:ss'Z'"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Admin&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="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;updatedAt&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Data&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;Vehicle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;EntityAbstract&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;


    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Create&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;registrationPlate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Create&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Create&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotEmpty&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Create&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;serialNumber&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;Finally we can use it in the controller :&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;@RestController&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;VehicleController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;VehicleService&lt;/span&gt; &lt;span class="n"&gt;vehicleService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="cm"&gt;/**
     * List véhicles
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/vehicle/list"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Summary&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="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listVehicles&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listVehicles&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/**
     * List vehicles (Admin)
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin/vehicle/list"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Summary&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="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;adminListVehicles&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listVehicles&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/**
     * Create vehicle (Admin)
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin/vehicle"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Admin&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="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="nf"&gt;postAdminVehicle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Create&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="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="n"&gt;vehicle&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createVehicle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="cm"&gt;/**
     * Update vehicle (Admin)
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@PutMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/admin/vehicle"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Admin&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="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="nf"&gt;putAdminVehicle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@JsonView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JsonViews&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Update&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="nc"&gt;Vehicle&lt;/span&gt; &lt;span class="n"&gt;vehicle&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;vehicleService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateVehicle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vehicle&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;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vehicle list (User view)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ curl http://localhost:8080/vehicle/list
&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:1,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-205-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"205"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"3KX9YLH2K980HNYYS1YZ"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:2,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-206-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"206"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"VST52ASAH145TIZGJ3LC"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:3,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-207-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"207"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"JI0BNW8D1HYGPFAECEPJ"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:4,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-208-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"208"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"LN97FF02UWRJYRAEGFL5"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:5,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-106-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"106"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"R94DJ8P5PT7M6PB5DP5B"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:6,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-107-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"107"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"OJHHEW2SP2KJDU3CZLFO"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:7,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-108-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"108"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"49LUMOTWMOQSNHK09MR3"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:8,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-306-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"306"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"0KBBBHVTZN9P66I8TVGT"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:9,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-307-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"307"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"TK0GX92MY29AR0H9ZYIB"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:10,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-308-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"308"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"EKNPA002KCQXYAUX04AV"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:11,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-309-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"309"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"D1788D08RU0AU3O5BVXR"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:12,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-405-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"405"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"PHU0M7NKZ799D64VBVJ8"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:13,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-406-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"406"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"NAD3F2ULL5TY4QCECLAO"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:14,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-407-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"407"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"C842THXCOBFFZ0O5U0YK"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:15,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-508-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"508"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"F0T2ELSQ0VPRF1NBY8XH"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:16,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-407-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"407"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"WEBVP95ON3HDX0TGPFXB"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:17,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-508-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"508"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"TEZ3D70UDBY8UKHRKSYY"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:18,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-605-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"605"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"WIGYNFTWH0Q6E85Z2HA2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:19,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-607-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"607"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"OLI3JCTF82WUQK14Z295"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vehicle list (Admin view)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ curl http://localhost:8080/admin/vehicle/list
&lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:1,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-205-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"205"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"3KX9YLH2K980HNYYS1YZ"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:2,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-206-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"206"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"VST52ASAH145TIZGJ3LC"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:3,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-207-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"207"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"JI0BNW8D1HYGPFAECEPJ"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:4,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-208-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"208"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"LN97FF02UWRJYRAEGFL5"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:5,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-106-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"106"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"R94DJ8P5PT7M6PB5DP5B"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:6,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-107-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"107"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"OJHHEW2SP2KJDU3CZLFO"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:7,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-108-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"108"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"49LUMOTWMOQSNHK09MR3"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:8,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-306-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"306"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"0KBBBHVTZN9P66I8TVGT"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:9,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-307-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"307"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"TK0GX92MY29AR0H9ZYIB"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:10,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-308-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"308"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"EKNPA002KCQXYAUX04AV"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:11,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-309-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"309"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"D1788D08RU0AU3O5BVXR"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:12,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-405-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"405"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"PHU0M7NKZ799D64VBVJ8"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:13,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-406-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"406"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"NAD3F2ULL5TY4QCECLAO"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:14,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-407-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"407"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"C842THXCOBFFZ0O5U0YK"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:15,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-508-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"508"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"F0T2ELSQ0VPRF1NBY8XH"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:16,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-407-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"407"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"WEBVP95ON3HDX0TGPFXB"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:17,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-508-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"508"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"TEZ3D70UDBY8UKHRKSYY"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:18,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-605-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"605"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"WIGYNFTWH0Q6E85Z2HA2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;,&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:19,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T14:32:13Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"AA-607-AA"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"607"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"PEUGEOT"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"OLI3JCTF82WUQK14Z295"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create vehicle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~ curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="s1"&gt;'localhost:8080/admin/vehicle'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--data-raw&lt;/span&gt; &lt;span class="s1"&gt;'{
    "brand": "VolksWagen",
    "model": "Golf",
    "serialNumber": "VWVWVWVWVWVWVW",
    "registrationPlate": "GO-123-LF"
}'&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;:20,&lt;span class="s2"&gt;"createdAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T15:34:03Z"&lt;/span&gt;,&lt;span class="s2"&gt;"updatedAt"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-02-28T15:34:03Z"&lt;/span&gt;,&lt;span class="s2"&gt;"registrationPlate"&lt;/span&gt;:&lt;span class="s2"&gt;"GO-123-LF"&lt;/span&gt;,&lt;span class="s2"&gt;"model"&lt;/span&gt;:&lt;span class="s2"&gt;"Golf"&lt;/span&gt;,&lt;span class="s2"&gt;"brand"&lt;/span&gt;:&lt;span class="s2"&gt;"VolksWagen"&lt;/span&gt;,&lt;span class="s2"&gt;"serialNumber"&lt;/span&gt;:&lt;span class="s2"&gt;"VWVWVWVWVWVWVW"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update vehicle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~ curl --location --request PUT 'localhost:8080/admin/vehicle' \
--header 'Content-Type: application/json' \
--data-raw '{
    "id": 20,
    "brand": "VolksWagen",
    "model": "Golf SW",
    "serialNumber": "VWVWVWVWVWVWVW",
    "registrationPlate": "GO-123-LF"
}'
{"id":20,"createdAt":"2022-02-28T15:40:55Z","updatedAt":"2022-02-28T15:40:55Z","registrationPlate":"GO-123-LF","model":"Golf SW","brand":"VolksWagen","serialNumber":"VWVWVWVWVWVWVW"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the github of the project here: &lt;a href="https://github.com/giboow/jsonview-article"&gt;https://github.com/giboow/jsonview-article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Credits : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;photo : &lt;a href="https://unsplash.com/photos/WVl1N2C2zEA"&gt;https://unsplash.com/photos/WVl1N2C2zEA&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>json</category>
      <category>api</category>
    </item>
    <item>
      <title>How to build a video transcoder with SpringBoot and FFMPEG!</title>
      <dc:creator>GiBoOw</dc:creator>
      <pubDate>Wed, 13 Oct 2021 06:14:11 +0000</pubDate>
      <link>https://dev.to/giboow/how-to-build-a-video-transcoder-with-springboot-and-ffmpeg-n7p</link>
      <guid>https://dev.to/giboow/how-to-build-a-video-transcoder-with-springboot-and-ffmpeg-n7p</guid>
      <description>&lt;p&gt;I recently started working on a surveillance camera system and I want to be able to display the collected videos on web pages.&lt;br&gt;
At first glance, it seemed very easy, but I quickly realized that I would have to rack my brains!&lt;/p&gt;

&lt;h2&gt;
  
  
  How it should work?
&lt;/h2&gt;

&lt;p&gt;The camera is connected to an NVR (Numeric Video recorder) which has an API that allows to retrieve the configuration information and the video stream. By searching a little on the Web (Yes the documentation is difficult to access..), I discover that the communication protocol used by the NVR is RTSP (Real-Time Streaming Protocol). This is where I encounter the main problem! How to use this protocol in an HTML page that does not support it? My solution is to use a server that allows to transcode the video in a more known format (MP4) and an ultra standard protocol (Http). This will also allow me to hide the access identifiers to the camera by using my server as a proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgiboow.fr%2Fstatic%2Fimages%2Fpost%2Fproxy-transcoder-ffmpeg%2Fgraph-ffmpeg-transcoder.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgiboow.fr%2Fstatic%2Fimages%2Fpost%2Fproxy-transcoder-ffmpeg%2Fgraph-ffmpeg-transcoder.png" alt="Graph it should work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do we do it?
&lt;/h2&gt;

&lt;p&gt;A great tool that is well known for doing video conversion is &lt;a href="https://www.ffmpeg.org/" rel="noopener noreferrer"&gt;FFMPeg&lt;/a&gt;, so I'm starting to look into how I can use it to convert RSTP. I quickly find a command line that works:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

ffmpeg &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-loglevel&lt;/span&gt; level+info &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nt"&gt;-re&lt;/span&gt; &lt;span class="nt"&gt;-acodec&lt;/span&gt; pcm_s16le &lt;span class="nt"&gt;-rtsp_transport&lt;/span&gt; tcp &lt;span class="nt"&gt;-i&lt;/span&gt; rtsp://user:passwd@192.168.1.200:554/ISAPI/Streaming/channels/101/live &lt;span class="nt"&gt;-vcodec&lt;/span&gt; copy &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nv"&gt;asetrate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22050 &lt;span class="nt"&gt;-acodec&lt;/span&gt; aac &lt;span class="nt"&gt;-b&lt;/span&gt;:a 96k &lt;span class="nt"&gt;-nostdin&lt;/span&gt; myvideo.mp4


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

&lt;/div&gt;

&lt;p&gt;Then how to make a proxy with SpringBoot? It's very simple actually, you just have to use the &lt;a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/StreamingResponseBody.html" rel="noopener noreferrer"&gt;StreamingResponseBody&lt;/a&gt; object. This allows to return an asynchronous request processing, where the application can write directly to the response output stream without blocking the rest of my API.&lt;/p&gt;

&lt;p&gt;Finally, I just have to use FFMPEG in my controller to send the stream via my API. I could have used " Runtime.getRuntime().exec(" ffmpeg...) " but I couldn't figure out how to get my stream. Fortunately I found a magic library &lt;a href="https://github.com/kokorin/Jaffree" rel="noopener noreferrer"&gt;Jaffree&lt;/a&gt; : "Jaffree stands for JAva FFmpeg and FFprobe FREE command line wrapper. Jaffree supports programmatic video production and consumption (with transparency)"&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;Here is the final solution and how to relay a video stream from a HikVision camera so that the format is usable by an HTML page.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.github.kokorin.jaffree.StreamType&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;com.github.kokorin.jaffree.ffmpeg.FFmpeg&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;com.github.kokorin.jaffree.ffmpeg.PipeOutput&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/video"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Log4j2&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;VideoController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/live.mp4"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@ResponseBody&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StreamingResponseBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;livestream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;tipperId&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="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rtspUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rtsp://user:passwd@192.168.1.200:554/ISAPI/Streaming/channels/101/live"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_OCTET_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;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&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;FFmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;atPath&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-re"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-acodec"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pcm_s16le"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-rtsp_transport"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tcp"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-i"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rtspUrl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-vcodec"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"copy"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-af"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"asetrate=22050"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-acodec"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"aac"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArguments&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-b:a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"96k"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOutput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PipeOutput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pumpTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disableStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StreamType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AUDIO&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disableStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StreamType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SUBTITLE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disableStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StreamType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DATA&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFrameCount&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StreamType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VIDEO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100L&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                     &lt;span class="c1"&gt;//1 frame every 10 seconds&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFrameRate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDuration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;HOURS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ismv"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addArgument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-nostdin"&lt;/span&gt;&lt;span class="o"&gt;)&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="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;You will also have to modify the configuration of your SpringBoot application (application.properties file) to increase the timeout for asynchronous requests.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

spring.mvc.async.request-timeout = 3600000


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

&lt;/div&gt;

&lt;p&gt;You just have to call your API on the web page:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"video"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;autoplay&lt;/span&gt; &lt;span class="na"&gt;muted&lt;/span&gt; &lt;span class="na"&gt;loop&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"event?.video"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:8080/video/live.mp4"&lt;/span&gt;
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"video/mp4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Sorry, your browser doesn't support embedded videos.
  &lt;span class="nt"&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And see the result :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgiboow.fr%2Fstatic%2Fimages%2Fpost%2Fproxy-transcoder-ffmpeg%2Fresult-ffmpeg-transcoder.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgiboow.fr%2Fstatic%2Fimages%2Fpost%2Fproxy-transcoder-ffmpeg%2Fresult-ffmpeg-transcoder.png" alt="Final result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://giboow.fr/posts/comment-transcoder-une-video-avec-springboot-et-ffmpeg?utm_source=dev.to"&gt;Find the article in French on my personal website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>ffmpeg</category>
      <category>hikvision</category>
    </item>
  </channel>
</rss>
