├── .env ├── .github ├── dependabot.yml └── mergify.yml ├── .gitignore ├── README.markdown ├── docker-compose.yml ├── mysql.sh ├── pom.xml └── src └── main ├── java └── fr │ └── pilato │ └── demo │ └── legacysearch │ ├── LegacySearchApp.java │ ├── dao │ └── PersonRepository.java │ ├── domain │ ├── Address.java │ ├── GeoPoint.java │ ├── Marketing.java │ └── Person.java │ ├── helper │ ├── CsvReader.java │ ├── PersonGenerator.java │ ├── SSLUtils.java │ └── Strings.java │ ├── service │ └── PersonService.java │ └── webapp │ ├── InitResult.java │ ├── PersonController.java │ └── PersonNotFoundException.java └── resources ├── application.yml ├── dozer.properties ├── logback-spring.xml ├── prenoms.csv └── static ├── advanced ├── advanced.component.js ├── advanced.module.js └── advanced.template.html ├── app.config.js ├── app.module.js ├── compute ├── compute.component.js ├── compute.module.js └── compute.template.html ├── img ├── glyphicons-halflings-white.png └── glyphicons-halflings.png ├── index.html ├── init ├── init.component.js ├── init.module.js └── init.template.html ├── kibana ├── console.txt ├── kibana.component.js ├── kibana.module.js ├── kibana.ndjson └── kibana.template.html ├── person-detail ├── person-detail.component.js ├── person-detail.module.js └── person-detail.template.html └── search ├── search.component.js ├── search.module.js └── search.template.html /.env: -------------------------------------------------------------------------------- 1 | # Password for the 'elastic' user (at least 6 characters) 2 | ELASTIC_PASSWORD=changeme 3 | 4 | # Password for the 'kibana_system' user (at least 6 characters) 5 | KIBANA_PASSWORD=changeme 6 | 7 | # Version of Elastic products 8 | STACK_VERSION=8.14.1 9 | 10 | # Set the cluster name 11 | CLUSTER_NAME=docker-cluster 12 | 13 | # Set to 'basic' or 'trial' to automatically start the 30-day trial 14 | #LICENSE=trial 15 | LICENSE=basic 16 | 17 | # Port to expose Elasticsearch HTTP API to the host 18 | ES_PORT=9200 19 | 20 | # Port to expose Kibana to the host 21 | KIBANA_PORT=5601 22 | 23 | # Increase or decrease based on the available host memory (in bytes) 24 | MEM_LIMIT=1073741824 25 | 26 | # Project namespace (defaults to the current folder name if not set) 27 | COMPOSE_PROJECT_NAME=legacy-search 28 | 29 | MYSQL_VERSION=latest 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 99 9 | target-branch: 06-fuzziness 10 | reviewers: 11 | - dadoonet 12 | assignees: 13 | - dadoonet 14 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatic merge on review 3 | conditions: 4 | - "#approved-reviews-by>=1" 5 | actions: 6 | merge: 7 | method: merge 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | target/ 3 | .idea/ 4 | 5 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Add Search to Legacy Application 2 | ================================ 3 | 4 | Introduction 5 | ------------ 6 | 7 | This is a demo project to show how to add elasticsearch to a legacy SQL project. 8 | 9 | In this branch, you will find the current legacy version of the project. 10 | 11 | 12 | Installation 13 | ------------ 14 | 15 | You need to have: 16 | 17 | * Maven 18 | * JDK8 or higher 19 | * Docker 20 | 21 | Run MySQL database using docker with: 22 | 23 | ```shell 24 | ./mysql.sh 25 | ``` 26 | 27 | Build the application: 28 | 29 | ```sh 30 | mvn clean install 31 | ``` 32 | 33 | Then run it with: 34 | 35 | ``` 36 | java -jar target/legacy-search-8.0-SNAPSHOT.jar 37 | ``` 38 | 39 | Or directly run from Maven: 40 | 41 | ```sh 42 | mvn clean spring-boot:run 43 | ``` 44 | 45 | Note that while developing, you would probably prefer running `LegacySearchApp#main()` 46 | from your IDE to get hot reload of the application. 47 | 48 | Play! 49 | ----- 50 | 51 | ### Some CRUD operations 52 | 53 | ```sh 54 | # Create one person 55 | curl -XPUT http://127.0.0.1:8080/api/1/person/1 -H "Content-Type: application/json" -d '{"name":"David Pilato"}' 56 | 57 | # Read that person 58 | curl http://127.0.0.1:8080/api/1/person/1 59 | 60 | # Update full document 61 | curl -XPUT http://127.0.0.1:8080/api/1/person/1 -H "Content-Type: application/json" -d '{"name":"David Pilato", "children":3}' 62 | 63 | # Check 64 | curl http://127.0.0.1:8080/api/1/person/1 65 | 66 | # Delete 67 | curl -XDELETE http://127.0.0.1:8080/api/1/person/1 68 | 69 | # Check (you should get a 404 error) 70 | curl http://127.0.0.1:8080/api/1/person/1 71 | ``` 72 | 73 | ### Database Initialisation 74 | 75 | ```sh 76 | # Initialize the database with 1 000 (default) or 10 000 persons 77 | curl http://127.0.0.1:8080/api/1/person/_init 78 | curl http://127.0.0.1:8080/api/1/person/_init?size=10000 79 | ``` 80 | 81 | ## Search 82 | 83 | ```sh 84 | # Search for something (`a la google`) 85 | curl "http://127.0.0.1:8080/api/1/person/_search?q=Joe" 86 | ``` 87 | 88 | You can then access the application using your browser: [http://127.0.0.1:8080/](http://127.0.0.1:8080/). 89 | You can also look at [advanced search](http://127.0.0.1:8080/#/advanced). 90 | 91 | Next step 92 | --------- 93 | 94 | Look at [branch 01-direct](https://github.com/dadoonet/legacy-search/tree/01-direct) 95 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.2" 2 | 3 | services: 4 | setup: 5 | image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} 6 | volumes: 7 | - certs:/usr/share/elasticsearch/config/certs 8 | user: "0" 9 | command: > 10 | bash -c ' 11 | if [ x${ELASTIC_PASSWORD} == x ]; then 12 | echo "Set the ELASTIC_PASSWORD environment variable in the .env file"; 13 | exit 1; 14 | elif [ x${KIBANA_PASSWORD} == x ]; then 15 | echo "Set the KIBANA_PASSWORD environment variable in the .env file"; 16 | exit 1; 17 | fi; 18 | if [ ! -f config/certs/ca.zip ]; then 19 | echo "Creating CA"; 20 | bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip; 21 | unzip config/certs/ca.zip -d config/certs; 22 | fi; 23 | if [ ! -f config/certs/certs.zip ]; then 24 | echo "Creating certs"; 25 | echo -ne \ 26 | "instances:\n"\ 27 | " - name: es01\n"\ 28 | " dns:\n"\ 29 | " - es01\n"\ 30 | " - localhost\n"\ 31 | " ip:\n"\ 32 | " - 127.0.0.1\n"\ 33 | > config/certs/instances.yml; 34 | bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key; 35 | unzip config/certs/certs.zip -d config/certs; 36 | fi; 37 | echo "Setting file permissions" 38 | chown -R root:root config/certs; 39 | find . -type d -exec chmod 750 \{\} \;; 40 | find . -type f -exec chmod 640 \{\} \;; 41 | echo "Waiting for Elasticsearch availability"; 42 | until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done; 43 | echo "Setting kibana_system password"; 44 | until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done; 45 | echo "All done!"; 46 | ' 47 | healthcheck: 48 | test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"] 49 | interval: 1s 50 | timeout: 5s 51 | retries: 120 52 | 53 | es01: 54 | depends_on: 55 | setup: 56 | condition: service_healthy 57 | image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} 58 | volumes: 59 | - certs:/usr/share/elasticsearch/config/certs 60 | - esdata01:/usr/share/elasticsearch/data 61 | ports: 62 | - ${ES_PORT}:9200 63 | environment: 64 | - node.name=es01 65 | - cluster.name=${CLUSTER_NAME} 66 | - cluster.initial_master_nodes=es01 67 | - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} 68 | - bootstrap.memory_lock=true 69 | - xpack.security.enabled=true 70 | - xpack.security.http.ssl.enabled=true 71 | - xpack.security.http.ssl.key=certs/es01/es01.key 72 | - xpack.security.http.ssl.certificate=certs/es01/es01.crt 73 | - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt 74 | - xpack.security.transport.ssl.enabled=true 75 | - xpack.security.transport.ssl.key=certs/es01/es01.key 76 | - xpack.security.transport.ssl.certificate=certs/es01/es01.crt 77 | - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt 78 | - xpack.security.transport.ssl.verification_mode=certificate 79 | - xpack.license.self_generated.type=${LICENSE} 80 | mem_limit: ${MEM_LIMIT} 81 | ulimits: 82 | memlock: 83 | soft: -1 84 | hard: -1 85 | healthcheck: 86 | test: 87 | [ 88 | "CMD-SHELL", 89 | "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", 90 | ] 91 | interval: 10s 92 | timeout: 10s 93 | retries: 120 94 | 95 | kibana: 96 | depends_on: 97 | es01: 98 | condition: service_healthy 99 | image: docker.elastic.co/kibana/kibana:${STACK_VERSION} 100 | volumes: 101 | - certs:/usr/share/kibana/config/certs 102 | - kibanadata:/usr/share/kibana/data 103 | ports: 104 | - ${KIBANA_PORT}:5601 105 | environment: 106 | - SERVERNAME=kibana 107 | - ELASTICSEARCH_HOSTS=https://es01:9200 108 | - ELASTICSEARCH_USERNAME=kibana_system 109 | - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD} 110 | - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt 111 | mem_limit: ${MEM_LIMIT} 112 | healthcheck: 113 | test: 114 | [ 115 | "CMD-SHELL", 116 | "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'", 117 | ] 118 | interval: 10s 119 | timeout: 10s 120 | retries: 120 121 | 122 | volumes: 123 | certs: 124 | driver: local 125 | esdata01: 126 | driver: local 127 | kibanadata: 128 | driver: local -------------------------------------------------------------------------------- /mysql.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source .env 4 | 5 | docker pull mysql:$MYSQL_VERSION 6 | docker stop legacy-mysql 7 | docker rm legacy-mysql 8 | docker run --name legacy-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=person -d mysql:$MYSQL_VERSION 9 | docker logs -f legacy-mysql 10 | 11 | # Stop the database when done 12 | docker stop legacy-mysql 13 | docker rm legacy-mysql 14 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 3.3.1 11 | 12 | 13 | fr.pilato.demo 14 | legacy-search 15 | 8.14-SNAPSHOT 16 | Add search to legacy demo 17 | 18 | This project is used for demos to add elasticsearch 19 | to a legacy application 20 | 21 | 22 | 2014 23 | 24 | 25 | 26 | The Apache Software License, Version 2.0 27 | http://www.apache.org/licenses/LICENSE-2.0.txt 28 | repo 29 | 30 | 31 | 32 | 33 | 1.8 34 | 1.8 35 | 2.17.1 36 | UTF-8 37 | 38 | 39 | 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-web 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-thymeleaf 50 | 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-data-jpa 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-actuator 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-devtools 68 | true 69 | 70 | 71 | 72 | 73 | com.mysql 74 | mysql-connector-j 75 | 9.0.0 76 | 77 | 78 | 79 | 80 | com.fasterxml.jackson.core 81 | jackson-databind 82 | ${jackson.version} 83 | 84 | 85 | com.fasterxml.jackson.core 86 | jackson-core 87 | ${jackson.version} 88 | 89 | 90 | com.fasterxml.jackson.core 91 | jackson-annotations 92 | ${jackson.version} 93 | 94 | 95 | com.fasterxml.jackson.datatype 96 | jackson-datatype-jsr310 97 | ${jackson.version} 98 | 99 | 100 | com.github.dozermapper 101 | dozer-core 102 | 7.0.0 103 | 104 | 105 | 106 | 107 | org.webjars 108 | bootstrap 109 | 5.3.3 110 | 111 | 112 | org.webjars 113 | angularjs 114 | 1.6.2 115 | 116 | 117 | org.webjars 118 | angular-ui-bootstrap 119 | 2.5.0 120 | 121 | 122 | 123 | 124 | 125 | 126 | org.springframework.boot 127 | spring-boot-maven-plugin 128 | 129 | 130 | 131 | 132 | 133 | 134 | jaxb 135 | 136 | [1.9) 137 | 138 | 139 | 140 | jakarta.xml.bind 141 | jakarta.xml.bind-api 142 | 4.0.2 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/LegacySearchApp.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch; 21 | 22 | 23 | import com.github.dozermapper.core.DozerBeanMapperBuilder; 24 | import com.github.dozermapper.core.Mapper; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.context.annotation.Bean; 30 | 31 | @SpringBootApplication 32 | public class LegacySearchApp { 33 | private static final Logger logger = LoggerFactory.getLogger(LegacySearchApp.class); 34 | 35 | public static void main(String[] args) { 36 | logger.info("Starting LegacySearch demo application"); 37 | SpringApplication.run(LegacySearchApp.class, args); 38 | } 39 | 40 | @Bean 41 | public Mapper dozerBeanMapper() { 42 | logger.debug("creating dozen bean mapper"); 43 | return DozerBeanMapperBuilder.buildDefault(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/dao/PersonRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package fr.pilato.demo.legacysearch.dao; 20 | 21 | import fr.pilato.demo.legacysearch.domain.Person; 22 | import org.springframework.data.domain.Page; 23 | import org.springframework.data.domain.Pageable; 24 | import org.springframework.data.jpa.repository.Query; 25 | import org.springframework.data.repository.CrudRepository; 26 | import org.springframework.data.repository.PagingAndSortingRepository; 27 | import org.springframework.data.repository.query.QueryByExampleExecutor; 28 | 29 | 30 | /** 31 | * Person Repository. 32 | */ 33 | public interface PersonRepository extends PagingAndSortingRepository, QueryByExampleExecutor, CrudRepository { 34 | 35 | @Query("select p from Person p where p.name like %?1% or p.address.country like %?1% or p.address.city like %?1%") 36 | Page findLikeGoogle(String query, Pageable pageable); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/domain/Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to David Pilato (the "Author") under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. Author licenses this 6 | * file to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.domain; 21 | 22 | import jakarta.persistence.Embedded; 23 | import jakarta.persistence.Entity; 24 | import jakarta.persistence.GeneratedValue; 25 | import jakarta.persistence.GenerationType; 26 | import jakarta.persistence.Id; 27 | 28 | @Entity 29 | public class Address { 30 | 31 | @Id 32 | @GeneratedValue(strategy= GenerationType.AUTO) 33 | private Integer id; 34 | 35 | private String country; 36 | private String zipcode; 37 | private String city; 38 | private String countrycode; 39 | private GeoPoint location; 40 | 41 | public Integer getId() { 42 | return id; 43 | } 44 | 45 | public void setId(Integer id) { 46 | this.id = id; 47 | } 48 | 49 | public String getCountry() { 50 | return country; 51 | } 52 | 53 | public void setCountry(String country) { 54 | this.country = country; 55 | } 56 | 57 | public String getZipcode() { 58 | return zipcode; 59 | } 60 | 61 | public void setZipcode(String zipcode) { 62 | this.zipcode = zipcode; 63 | } 64 | 65 | public String getCity() { 66 | return city; 67 | } 68 | 69 | public void setCity(String city) { 70 | this.city = city; 71 | } 72 | 73 | public String getCountrycode() { 74 | return countrycode; 75 | } 76 | 77 | public void setCountrycode(String countrycode) { 78 | this.countrycode = countrycode; 79 | } 80 | 81 | @Embedded 82 | public GeoPoint getLocation() { 83 | return location; 84 | } 85 | 86 | public void setLocation(GeoPoint location) { 87 | this.location = location; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/domain/GeoPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.domain; 21 | 22 | import jakarta.persistence.Embeddable; 23 | 24 | @Embeddable 25 | public class GeoPoint { 26 | 27 | private double lat; 28 | private double lon; 29 | 30 | public GeoPoint() { 31 | } 32 | 33 | public GeoPoint(double lat, double lon) { 34 | this.lat = lat; 35 | this.lon = lon; 36 | } 37 | 38 | public void setLat(double lat) { 39 | this.lat = lat; 40 | } 41 | 42 | public void setLon(double lon) { 43 | this.lon = lon; 44 | } 45 | 46 | public final double getLat() { 47 | return this.lat; 48 | } 49 | 50 | public final double getLon() { 51 | return this.lon; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/domain/Marketing.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.demo.legacysearch.domain; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | 8 | /** 9 | * We define here marketing meta data: 10 | * Number of clicks on each segment 11 | */ 12 | @Entity 13 | public class Marketing { 14 | 15 | @Id 16 | @GeneratedValue(strategy= GenerationType.AUTO) 17 | private Integer id; 18 | 19 | private Integer cars; 20 | private Integer shoes; 21 | private Integer toys; 22 | private Integer fashion; 23 | private Integer music; 24 | private Integer garden; 25 | private Integer electronic; 26 | private Integer hifi; 27 | private Integer food; 28 | 29 | public Integer getId() { 30 | return id; 31 | } 32 | 33 | public void setId(Integer id) { 34 | this.id = id; 35 | } 36 | 37 | public Integer getCars() { 38 | return cars; 39 | } 40 | 41 | public void setCars(Integer cars) { 42 | this.cars = cars; 43 | } 44 | 45 | public Integer getShoes() { 46 | return shoes; 47 | } 48 | 49 | public void setShoes(Integer shoes) { 50 | this.shoes = shoes; 51 | } 52 | 53 | public Integer getToys() { 54 | return toys; 55 | } 56 | 57 | public void setToys(Integer toys) { 58 | this.toys = toys; 59 | } 60 | 61 | public Integer getFashion() { 62 | return fashion; 63 | } 64 | 65 | public void setFashion(Integer fashion) { 66 | this.fashion = fashion; 67 | } 68 | 69 | public Integer getMusic() { 70 | return music; 71 | } 72 | 73 | public void setMusic(Integer music) { 74 | this.music = music; 75 | } 76 | 77 | public Integer getGarden() { 78 | return garden; 79 | } 80 | 81 | public void setGarden(Integer garden) { 82 | this.garden = garden; 83 | } 84 | 85 | public Integer getElectronic() { 86 | return electronic; 87 | } 88 | 89 | public void setElectronic(Integer electronic) { 90 | this.electronic = electronic; 91 | } 92 | 93 | public Integer getHifi() { 94 | return hifi; 95 | } 96 | 97 | public void setHifi(Integer hifi) { 98 | this.hifi = hifi; 99 | } 100 | 101 | public Integer getFood() { 102 | return food; 103 | } 104 | 105 | public void setFood(Integer food) { 106 | this.food = food; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/domain/Person.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to David Pilato (the "Author") under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. Author licenses this 6 | * file to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.domain; 21 | 22 | import jakarta.persistence.CascadeType; 23 | import jakarta.persistence.Entity; 24 | import jakarta.persistence.GeneratedValue; 25 | import jakarta.persistence.GenerationType; 26 | import jakarta.persistence.Id; 27 | import jakarta.persistence.OneToOne; 28 | import java.time.LocalDate; 29 | 30 | @Entity 31 | public class Person { 32 | 33 | @Id 34 | @GeneratedValue(strategy= GenerationType.AUTO) 35 | private Integer id = null; 36 | 37 | private String name = null; 38 | private LocalDate dateOfBirth = null; 39 | private String gender = null; 40 | private Integer children; 41 | 42 | @OneToOne(cascade = CascadeType.ALL) 43 | private Marketing marketing; 44 | 45 | @OneToOne(cascade = CascadeType.ALL) 46 | private Address address; 47 | 48 | public Integer getId() { 49 | return id; 50 | } 51 | 52 | public void setId(Integer id) { 53 | this.id = id; 54 | } 55 | 56 | public String idAsString() { 57 | return id != null ? "" + id : null; 58 | } 59 | 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | public void setName(String name) { 65 | this.name = name; 66 | } 67 | 68 | public LocalDate getDateOfBirth() { 69 | return dateOfBirth; 70 | } 71 | 72 | public void setDateOfBirth(LocalDate dateOfBirth) { 73 | this.dateOfBirth = dateOfBirth; 74 | } 75 | 76 | public String getGender() { 77 | return gender; 78 | } 79 | 80 | public void setGender(String gender) { 81 | this.gender = gender; 82 | } 83 | 84 | public Marketing getMarketing() { 85 | return marketing; 86 | } 87 | 88 | public void setMarketing(Marketing marketing) { 89 | this.marketing = marketing; 90 | } 91 | 92 | public Address getAddress() { 93 | return address; 94 | } 95 | 96 | public void setAddress(Address address) { 97 | this.address = address; 98 | } 99 | 100 | public Integer getChildren() { 101 | return children; 102 | } 103 | 104 | public void setChildren(Integer children) { 105 | this.children = children; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/helper/CsvReader.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.demo.legacysearch.helper; 2 | 3 | 4 | import java.io.*; 5 | import java.util.ArrayList; 6 | 7 | class CsvReader { 8 | 9 | static ArrayList readAsStrings(String url) throws IOException { 10 | ArrayList data = new ArrayList<>(); 11 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(CsvReader.class.getResourceAsStream(url)))) { 12 | String nextLine = reader.readLine(); 13 | while (nextLine != null) { 14 | data.add(nextLine); 15 | nextLine = reader.readLine(); 16 | } 17 | } 18 | 19 | return data; 20 | } 21 | 22 | static ArrayList extractFromCommas(String dataLine) { 23 | //Gives back the data that is found between commas in a String 24 | ArrayList data = new ArrayList<>(); 25 | String theString = ""; 26 | for (int i = 0; i < dataLine.length(); i++) { //go down the whole string 27 | if (dataLine.charAt(i) == ',') { 28 | if (i != 0) { 29 | data.add(theString); //this means that the next comma has been reached 30 | theString = ""; //reset theString Variable 31 | } 32 | } else { 33 | theString = theString + dataLine.charAt(i); //otherwise, just keep piling the chars onto the cumulative string 34 | } 35 | } 36 | if (!theString.equalsIgnoreCase("")) //only if the last position is not occupied with nothing then add the end on 37 | { 38 | data.add(theString); 39 | } 40 | return data; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/helper/PersonGenerator.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.demo.legacysearch.helper; 2 | 3 | 4 | import fr.pilato.demo.legacysearch.domain.Address; 5 | import fr.pilato.demo.legacysearch.domain.GeoPoint; 6 | import fr.pilato.demo.legacysearch.domain.Marketing; 7 | import fr.pilato.demo.legacysearch.domain.Person; 8 | 9 | import java.io.IOException; 10 | import java.time.LocalDate; 11 | import java.util.ArrayList; 12 | import java.util.Random; 13 | 14 | public class PersonGenerator { 15 | 16 | private static ArrayList names; 17 | 18 | static { 19 | try { 20 | PersonGenerator.names = CsvReader.readAsStrings("/prenoms.csv"); 21 | } catch (IOException e) { 22 | System.err.println("Can not generate names from CSV"); 23 | } 24 | } 25 | 26 | public static Person personGenerator() throws IOException { 27 | Person person = new Person(); 28 | buildGender(person); 29 | person.setDateOfBirth(buildBirthDate()); 30 | person.setMarketing(buildMeta()); 31 | person.setAddress(buildAddress()); 32 | person.setChildren(buildChildren()); 33 | 34 | return person; 35 | } 36 | 37 | private static Marketing buildMeta() { 38 | Marketing marketing = new Marketing(); 39 | int nbMeta = numberGenerator(1, 5); 40 | 41 | for (int i = 0; i < nbMeta; i++) { 42 | int nbConsult = numberGenerator(30, 2000); 43 | int typeMeta = numberGenerator(0, 9); 44 | switch (typeMeta) { 45 | case 0: 46 | marketing.setShoes(nbConsult); 47 | break; 48 | case 1: 49 | marketing.setToys(nbConsult); 50 | break; 51 | case 2: 52 | marketing.setFashion(nbConsult); 53 | break; 54 | case 3: 55 | marketing.setMusic(nbConsult); 56 | break; 57 | case 4: 58 | marketing.setGarden(nbConsult); 59 | break; 60 | case 5: 61 | marketing.setElectronic(nbConsult); 62 | break; 63 | case 6: 64 | marketing.setHifi(nbConsult); 65 | break; 66 | case 7: 67 | marketing.setCars(nbConsult); 68 | break; 69 | case 8: 70 | marketing.setFood(nbConsult); 71 | break; 72 | default: 73 | System.err.println(" ->" + typeMeta); 74 | break; 75 | } 76 | 77 | 78 | } 79 | 80 | return marketing; 81 | 82 | } 83 | 84 | private static LocalDate buildBirthDate() { 85 | int year = numberGenerator(1940, 70); 86 | int month = numberGenerator(1, 12); 87 | int day = numberGenerator(1, 28); 88 | return LocalDate.of(year, month, day); 89 | } 90 | 91 | private static void buildGender(Person person) throws IOException { 92 | int pos = numberGenerator(0, names.size()); 93 | 94 | String line = names.get(pos); 95 | ArrayList temp = CsvReader.extractFromCommas(line); 96 | person.setName(temp.get(0) + " " + CsvReader.extractFromCommas( 97 | names.get(numberGenerator(0, names.size()))).get(0)); 98 | person.setGender(temp.get(1)); 99 | } 100 | 101 | private static Address buildAddress() throws IOException { 102 | Address address = new Address(); 103 | generateCountry(address); 104 | Long result = Math.round(Math.random() * 2); 105 | 106 | if ("FR".equals(address.getCountrycode())) { 107 | switch (result.intValue()) { 108 | case 0: 109 | address.setCity("Paris"); 110 | address.setZipcode("75000"); 111 | address.setLocation(new GeoPoint(doubleGenerator(48.819918, 48.900552), doubleGenerator(2.25929, 2.4158559))); 112 | break; 113 | case 1: 114 | address.setCity("Nantes"); 115 | address.setZipcode("44000"); 116 | address.setLocation(new GeoPoint(doubleGenerator(47.157742, 47.270729), doubleGenerator(-1.623467, -1.471032))); 117 | break; 118 | case 2: 119 | address.setCity("Cergy"); 120 | address.setZipcode("95000"); 121 | address.setLocation(new GeoPoint(doubleGenerator(49.019583, 49.059419), doubleGenerator(2.003001, 2.090892))); 122 | break; 123 | default: 124 | System.err.println("buildAddress ->" + result.intValue()); 125 | break; 126 | } 127 | } 128 | 129 | if ("GB".equals(address.getCountrycode())) { 130 | switch (result.intValue()) { 131 | case 0: 132 | address.setCity("London"); 133 | address.setZipcode("98888"); 134 | address.setLocation(new GeoPoint(doubleGenerator(51.444014, 51.607633), doubleGenerator(-0.294245, 0.064184))); 135 | break; 136 | case 1: 137 | address.setCity("Plymouth"); 138 | address.setZipcode("5226"); 139 | address.setLocation(new GeoPoint(doubleGenerator(50.345272, 50.434797), doubleGenerator(-4.190161, -4.034636))); 140 | break; 141 | case 2: 142 | address.setCity("Liverpool"); 143 | address.setZipcode("86767"); 144 | address.setLocation(new GeoPoint(doubleGenerator(53.345346, 53.496339), doubleGenerator(-3.047485, -2.564774))); 145 | break; 146 | default: 147 | System.err.println("buildAddress ->" + result.intValue()); 148 | break; 149 | } 150 | } 151 | 152 | if ("DE".equals(address.getCountrycode())) { 153 | switch (result.intValue()) { 154 | case 0: 155 | address.setCity("Berlin"); 156 | address.setZipcode("9998"); 157 | address.setLocation(new GeoPoint(doubleGenerator(52.364796, 52.639827), doubleGenerator(13.115778, 13.769465))); 158 | break; 159 | case 1: 160 | address.setCity("Bonn"); 161 | address.setZipcode("0099"); 162 | address.setLocation(new GeoPoint(doubleGenerator(50.649948, 50.766049), doubleGenerator(7.025075, 7.214589))); 163 | break; 164 | case 2: 165 | address.setCity("Munich"); 166 | address.setZipcode("45445"); 167 | address.setLocation(new GeoPoint(doubleGenerator(48.081337, 48.238441), doubleGenerator(11.371548, 11.711437))); 168 | break; 169 | default: 170 | System.err.println("buildAddress ->" + result.intValue()); 171 | break; 172 | } 173 | } 174 | 175 | if ("IT".equals(address.getCountrycode())) { 176 | switch (result.intValue()) { 177 | case 0: 178 | address.setCity("Rome"); 179 | address.setZipcode("00100"); 180 | address.setLocation(new GeoPoint(doubleGenerator(41.797211, 41.980805), doubleGenerator(12.373950, 12.601393))); 181 | break; 182 | case 1: 183 | address.setCity("Turin"); 184 | address.setZipcode("10100"); 185 | address.setLocation(new GeoPoint(doubleGenerator(45.007912, 45.122125), doubleGenerator(7.593528, 7.747337))); 186 | break; 187 | case 2: 188 | address.setCity("Ischia"); 189 | address.setZipcode("80100"); 190 | address.setLocation(new GeoPoint(doubleGenerator(40.704982, 40.758477), doubleGenerator(13.859360, 13.953002))); 191 | break; 192 | default: 193 | System.err.println("buildAddress ->" + result.intValue()); 194 | break; 195 | } 196 | } 197 | 198 | return address; 199 | } 200 | 201 | private static String generateCountry(Address address) { 202 | 203 | int result = numberGenerator(0,4); 204 | 205 | switch (result) { 206 | case 0: 207 | address.setCountry("France"); 208 | address.setCountrycode("FR"); 209 | break; 210 | case 1: 211 | address.setCountry("Germany"); 212 | address.setCountrycode("DE"); 213 | break; 214 | case 2: 215 | address.setCountry("England"); 216 | address.setCountrycode("GB"); 217 | break; 218 | case 3: 219 | address.setCountry("Italy"); 220 | address.setCountrycode("IT"); 221 | break; 222 | default: 223 | System.err.println("generateCountry ->" + result); 224 | break; 225 | } 226 | 227 | return null; 228 | } 229 | 230 | private static Integer buildChildren() { 231 | return numberGenerator(0,5); 232 | } 233 | 234 | private static int numberGenerator(int min, int range) { 235 | return (int) Math.floor(Math.random()*range+min); 236 | } 237 | 238 | private static double doubleGenerator(double min, double max) { 239 | return min + (max - min) * new Random().nextDouble(); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/helper/SSLUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.helper; 21 | 22 | import javax.net.ssl.SSLContext; 23 | import javax.net.ssl.TrustManager; 24 | import javax.net.ssl.X509TrustManager; 25 | import java.security.KeyManagementException; 26 | import java.security.NoSuchAlgorithmException; 27 | import java.security.SecureRandom; 28 | import java.security.cert.X509Certificate; 29 | 30 | /** 31 | * Some utilities for SSL 32 | */ 33 | public class SSLUtils { 34 | private static final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { 35 | @Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} 36 | @Override public void checkServerTrusted(X509Certificate[] chain, String authType) {} 37 | @Override public X509Certificate[] getAcceptedIssuers() { return null; } 38 | }}; 39 | 40 | public static SSLContext createTrustAllCertsContext() { 41 | try { 42 | SSLContext sslContext = SSLContext.getInstance("SSL"); 43 | sslContext.init(null, trustAllCerts, new SecureRandom()); 44 | return sslContext; 45 | } catch (NoSuchAlgorithmException | KeyManagementException e) { 46 | throw new RuntimeException("Can not create the SSLContext", e); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/helper/Strings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.helper; 21 | 22 | public class Strings { 23 | 24 | /** 25 | * Check that the given CharSequence is neither null nor of length 0. 26 | * Note: Will return true for a CharSequence that purely consists of whitespace. 27 | *
 28 |      * StringUtils.hasLength(null) = false
 29 |      * StringUtils.hasLength("") = false
 30 |      * StringUtils.hasLength(" ") = true
 31 |      * StringUtils.hasLength("Hello") = true
 32 |      * 
33 | * 34 | * @param str the CharSequence to check (may be null) 35 | * @return true if the CharSequence is not null and has length 36 | * @see #hasText(String) 37 | */ 38 | public static boolean hasLength(CharSequence str) { 39 | return (str != null && str.length() > 0); 40 | } 41 | 42 | 43 | /** 44 | * Check that the given CharSequence is either null or of length 0. 45 | * Note: Will return false for a CharSequence that purely consists of whitespace. 46 | *
 47 |      * StringUtils.isEmpty(null) = true
 48 |      * StringUtils.isEmpty("") = true
 49 |      * StringUtils.isEmpty(" ") = false
 50 |      * StringUtils.isEmpty("Hello") = false
 51 |      * 
52 | * 53 | * @param str the CharSequence to check (may be null) 54 | * @return true if the CharSequence is either null or has a zero length 55 | */ 56 | public static boolean isEmpty(CharSequence str) { 57 | return !hasLength(str); 58 | } 59 | 60 | 61 | /** 62 | * Check whether the given CharSequence has actual text. 63 | * More specifically, returns true if the string not null, 64 | * its length is greater than 0, and it contains at least one non-whitespace character. 65 | *
 66 |      * StringUtils.hasText(null) = false
 67 |      * StringUtils.hasText("") = false
 68 |      * StringUtils.hasText(" ") = false
 69 |      * StringUtils.hasText("12345") = true
 70 |      * StringUtils.hasText(" 12345 ") = true
 71 |      * 
72 | * 73 | * @param str the CharSequence to check (may be null) 74 | * @return true if the CharSequence is not null, 75 | * its length is greater than 0, and it does not contain whitespace only 76 | * @see Character#isWhitespace 77 | */ 78 | public static boolean hasText(CharSequence str) { 79 | if (!hasLength(str)) { 80 | return false; 81 | } 82 | int strLen = str.length(); 83 | for (int i = 0; i < strLen; i++) { 84 | if (!Character.isWhitespace(str.charAt(i))) { 85 | return true; 86 | } 87 | } 88 | return false; 89 | } 90 | 91 | /** 92 | * Check whether the given String has actual text. 93 | * More specifically, returns true if the string not null, 94 | * its length is greater than 0, and it contains at least one non-whitespace character. 95 | * 96 | * @param str the String to check (may be null) 97 | * @return true if the String is not null, its length is 98 | * greater than 0, and it does not contain whitespace only 99 | * @see #hasText(CharSequence) 100 | */ 101 | public static boolean hasText(String str) { 102 | return hasText((CharSequence) str); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/service/PersonService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package fr.pilato.demo.legacysearch.service; 20 | 21 | import com.fasterxml.jackson.annotation.JsonIgnore; 22 | import com.fasterxml.jackson.core.JsonProcessingException; 23 | import com.fasterxml.jackson.databind.ObjectMapper; 24 | import com.github.dozermapper.core.Mapper; 25 | import fr.pilato.demo.legacysearch.dao.PersonRepository; 26 | import fr.pilato.demo.legacysearch.domain.Address; 27 | import fr.pilato.demo.legacysearch.domain.GeoPoint; 28 | import fr.pilato.demo.legacysearch.domain.Person; 29 | import fr.pilato.demo.legacysearch.helper.PersonGenerator; 30 | import fr.pilato.demo.legacysearch.helper.Strings; 31 | import fr.pilato.demo.legacysearch.webapp.InitResult; 32 | import fr.pilato.demo.legacysearch.webapp.PersonNotFoundException; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | import org.springframework.beans.factory.annotation.Value; 36 | import org.springframework.data.domain.Example; 37 | import org.springframework.data.domain.ExampleMatcher; 38 | import org.springframework.data.domain.Page; 39 | import org.springframework.data.domain.PageRequest; 40 | import org.springframework.stereotype.Service; 41 | 42 | import java.io.IOException; 43 | import java.util.ArrayList; 44 | import java.util.Collection; 45 | import java.util.Collections; 46 | import java.util.concurrent.atomic.AtomicInteger; 47 | 48 | import static org.springframework.data.domain.ExampleMatcher.GenericPropertyMatchers.contains; 49 | 50 | @Service 51 | public class PersonService { 52 | private final Logger logger = LoggerFactory.getLogger(PersonService.class); 53 | 54 | @Value("${app.batch.size:100}") 55 | private int batchSize; 56 | 57 | private final PersonRepository personRepository; 58 | private final ObjectMapper mapper; 59 | private final Mapper dozerBeanMapper; 60 | 61 | public PersonService(PersonRepository personRepository, ObjectMapper mapper, 62 | Mapper dozerBeanMapper) { 63 | this.personRepository = personRepository; 64 | this.mapper = mapper; 65 | this.dozerBeanMapper = dozerBeanMapper; 66 | } 67 | 68 | public Person get(Integer id) { 69 | Person person = personRepository.findById(id).orElseThrow(PersonNotFoundException::new); 70 | logger.debug("get({})={}", id, person); 71 | return person; 72 | } 73 | 74 | private Iterable saveAll(Collection persons) { 75 | Iterable personsDb = personRepository.saveAll(persons); 76 | 77 | logger.debug("Saved [{}] persons", persons.size()); 78 | return personsDb; 79 | } 80 | 81 | public Person upsert(Integer id, Person person) { 82 | // We try to find an existing document 83 | try { 84 | Person personDb = get(id); 85 | dozerBeanMapper.map(person, personDb); 86 | person = personDb; 87 | person.setId(id); 88 | } catch (PersonNotFoundException ignored) { } 89 | return saveAll(Collections.singleton(person)).iterator().next(); 90 | } 91 | 92 | public void delete(Integer id) { 93 | logger.debug("Person: {}", id); 94 | 95 | if (id != null) { 96 | personRepository.deleteById(id); 97 | } 98 | 99 | logger.debug("Person deleted: {}", id); 100 | } 101 | 102 | public String search(String q, String f_country, String f_date, Integer from, Integer size) throws IOException { 103 | long start = System.nanoTime(); 104 | 105 | Page page; 106 | 107 | if (Strings.isEmpty(q)) { 108 | page = personRepository.findAll(PageRequest.of(from / size, size)); 109 | } else { 110 | page = personRepository.findLikeGoogle(q, PageRequest.of(from / size, size)); 111 | } 112 | 113 | long total = page.getTotalElements(); 114 | Collection personsFound = page.getContent(); 115 | long took = (System.nanoTime() - start) / 1_000_000; 116 | 117 | RestSearchResponse response = buildResponse(personsFound, total, took); 118 | 119 | logger.debug("search({})={} persons", q, response.getHits().getTotalHits()); 120 | 121 | String json = null; 122 | try { 123 | json = mapper.writeValueAsString(response); 124 | } catch (JsonProcessingException e) { 125 | logger.error("can not serialize to json", e); 126 | } 127 | 128 | return json; 129 | } 130 | 131 | public String advancedSearch(String name, String country, String city, Integer from, Integer size) throws IOException { 132 | Person person = new Person(); 133 | if (name != null) { 134 | person.setName(name); 135 | } 136 | 137 | if (country != null || city != null) { 138 | Address address = new Address(); 139 | if (country != null) { 140 | address.setCountry(country); 141 | } 142 | if (city != null) { 143 | address.setCity(city); 144 | } 145 | person.setAddress(address); 146 | } 147 | 148 | ExampleMatcher matcher = ExampleMatcher.matching() 149 | .withMatcher("name", contains().ignoreCase()) 150 | .withMatcher("address.country", contains().ignoreCase()) 151 | .withMatcher("address.city", contains().ignoreCase()); 152 | 153 | Example example = Example.of(person, matcher); 154 | 155 | long start = System.nanoTime(); 156 | 157 | Page page = personRepository.findAll(example, PageRequest.of(from / size, size)); 158 | 159 | long total = page.getTotalElements(); 160 | Collection personsFound = page.getContent(); 161 | long took = (System.nanoTime() - start) / 1_000_000; 162 | 163 | RestSearchResponse response = buildResponse(personsFound, total, took); 164 | 165 | logger.debug("advancedSearch({},{},{})={} persons", name, country, city, response.getHits().getTotalHits()); 166 | 167 | String json = null; 168 | try { 169 | json = mapper.writeValueAsString(response); 170 | } catch (JsonProcessingException e) { 171 | logger.error("can not serialize to json", e); 172 | } 173 | 174 | return json; 175 | } 176 | 177 | private final AtomicInteger currentItem = new AtomicInteger(); 178 | private long start = 0; 179 | 180 | public InitResult init(Integer size) throws IOException { 181 | currentItem.set(0); 182 | 183 | logger.debug("Initializing database for {} persons", size); 184 | start = System.nanoTime(); 185 | 186 | Collection persons = new ArrayList<>(); 187 | 188 | Person joe = PersonGenerator.personGenerator(); 189 | joe.setName("Joe Smith"); 190 | joe.getAddress().setCountry("France"); 191 | joe.getAddress().setCity("Paris"); 192 | joe.getAddress().setCountrycode("FR"); 193 | joe.getAddress().setLocation(new GeoPoint(48.84, 2.31)); 194 | 195 | persons.add(joe); 196 | currentItem.incrementAndGet(); 197 | 198 | Person franceGall = PersonGenerator.personGenerator(); 199 | franceGall.setName("France Gall"); 200 | franceGall.setGender("female"); 201 | franceGall.getAddress().setCountry("Italy"); 202 | franceGall.getAddress().setCity("Ischia"); 203 | franceGall.getAddress().setCountrycode("IT"); 204 | franceGall.getAddress().setLocation(new GeoPoint(40.72, 13.90)); 205 | 206 | persons.add(franceGall); 207 | currentItem.incrementAndGet(); 208 | 209 | // We generate numPersons persons and every batchSize, we send them to the DB 210 | for (int i = 2; i < size; i++) { 211 | Person person = PersonGenerator.personGenerator(); 212 | persons.add(person); 213 | currentItem.incrementAndGet(); 214 | if (i % batchSize == 0) { 215 | saveAll(persons); 216 | } 217 | } 218 | 219 | // Save all remaining persons 220 | saveAll(persons); 221 | 222 | long took = (System.nanoTime() - start) / 1_000_000; 223 | 224 | logger.debug("Database initialized with {} persons. Took: {} ms, around {} per second.", 225 | size, took, 1000L * size / took); 226 | 227 | return new InitResult(took, 1000L * size / took, size); 228 | } 229 | 230 | public InitResult getInitCurrentAchievement() { 231 | int current = currentItem.get(); 232 | long took = (System.nanoTime() - start) / 1_000_000; 233 | return new InitResult(took, 1000L * current / took, current); 234 | } 235 | 236 | private RestSearchResponse buildResponse(Collection persons, long total, long took) { 237 | RestSearchResponse response = new RestSearchResponse<>(); 238 | response.setTook(took); 239 | 240 | RestSearchHits hits = new RestSearchHits<>(); 241 | RestSearchHit[] hitsItems = new RestSearchHit[persons.size()]; 242 | 243 | int i =0; 244 | for (Person person : persons) { 245 | RestSearchHit hit = new RestSearchHit(); 246 | hit.set_source(person); 247 | hit.set_id(person.idAsString()); 248 | hitsItems[i++] = hit; 249 | } 250 | hits.setHits(hitsItems).setTotal(total); 251 | response.setHits(hits); 252 | 253 | return response; 254 | } 255 | 256 | public static class RestSearchTotal { 257 | private long value; 258 | 259 | public RestSearchTotal(long value) { 260 | this.value = value; 261 | } 262 | 263 | public void setValue(long value) { 264 | this.value = value; 265 | } 266 | 267 | public long getValue() { 268 | return value; 269 | } 270 | } 271 | 272 | public static class RestSearchHits { 273 | private RestSearchHit[] hits; 274 | private RestSearchTotal total; 275 | 276 | public RestSearchTotal getTotal() { 277 | return total; 278 | } 279 | 280 | // Just for elasticsearch compatibility purpose 281 | @JsonIgnore 282 | public long getTotalHits() { 283 | return total.value; 284 | } 285 | 286 | public RestSearchHits setTotal(long total) { 287 | this.total = new RestSearchTotal(total); 288 | return this; 289 | } 290 | 291 | public RestSearchHit[] getHits() { 292 | return hits; 293 | } 294 | 295 | public RestSearchHits setHits(RestSearchHit[] hits) { 296 | this.hits = hits; 297 | return this; 298 | } 299 | } 300 | 301 | public static class RestSearchHit { 302 | private T _source; 303 | private String _id; 304 | 305 | public void set_source(T _source) { 306 | this._source = _source; 307 | } 308 | 309 | public T get_source() { 310 | return _source; 311 | } 312 | 313 | public void set_id(String _id) { 314 | this._id = _id; 315 | } 316 | 317 | public String get_id() { 318 | return _id; 319 | } 320 | } 321 | 322 | public static class RestSearchResponse { 323 | private long took; 324 | private RestSearchHits hits; 325 | 326 | public RestSearchResponse() { 327 | } 328 | 329 | public RestSearchHits getHits() { 330 | return hits; 331 | } 332 | 333 | public void setHits(RestSearchHits hits) { 334 | this.hits = hits; 335 | } 336 | 337 | public long getTook() { 338 | return took; 339 | } 340 | 341 | public RestSearchResponse setTook(long took) { 342 | this.took = took; 343 | return this; 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/webapp/InitResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.webapp; 21 | 22 | public class InitResult { 23 | 24 | private long took; 25 | private long rate; 26 | private int current; 27 | 28 | public long getRate() { 29 | return rate; 30 | } 31 | 32 | public long getTook() { 33 | return took; 34 | } 35 | 36 | public int getCurrent() { 37 | return current; 38 | } 39 | 40 | public InitResult(long took, long rate, int current) { 41 | this.took = took; 42 | this.rate = rate; 43 | this.current = current; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/webapp/PersonController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package fr.pilato.demo.legacysearch.webapp; 20 | 21 | import fr.pilato.demo.legacysearch.domain.Person; 22 | import fr.pilato.demo.legacysearch.service.PersonService; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | import org.springframework.web.bind.annotation.DeleteMapping; 26 | import org.springframework.web.bind.annotation.GetMapping; 27 | import org.springframework.web.bind.annotation.PathVariable; 28 | import org.springframework.web.bind.annotation.PutMapping; 29 | import org.springframework.web.bind.annotation.RequestBody; 30 | import org.springframework.web.bind.annotation.RequestParam; 31 | import org.springframework.web.bind.annotation.RestController; 32 | 33 | import java.io.IOException; 34 | 35 | @RestController 36 | public class PersonController { 37 | private final Logger logger = LoggerFactory.getLogger(PersonController.class); 38 | 39 | private final PersonService personService; 40 | 41 | public PersonController(PersonService personService) { 42 | this.personService = personService; 43 | } 44 | 45 | @GetMapping("/api/1/person/{id}") 46 | public Person get(@PathVariable Integer id) { 47 | return personService.get(id); 48 | } 49 | 50 | /** 51 | * Create or update an entity 52 | */ 53 | @PutMapping("/api/1/person/{id}") 54 | public Person upsert(@PathVariable Integer id, @RequestBody Person person) throws IOException { 55 | logger.debug("upsert({}, {})", id, person); 56 | Person upsert = personService.upsert(id, person); 57 | logger.debug("created/updated {}: {}", id, upsert); 58 | return upsert; 59 | } 60 | 61 | @DeleteMapping("/api/1/person/{id}") 62 | public void delete(@PathVariable Integer id) throws IOException { 63 | personService.delete(id); 64 | } 65 | 66 | @GetMapping("/api/1/person/_search") 67 | public String search(@RequestParam(required = false) String q, @RequestParam(required = false) String f_country, 68 | @RequestParam(required = false) String f_date, @RequestParam(required = false, defaultValue = "0") Integer from, 69 | @RequestParam(required = false, defaultValue = "10") Integer size) throws IOException { 70 | return personService.search(q, f_country, f_date, from, size); 71 | } 72 | 73 | @GetMapping("/api/1/person/_advanced_search") 74 | public String advancedSearch(@RequestParam(required = false) String name, @RequestParam(required = false) String country, 75 | @RequestParam(required = false) String city, 76 | @RequestParam(required = false, defaultValue = "0") Integer from, 77 | @RequestParam(required = false, defaultValue = "10") Integer size) throws IOException { 78 | return personService.advancedSearch(name, country, city, from, size); 79 | } 80 | 81 | @GetMapping("/api/1/person/_init") 82 | public InitResult init(@RequestParam(required = false, defaultValue = "1000") Integer size) throws IOException { 83 | return personService.init(size); 84 | } 85 | 86 | @GetMapping("/api/1/person/_init_status") 87 | public InitResult initStatus() { 88 | return personService.getInitCurrentAchievement(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/demo/legacysearch/webapp/PersonNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package fr.pilato.demo.legacysearch.webapp; 21 | 22 | import org.springframework.http.HttpStatus; 23 | import org.springframework.web.bind.annotation.ResponseStatus; 24 | 25 | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "person not found") 26 | public class PersonNotFoundException extends RuntimeException { 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # Give the project a nice name 2 | spring.application.name: person 3 | 4 | # Enable all the actuator endpoints for HTTP (keep them under the base path) and JMX 5 | management.endpoints: 6 | web: 7 | base-path: / 8 | exposure.include: "*" 9 | 10 | spring.jpa.hibernate.ddl-auto: create 11 | spring.datasource.url: jdbc:mysql://localhost:3306/person?serverTimezone=UTC 12 | spring.datasource.username: root 13 | spring.datasource.password: password 14 | 15 | management.health.elasticsearch.enabled: false 16 | -------------------------------------------------------------------------------- /src/main/resources/dozer.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dadoonet/legacy-search/7b7b3035df531248bfb2c93ccbb3aacd5e3188ad/src/main/resources/dozer.properties -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | 15 | ${CONSOLE_LOG_PATTERN} 16 | utf8 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/prenoms.csv: -------------------------------------------------------------------------------- 1 | Gabriel,male 2 | Arthur,male 3 | Louise,female 4 | Raphael,male 5 | Adam,male 6 | Chloe,female 7 | Paul,male 8 | Alexandre,male 9 | Louis,male 10 | Emma,female 11 | Antoine,male 12 | Maxime,male 13 | Alice,female 14 | Ines,female 15 | Sarah,female 16 | Jeanne,female 17 | Lucas,male 18 | Victor,male 19 | Mohamed,male 20 | Camille,female 21 | Juliette,female 22 | Lea,female 23 | Nathan,male 24 | Thomas,male 25 | Sacha,male 26 | Jules,male 27 | Eva,female 28 | Lina,female 29 | Hugo,male 30 | Manon,female 31 | Zoe,female 32 | Adrien,male 33 | Nina,female 34 | Jade,female 35 | Noah,male 36 | Anna,female 37 | Clement,male 38 | Gaspard,male 39 | Joseph,male 40 | Augustin,male 41 | Charlotte,female 42 | Anais,female 43 | Rayan,male 44 | Ethan,male 45 | Oscar,male 46 | Martin,male 47 | Samuel,male 48 | Yanis,male 49 | Baptiste,male 50 | Mathilde,female 51 | Romane,female 52 | Leo,male 53 | Clemence,female 54 | Enzo,male 55 | Lucie,female 56 | Rose,female 57 | Gabrielle,female 58 | Lou,female 59 | Simon,male 60 | Noe,male 61 | Clara,female 62 | Adele,female 63 | Sofia,female 64 | Maxence,male 65 | Marie,female 66 | Axel,male 67 | Lisa,female 68 | Victoire,female 69 | Josephine,female 70 | Theo,male 71 | Valentin,male 72 | Alexis,male 73 | Leonie,female 74 | Heloise,female 75 | Julia,female 76 | Ava,female 77 | Lola,female 78 | Liam,male 79 | Margaux,female 80 | Maya,female 81 | Quentin,male 82 | Valentine,female 83 | Charles,male 84 | Come,male 85 | Agathe,female 86 | Pierre,male 87 | Yasmine,female 88 | Aaron,male 89 | Alix,female 90 | Elisa,female 91 | Diane,female 92 | Mathis,male 93 | Mila,female 94 | Victoria,female 95 | Jean,male 96 | Apolline,female 97 | Benjamin,male 98 | Noemie,female 99 | Elise,female 100 | Gabin,male 101 | Camille,male 102 | Ismael,male 103 | Lena,female 104 | Olivia,female 105 | Pauline,female 106 | Sara,female 107 | Timothee,male 108 | Elsa,female 109 | Margot,female 110 | David,male 111 | Leon,male 112 | Capucine,female 113 | Tom,male 114 | Alicia,female 115 | Elias,male 116 | Salome,female 117 | Eliott,male 118 | Lucien,male 119 | Mael,male 120 | Romain,male 121 | Isaac,male 122 | Noam,male 123 | Emilie,female 124 | Robin,male 125 | Youssef,male 126 | Aya,female 127 | Ibrahim,male 128 | Nael,male 129 | Felix,male 130 | Laura,female 131 | Nour,female 132 | Tristan,male 133 | Iris,female 134 | Matthieu,male 135 | Ruben,male 136 | Joshua,male 137 | Mariam,female 138 | Esther,female 139 | Fatoumata,female 140 | Lily,female 141 | Romy,female 142 | Violette,female 143 | William,male 144 | Garance,female 145 | Julie,female 146 | Nicolas,male 147 | Ulysse,male 148 | Octave,male 149 | Constance,female 150 | Eleonore,female 151 | Amine,male 152 | Leonard,male 153 | Luna,female 154 | Suzanne,female 155 | Daniel,male 156 | Daphne,female 157 | Julien,male 158 | Stella,female 159 | Ambre,female 160 | Evan,male 161 | Malo,male 162 | Marius,male 163 | Nolan,male 164 | Roman,male 165 | Samy,male 166 | Faustine,female 167 | Gustave,male 168 | Mehdi,male 169 | Sophie,female 170 | Yacine,male 171 | Eden,female 172 | Gregoire,male 173 | Sasha,female 174 | Antonin,male 175 | Aurelien,male 176 | Ayoub,male 177 | Eloise,female 178 | Solal,male 179 | Achille,male 180 | Ines,female 181 | Oceane,female 182 | Sophia,female 183 | Anatole,male 184 | Edouard,male 185 | Elie,male 186 | Lila,female 187 | Mathias,male 188 | Rafael,male 189 | Emmanuel,male 190 | Justine,female 191 | Lenny,male 192 | Amir,male 193 | Hadrien,male 194 | Lena,female 195 | Lyna,female 196 | Milo,male 197 | Noham,male 198 | Theodore,male 199 | Aminata,female 200 | Armand,male 201 | Celeste,female 202 | Edgar,male 203 | Mathieu,male 204 | Noa,female 205 | Rayane,male 206 | Thibault,male 207 | Auguste,male 208 | Madeleine,female 209 | Matheo,male 210 | Ahmed,male 211 | Aicha,female 212 | Anis,male 213 | Assia,female 214 | Basile,male 215 | Marion,female 216 | Vadim,male 217 | Ali,male 218 | Esteban,male 219 | Manel,female 220 | Melissa,female 221 | Myriam,female 222 | Sixtine,female 223 | Vincent,male 224 | Lise,female 225 | Roxane,female 226 | Wassim,male 227 | Yassine,male 228 | Clementine,female 229 | Inaya,female 230 | Melina,female 231 | Mia,female 232 | Moussa,male 233 | Abel,male 234 | Anouk,female 235 | Cesar,male 236 | Emile,male 237 | Kenza,female 238 | Lilia,female 239 | Luca,male 240 | Maelys,female 241 | Celia,female 242 | Charlie,female 243 | Chiara,female 244 | Ella,female 245 | Eugenie,female 246 | Guillaume,male 247 | Leopold,male 248 | Theophile,male 249 | Titouan,male 250 | Arsene,male 251 | Diego,male 252 | Hector,male 253 | Ilyes,male 254 | Issa,male 255 | Joachim,male 256 | Lilou,female 257 | Max,male 258 | Paloma,female 259 | Zakaria,male 260 | Corentin,male 261 | Estelle,female 262 | Hanna,female 263 | Imane,female 264 | Jasmine,female 265 | Noa,male 266 | Sofiane,male 267 | Stanislas,male 268 | Zelie,female 269 | Alex,male 270 | Amaury,male 271 | Amelie,female 272 | Fatima,female 273 | Florian,male 274 | Lorenzo,male 275 | Louna,female 276 | Mamadou,male 277 | Marguerite,female 278 | Nathanael,male 279 | Adem,male 280 | Albane,female 281 | Alma,female 282 | Blanche,female 283 | Erwan,male 284 | Eve,female 285 | Marin,male 286 | Sami,male 287 | Younes,male 288 | Amina,female 289 | Berenice,female 290 | Hortense,female 291 | Isaure,female 292 | Jacques,male 293 | Maria,female 294 | Maryam,female 295 | Nassim,male 296 | Salma,female 297 | Selma,female 298 | Thais,female 299 | Hippolyte,male 300 | Livia,female 301 | Marc,male 302 | Naomi,female 303 | Ninon,female 304 | Alban,male 305 | Alexandra,female 306 | Awa,female 307 | Dina,female 308 | Djibril,male 309 | Hawa,female 310 | Ibrahima,male 311 | Ilian,male 312 | Kais,male 313 | Kylian,male 314 | Maelle,female 315 | Maissa,female 316 | Maximilien,male 317 | Nino,male 318 | Tess,female 319 | Amira,female 320 | Anaelle,female 321 | Dylan,male 322 | Elisabeth,female 323 | Elliot,male 324 | Leane,female 325 | Loic,male 326 | Marine,female 327 | Penelope,female 328 | Tiago,male 329 | Timeo,male 330 | Timothe,male 331 | Virgile,male 332 | Yann,male 333 | Alexia,female 334 | Anas,male 335 | Angele,female 336 | Anthony,male 337 | Bintou,female 338 | Charline,female 339 | Claire,female 340 | Colombe,female 341 | Farah,female 342 | Luc,male 343 | Matteo,male 344 | Milan,male 345 | Nora,female 346 | Oumou,female 347 | Ousmane,male 348 | Pablo,male 349 | Rachel,female 350 | Souleymane,male 351 | Andrea,male 352 | Celine,female 353 | Coline,female 354 | Dimitri,male 355 | Elena,female 356 | Eliot,male 357 | Giulia,female 358 | Hannah,female 359 | Henri,male 360 | Khadija,female 361 | Leandre,male 362 | Lucile,female 363 | Marcus,male 364 | Matthias,male 365 | Romeo,male 366 | Sirine,female 367 | Ana,female 368 | Aurore,female 369 | Axelle,female 370 | Charlie,male 371 | Kevin,male 372 | Lassana,male 373 | Marwa,female 374 | Mathys,male 375 | Mohammed,male 376 | Nahel,male 377 | Rebecca,female 378 | Aymen,male 379 | Brune,female 380 | Cleo,female 381 | Dorian,male 382 | Elina,female 383 | Emy,female 384 | Ethel,female 385 | Fanta,female 386 | Francois,male 387 | Gael,male 388 | Helene,female 389 | Jonas,male 390 | Louane,female 391 | Maia,female 392 | Marceau,male 393 | Matteo,male 394 | Morgane,female 395 | Rafael,male 396 | Zacharie,male 397 | Abdoulaye,male 398 | Amandine,female 399 | Aurele,male 400 | Bianca,female 401 | Eden,male 402 | Gaston,male 403 | Hamza,male 404 | Hana,female 405 | Kenzo,male 406 | Mina,female 407 | Nael,male 408 | Nils,male 409 | Olivier,male 410 | Raphael,male 411 | Raphaelle,female 412 | Sasha,male 413 | Syrine,female 414 | Alienor,female 415 | Alya,female 416 | Amadou,male 417 | Ambroise,male 418 | Ariane,female 419 | Camelia,female 420 | Cassandre,female 421 | Clotilde,female 422 | Emmy,female 423 | Eric,male 424 | Etienne,male 425 | Ilyas,male 426 | Ismail,male 427 | Judith,female 428 | Justin,male 429 | Karim,male 430 | Lana,female 431 | Lara,female 432 | Lison,female 433 | Louisa,female 434 | Neil,male 435 | Omar,male 436 | Philippine,female 437 | Remi,male 438 | Ryan,male 439 | Solene,female 440 | Aliya,female 441 | Andre,male 442 | Anton,male 443 | Astrid,female 444 | Aymeric,male 445 | Damien,male 446 | eleonore,female 447 | Elliott,male 448 | Elya,female 449 | Flora,female 450 | Jenna,female 451 | Jessica,female 452 | Johan,male 453 | Julian,male 454 | Kelly,female 455 | Laure,female 456 | Luka,male 457 | Maimouna,female 458 | Mariama,female 459 | Naim,male 460 | Owen,male 461 | Sandro,male 462 | Wael,male 463 | Walid,male 464 | Aissatou,female 465 | Aksel,male 466 | Balthazar,male 467 | Caroline,female 468 | Elea,female 469 | Elia,female 470 | Ewen,male 471 | Gauthier,male 472 | Idriss,male 473 | James,male 474 | Leonore,female 475 | Louison,female 476 | Maxine,female 477 | Michel,male 478 | Oumar,male 479 | Tessa,female 480 | Adama,male 481 | Aissata,female 482 | Ashley,female 483 | Assa,female 484 | Audrey,female 485 | Bastien,male 486 | Bilel,male 487 | Colette,female 488 | Constantin,male 489 | Elyes,male 490 | Hanae,female 491 | Leonardo,male 492 | Maeva,female 493 | Mayeul,male 494 | Mya,female 495 | Nayla,female 496 | Norah,female 497 | Rania,female 498 | Sam,male 499 | Shirel,female 500 | Sibylle,female 501 | Tara,female 502 | Wael,male 503 | Yohan,male 504 | Youssouf,male 505 | Adel,male 506 | Alan,male 507 | Anae,female 508 | Anastasia,female 509 | Augustine,female 510 | Calixte,male 511 | Coumba,female 512 | Diana,female 513 | Domitille,female 514 | Eloi,male 515 | Ernest,male 516 | Ewan,male 517 | Ferdinand,male 518 | Ilan,male 519 | Ivan,male 520 | Jana,female 521 | Jonathan,male 522 | Lili,female 523 | Loane,female 524 | Ludivine,female 525 | Melissa,female 526 | Nine,female 527 | Philomene,female 528 | Robinson,male 529 | Rosalie,female 530 | Sonia,female 531 | Thelma,female 532 | Alia,female 533 | Angelina,female 534 | Boubacar,male 535 | Bryan,male 536 | Christian,male 537 | Christophe,male 538 | Clarisse,female 539 | Enora,female 540 | Fares,male 541 | Georges,male 542 | Jad,male 543 | Kamil,male 544 | Lilya,female 545 | Lino,male 546 | Marcel,male 547 | Mateo,male 548 | Mateo,male 549 | Maylis,female 550 | Melvil,male 551 | Mona,female 552 | Mouhamed,male 553 | Nada,female 554 | Naelle,female 555 | Nell,female 556 | Niels,male 557 | Sacha,female 558 | Serena,female 559 | Soline,female 560 | Talia,female 561 | Wendy,female 562 | Yara,female 563 | Aliyah,female 564 | Alyssa,female 565 | Andrea,female 566 | Andy,male 567 | Anissa,female 568 | Bilal,male 569 | Carla,female 570 | Charly,male 571 | Chris,male 572 | Cleophee,female 573 | Colin,male 574 | Dan,male 575 | elea,female 576 | Elio,male 577 | Elouan,male 578 | Ema,female 579 | Emmanuelle,female 580 | Emna,female 581 | Eugene,male 582 | Felicie,female 583 | Fleur,female 584 | Florent,male 585 | Gaetan,male 586 | Isabelle,female 587 | Jeremy,male 588 | June,female 589 | Kevin,male 590 | Kiara,female 591 | Leila,female 592 | Lilas,female 593 | Lilian,male 594 | Louka,male 595 | Mahamadou,male 596 | Malak,female 597 | Malik,male 598 | Marilou,female 599 | Marwane,male 600 | Maud,female 601 | Maxim,male 602 | Melina,female 603 | Naomie,female 604 | Nawel,female 605 | Oren,male 606 | Rita,female 607 | Sekou,male 608 | Shana,female 609 | Soren,male 610 | Valentina,female 611 | Zachary,male 612 | Abdoul,male 613 | Aboubacar,male 614 | Alessio,male 615 | Amelia,female 616 | Andreas,male 617 | Anouck,female 618 | Asma,female 619 | Aurelie,female 620 | Camil,male 621 | Dalia,female 622 | Darius,male 623 | Eyal,male 624 | Fanny,female 625 | Fatou,female 626 | Florence,female 627 | Gabriela,female 628 | Hassan,male 629 | Ilyana,female 630 | Imran,male 631 | Ismael,male 632 | Jeremie,male 633 | Jordan,male 634 | Kenan,male 635 | Lior,male 636 | Liv,female 637 | Loan,male 638 | Lukas,male 639 | Marianne,female 640 | Marley,male 641 | Meline,female 642 | Mellina,female 643 | Nahil,male 644 | Pia,female 645 | Prune,female 646 | Rami,male 647 | Tali,female 648 | Youcef,male 649 | Zadig,male 650 | Zahra,female 651 | Adriana,female 652 | Alistair,male 653 | Alix,male 654 | Amelia,female 655 | Armel,male 656 | Bertille,female 657 | Billie,female 658 | Chaima,female 659 | Cheick,male 660 | Clelia,female 661 | Cyprien,male 662 | elise,female 663 | eloise,female 664 | Elyas,male 665 | Emilia,female 666 | Emilien,male 667 | Eulalie,female 668 | Flore,female 669 | Gabriella,female 670 | Gloria,female 671 | Isis,female 672 | Iyad,male 673 | Lou,male 674 | Lya,female 675 | Lyam,male 676 | Lylia,female 677 | Mahaut,female 678 | Mariame,female 679 | Marwan,male 680 | Melchior,male 681 | Mickael,male 682 | Milena,female 683 | Morgan,male 684 | Naila,female 685 | Neila,female 686 | Nikita,male 687 | Ornella,female 688 | Philippe,male 689 | Rokia,female 690 | Selyan,male 691 | Sienna,female 692 | Teo,male 693 | Thibaut,male 694 | Younes,male 695 | Adrian,male 696 | Aida,female 697 | Aidan,male 698 | Alassane,male 699 | Albert,male 700 | Alessandro,male 701 | Aline,female 702 | Alisha,female 703 | Alois,male 704 | Alycia,female 705 | Amel,female 706 | Angelo,male 707 | Ariel,male 708 | Athenais,female 709 | Avril,female 710 | Benoit,male 711 | Blandine,female 712 | Cecile,female 713 | Celia,female 714 | Christopher,male 715 | Cloe,female 716 | Clovis,male 717 | Damian,male 718 | Dario,male 719 | elie,male 720 | Ellie,female 721 | Fatim,female 722 | Filip,male 723 | Gaetan,male 724 | Grace,female 725 | Honore,male 726 | Ilias,male 727 | Ilyan,male 728 | Ilyes,male 729 | Irene,female 730 | Issam,male 731 | Iyed,male 732 | Khadidja,female 733 | Kim,female 734 | Lahna,female 735 | Lamine,male 736 | Leandro,male 737 | Lia,female 738 | Line,female 739 | Lucia,female 740 | Lucy,female 741 | Lyes,male 742 | Maeva,female 743 | Mahault,female 744 | Mario,male 745 | Marvin,male 746 | Mathilda,female 747 | Mayssa,female 748 | Melvin,male 749 | Nesrine,female 750 | Nolann,male 751 | Nolhan,male 752 | Noor,female 753 | Nourane,female 754 | Roxanne,female 755 | Ryad,male 756 | Sabrina,female 757 | Sana,female 758 | Sean,male 759 | Simeon,male 760 | Tony,male 761 | Yael,female 762 | Yael,female 763 | Yoann,male 764 | Abraham,male 765 | Alyah,female 766 | Alyssia,female 767 | Amy,female 768 | Ania,female 769 | Anne,female 770 | Anselme,male 771 | Ari,male 772 | Arnaud,male 773 | Assya,female 774 | Bakary,male 775 | Cameron,male 776 | Candice,female 777 | Christine,female 778 | Clea,female 779 | Cyril,male 780 | Daria,female 781 | elisa,female 782 | Elly,female 783 | Elodie,female 784 | Emeline,female 785 | emile,male 786 | emilie,female 787 | Emily,female 788 | Emir,male 789 | eva,female 790 | Fares,male 791 | Flavie,female 792 | Gautier,male 793 | Guilhem,male 794 | Hafsa,female 795 | Hayden,male 796 | Hermine,female 797 | Ian,male 798 | Idrissa,male 799 | Imene,female 800 | Islam,male 801 | Janna,female 802 | Jean-Baptiste,male 803 | Jibril,male 804 | Joan,male 805 | Johanna,female 806 | Katia,female 807 | Kenny,male 808 | Kilian,male 809 | Laetitia,female 810 | Lazare,male 811 | Leina,female 812 | Leyna,female 813 | Lindsay,female 814 | Liza,female 815 | Luce,female 816 | Mael,male 817 | Mahe,male 818 | Malia,female 819 | Marco,male 820 | Marlon,male 821 | Matias,male 822 | Matis,male 823 | Meriem,female 824 | Merlin,male 825 | Milhan,male 826 | Moustapha,male 827 | Nelia,female 828 | Neyla,female 829 | Noan,male 830 | Noelie,female 831 | Paola,female 832 | Rahma,female 833 | Raoul,male 834 | Rodrigue,male 835 | Ronan,male 836 | Salimata,female 837 | Samba,male 838 | Sebastien,male 839 | Sidonie,female 840 | Silas,male 841 | Sohane,female 842 | Souleyman,male 843 | Stefan,male 844 | Tania,female 845 | Tasnim,female 846 | Tiffany,female 847 | Vanessa,female 848 | Vasco,male 849 | Viktor,male 850 | Yani,male 851 | Yannis,male 852 | Yse,female 853 | Zakariya,male 854 | Aleksandra,female 855 | Alessia,female 856 | Alfred,male 857 | Allan,male 858 | Amalia,female 859 | Amara,male 860 | Ambrine,female 861 | Andrea,female 862 | Angela,female 863 | Angelina,female 864 | Annaelle,female 865 | Anya,female 866 | Aristide,male 867 | Artus,male 868 | Aubin,male 869 | Auriane,female 870 | Aziz,male 871 | Ben,male 872 | Binta,female 873 | Celestin,male 874 | Celestine,female 875 | Celian,male 876 | Chahine,male 877 | Djeneba,female 878 | Elena,female 879 | Elior,male 880 | Elyne,female 881 | Erin,female 882 | Evann,male 883 | eve,female 884 | Ezio,male 885 | Fadi,male 886 | Harry,male 887 | Hedi,male 888 | Helena,female 889 | Henry,male 890 | Hiba,female 891 | Hind,female 892 | Imrane,male 893 | Jaden,male 894 | Jason,male 895 | Jeremy,male 896 | Joanna,female 897 | Kadiatou,female 898 | Karl,male 899 | Kayna,female 900 | Khalil,male 901 | Killian,male 902 | Laetitia,female 903 | Lauren,female 904 | Laurent,male 905 | Leana,female 906 | Leonard,male 907 | Linda,female 908 | Linoi,female 909 | Liora,female 910 | Lisandro,male 911 | Lital,female 912 | Lois,male 913 | Lou-Ann,female 914 | Louay,male 915 | Louca,male 916 | Lydia,female 917 | Lyla,female 918 | Mae,female 919 | Maelie,female 920 | Maelyne,female 921 | Maeva,female 922 | Mahmoud,male 923 | Mailys,female 924 | Maiwenn,female 925 | Manelle,female 926 | Marko,male 927 | Matilde,female 928 | Mattia,male 929 | Melody,female 930 | Mendel,male 931 | Meryam,female 932 | Mohamed-Amine,male 933 | Nabil,male 934 | Nadia,female 935 | Natalia,female 936 | Nelly,female 937 | Nicole,female 938 | Noura,female 939 | Oliver,male 940 | Olympe,female 941 | Pacome,male 942 | Pedro,male 943 | Ranya,female 944 | Safa,female 945 | Salim,male 946 | Samia,female 947 | Santiago,male 948 | Scarlett,female 949 | Selena,female 950 | Soumaya,female 951 | Tamara,female 952 | Thea,female 953 | Tiphaine,female 954 | Tomas,male 955 | Vianney,male 956 | Viviane,female 957 | Warren,male 958 | Willy,male 959 | Xavier,male 960 | Yohann,male 961 | Yossef,male 962 | Ysee,female 963 | Abdallah,male 964 | Abigail,female 965 | Aby,female 966 | Adame,male 967 | Adil,male 968 | Agnes,female 969 | Ahmad,male 970 | Aisha,female 971 | Alba,female 972 | Alexander,male 973 | Alima,female 974 | Alina,female 975 | Alissa,female 976 | Alizee,female 977 | Allegra,female 978 | Amanda,female 979 | Aris,male 980 | Arman,male 981 | Ayline,female 982 | Aymane,male 983 | Barbara,female 984 | Blessing,female 985 | Bonnie,female 986 | Brahim,male 987 | Brieuc,male 988 | Camilia,female 989 | Carl,male 990 | Cassandra,female 991 | Cassie,female 992 | Cecilia,female 993 | Chayma,female 994 | Clarence,male 995 | Clothilde,female 996 | Dani,male 997 | Daouda,male 998 | Darine,female 999 | Deborah,female 1000 | Deborah,female 1001 | El,male 1002 | Ela,female 1003 | Eleanore,female 1004 | Eliana,female 1005 | elias,male 1006 | Elizabeth,female 1007 | elodie,female 1008 | Elyssa,female 1009 | Enguerrand,male 1010 | Erwann,male 1011 | Fabio,male 1012 | Fatouma,female 1013 | Fiona,female 1014 | Fode,male 1015 | Goundo,female 1016 | Grace,female 1017 | Haroun,male 1018 | Haya,female 1019 | Hillel,male 1020 | Idris,male 1021 | Iliana,female 1022 | Ilona,female 1023 | Imad,male 1024 | Irina,female 1025 | Isra,female 1026 | Jayden,male 1027 | Jimmy,male 1028 | Joakim,male 1029 | Joana,female 1030 | Johann,male 1031 | John,male 1032 | Jonah,male 1033 | Joris,male 1034 | Josh,male 1035 | Juan,male 1036 | Kadidiatou,female 1037 | Kelyan,male 1038 | Keren,female 1039 | Leny,male 1040 | Leo,male 1041 | Loris,male 1042 | Louison,male 1043 | Lyana,female 1044 | Maceo,male 1045 | Maissane,female 1046 | Malick,male 1047 | Marina,female 1048 | Mark,male 1049 | Matheo,male 1050 | Matthew,male 1051 | May,female 1052 | Melanie,female 1053 | Melodie,female 1054 | Menahem,male 1055 | Michael,male 1056 | Mickael,male 1057 | Miguel,male 1058 | Minh,male 1059 | Mohammad,male 1060 | Mustapha,male 1061 | Nadir,male 1062 | Nail,male 1063 | Naima,female 1064 | Nathaniel,male 1065 | Nayel,male 1066 | Nelson,male 1067 | Nikola,male 1068 | Nohan,male 1069 | Nola,female 1070 | Oriane,female 1071 | Paolo,male 1072 | Patrick,male 1073 | Philemon,male 1074 | Quitterie,female 1075 | Rosa,female 1076 | Saad,male 1077 | Safia,female 1078 | Samson,male 1079 | Sekou,male 1080 | Selene,female 1081 | Sharon,female 1082 | Shelly,female 1083 | Sherine,female 1084 | Siloe,female 1085 | Stephanie,female 1086 | Sven,male 1087 | Swann,male 1088 | Sybille,female 1089 | Tesnime,female 1090 | Thalia,female 1091 | Thibaud,male 1092 | Tim,male 1093 | Tommy,male 1094 | Vladimir,male 1095 | Wissem,male 1096 | Yannick,male 1097 | Yasmina,female 1098 | Yassin,male 1099 | Yoan,male 1100 | Yousra,female 1101 | Yuri,male 1102 | Zephyr,male 1103 | Abd,male 1104 | Aicha,female 1105 | Aidan,male 1106 | Aiden,male 1107 | Aimee,female 1108 | Aina,female 1109 | Aissa,female 1110 | Aissatou,female 1111 | Akram,male 1112 | Alec,male 1113 | Alone,male 1114 | Aloys,male 1115 | Alpha,male 1116 | Anabelle,female 1117 | Anass,male 1118 | Ange,male 1119 | Angel,female 1120 | Angeline,female 1121 | Anita,female 1122 | Annabelle,female 1123 | Antonia,female 1124 | Arie,male 1125 | Arielle,female 1126 | Arij,female 1127 | Armance,female 1128 | Armelle,female 1129 | Arwa,female 1130 | Asia,female 1131 | Asmaa,female 1132 | Aurel,male 1133 | Aydan,male 1134 | Aylan,male 1135 | Aylin,female 1136 | Ayman,male 1137 | Barnabe,male 1138 | Barthelemy,male 1139 | Bella,female 1140 | Bradley,male 1141 | Camila,female 1142 | Cassiopee,female 1143 | Castille,female 1144 | Cerise,female 1145 | Chahinez,female 1146 | Charlene,female 1147 | Charlize,female 1148 | Cheikh,male 1149 | Coralie,female 1150 | Cosima,female 1151 | Cyrielle,female 1152 | Cyrine,female 1153 | Dalla,female 1154 | Daphnee,female 1155 | Darren,male 1156 | Dayane,male 1157 | Dov,male 1158 | Driss,male 1159 | Eddy,male 1160 | Eglantine,female 1161 | eline,female 1162 | Elissa,female 1163 | Eloane,female 1164 | eloi,male 1165 | Eya,female 1166 | Eytan,male 1167 | Fantine,female 1168 | Farouk,male 1169 | Fatma,female 1170 | Feryel,female 1171 | Franck,male 1172 | Franklin,male 1173 | Gaelle,female 1174 | Gary,male 1175 | George,male 1176 | Georgia,female 1177 | Gianni,male 1178 | Gwenaelle,female 1179 | Hadja,female 1180 | Hadriel,male 1181 | Hajar,female 1182 | Halima,female 1183 | Hania,female 1184 | Harouna,male 1185 | Hatouma,female 1186 | Hedi,male 1187 | Helena,female 1188 | Ilhan,male 1189 | Iliane,male 1190 | Ilyane,male 1191 | Ilyess,male 1192 | Ines,female 1193 | Inza,male 1194 | Issiaka,male 1195 | Jacob,male 1196 | Jacqueline,female 1197 | Joanne,female 1198 | Joe,male 1199 | Josue,male 1200 | Joyce,female 1201 | Juliana,female 1202 | Kadidia,female 1203 | Kayla,female 1204 | Keira,female 1205 | Kenzi,male 1206 | Kenzy,male 1207 | Keyla,female 1208 | Kim,male 1209 | Laurine,female 1210 | Leandro,male 1211 | Leontine,female 1212 | Leopoldine,female 1213 | Leticia,female 1214 | Levana,female 1215 | Leyla,female 1216 | Liana,female 1217 | Lilie,female 1218 | Lili-Rose,female 1219 | Lilly,female 1220 | Lily-Rose,female 1221 | Lois,female 1222 | Loise,female 1223 | Lou-Anne,female 1224 | Lounis,male 1225 | Lubin,male 1226 | Lucille,female 1227 | Ludmila,female 1228 | Mae,male 1229 | Mamoudou,male 1230 | Marie-Ange,female 1231 | Matisse,male 1232 | Matys,male 1233 | Maxens,male 1234 | Mayar,female 1235 | Meryem,female 1236 | Milla,female 1237 | Mira,female 1238 | Miya,female 1239 | Monica,female 1240 | Naia,female 1241 | Nala,female 1242 | Natacha,female 1243 | Nelya,female 1244 | Niame,female 1245 | Niouma,female 1246 | Nizar,male 1247 | Noam,male 1248 | Nouha,male 1249 | Olga,female 1250 | Ophelie,female 1251 | Paula,female 1252 | Peter,male 1253 | Priscille,female 1254 | Prosper,male 1255 | Prudence,female 1256 | Rebecca,female 1257 | Reda,male 1258 | Reda,male 1259 | Sadio,female 1260 | Said,male 1261 | Selena,female 1262 | Shai,male 1263 | Shaima,female 1264 | Shaina,female 1265 | Shannon,female 1266 | Shayan,male 1267 | Shayna,female 1268 | Sherine,female 1269 | Shirine,female 1270 | Sihem,female 1271 | Sohan,male 1272 | Stephane,male 1273 | Steven,male 1274 | Swan,male 1275 | Talya,female 1276 | Tancrede,male 1277 | Tanya,female 1278 | Tao,male 1279 | Tatiana,female 1280 | Tea,female 1281 | Terence,male 1282 | Theo,male 1283 | Theophane,male 1284 | Tiana,female 1285 | Tidiane,male 1286 | Tina,female 1287 | Tsipora,female 1288 | Tyron,male 1289 | Vera,female 1290 | Viktoria,female 1291 | Yahya,male 1292 | Yamina,female 1293 | Ylan,male 1294 | Yoel,male 1295 | Yuna,female 1296 | Yvan,male 1297 | Zacharia,male 1298 | Zakary,male 1299 | -------------------------------------------------------------------------------- /src/main/resources/static/advanced/advanced.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `advanced` component, along with its associated controller and template 4 | angular. 5 | module('advanced'). 6 | component('advanced', { 7 | templateUrl: 'advanced/advanced.template.html', 8 | controller: ['$http', function AdvancedController($http) { 9 | var self = this; 10 | self.name = ""; 11 | self.country = ""; 12 | self.city = ""; 13 | 14 | self.advanced_search = function() { 15 | $http({method: 'GET', url: '/api/1/person/_advanced_search?from=0&size=10&country='+self.country+'&city='+self.city+'&name='+ self.name }) 16 | .then(function successCallback(response) { 17 | self.error = null; 18 | self.result = response.data; 19 | console.log(self.result.hits.total); 20 | }, function errorCallback(response) { 21 | self.error = "Backend not available"; 22 | }); 23 | }; 24 | 25 | self.advanced_search(); 26 | }] 27 | }); 28 | -------------------------------------------------------------------------------- /src/main/resources/static/advanced/advanced.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `advanced` module 4 | angular.module('advanced', []); 5 | -------------------------------------------------------------------------------- /src/main/resources/static/advanced/advanced.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 9 | 10 |
11 |
12 | 13 |
14 |
15 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 27 | 28 |
29 |
30 |
31 | 32 |
33 |
34 |

35 | Found {{$ctrl.result.hits.total.value}} hits in 36 | {{$ctrl.result.took}} ms 37 |

38 |

39 | {{$ctrl.error}} 40 |

41 |
42 |
43 | 44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
NameGenderDate Of BirthCountryCity
{{entry._source.name}}{{entry._source.gender}}{{entry._source.dateOfBirth}}{{entry._source.address.country}}{{entry._source.address.city}}
67 |
68 |
69 | 70 |
71 | -------------------------------------------------------------------------------- /src/main/resources/static/app.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular. 4 | module('legacyApp'). 5 | constant("config", { 6 | // "backend": "http://0.0.0.0:8080/" // For local tests 7 | "backend": "" 8 | }). 9 | config(['$locationProvider' ,'$routeProvider', 10 | function config($locationProvider, $routeProvider) { 11 | $locationProvider.hashPrefix('!'); 12 | 13 | $routeProvider. 14 | when('/search', {template: ''}). 15 | when('/init', {template: '' }). 16 | when('/compute', {template: '' }). 17 | when('/advanced', {template: '' }). 18 | when('/kibana', {template: '' }). 19 | when('/person/:id', {template: ''}). 20 | otherwise({redirectTo: '/search'}); 21 | 22 | console.log("Project running with AngularJS " + angular.version.full); 23 | } 24 | ]); 25 | -------------------------------------------------------------------------------- /src/main/resources/static/app.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `legacyApp` module 4 | angular.module('legacyApp', [ 5 | 'ngRoute', 6 | 'ui.bootstrap', 7 | 'init', 8 | 'search', 9 | 'advanced', 10 | 'compute', 11 | 'kibana', 12 | 'personDetail' 13 | ]); 14 | -------------------------------------------------------------------------------- /src/main/resources/static/compute/compute.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `compute` component, along with its associated controller and template 4 | angular. 5 | module('compute'). 6 | component('compute', { 7 | templateUrl: 'compute/compute.template.html', 8 | controller: ['$http', function ComputeController($http) { 9 | var self = this; 10 | self.query = ""; 11 | self.f_date = ""; 12 | self.f_country = ""; 13 | 14 | self.search = function() { 15 | $http({method: 'GET', url: '/api/1/person/_search?from=0&size=10&q='+ self.query 16 | + '&f_date=' + self.f_date + '&f_country=' + self.f_country }) 17 | .then(function successCallback(response) { 18 | self.error = null; 19 | self.result = response.data; 20 | 21 | // Group data every 10 years (facets don't support it yet) 22 | self.dates = new Array(); 23 | 24 | // If we have aggs, compute (for future use) 25 | if (self.result.aggregations) { 26 | var buckets = self.result.aggregations['date_histogram#by_year'].buckets; 27 | 28 | var i = -1; 29 | for (var idx in buckets) { 30 | var year = buckets[idx].key_as_string; 31 | var docs = buckets[idx].doc_count; 32 | var subyear = year.substr(0,3); 33 | 34 | if (i == -1 || subyear != self.dates[i].key) { 35 | i++; 36 | self.dates[i] = {}; 37 | self.dates[i].key = subyear; 38 | self.dates[i].docs = docs; 39 | } else { 40 | self.dates[i].docs += docs; 41 | } 42 | } 43 | } 44 | }, function errorCallback(response) { 45 | self.error = "Backend not available"; 46 | }); 47 | }; 48 | 49 | self.addFilterCountry = function(bucket) { 50 | console.log(bucket.key); 51 | self.f_country = bucket.key; 52 | self.search(); 53 | }; 54 | 55 | self.addFilterDate = function(bucket) { 56 | console.log(bucket.key+"0"); 57 | self.f_date = bucket.key+"0"; 58 | self.search(); 59 | }; 60 | 61 | self.search(); 62 | }] 63 | }); 64 | -------------------------------------------------------------------------------- /src/main/resources/static/compute/compute.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `compute` module 4 | angular.module('compute', []); 5 | -------------------------------------------------------------------------------- /src/main/resources/static/compute/compute.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 9 | 10 |
11 |
12 |
13 | 14 |
15 |
16 |

17 | Found {{$ctrl.result.hits.total.value}} hits in 18 | {{$ctrl.result.took}} ms 19 |

20 |

21 | {{$ctrl.error}} 22 |

23 |
24 |
25 | 26 |
27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 61 | 62 | 63 | 64 |
CountryCountPer Year
{{bucket.key}}{{bucket.doc_count}} 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 |
year{{year.key_as_string}}
persons{{year.doc_count}}
children{{year['avg#avg_children'].value.toFixed(1)}} 56 |
60 |
65 |
66 |
67 | 68 |
69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
NameGenderDate Of BirthCountryCityScore
{{entry._source.name}}{{entry._source.gender}}{{entry._source.dateOfBirth}}{{entry._source.address.country}}{{entry._source.address.city}}{{entry._score}}
93 |
94 |
95 | 96 |
97 | -------------------------------------------------------------------------------- /src/main/resources/static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dadoonet/legacy-search/7b7b3035df531248bfb2c93ccbb3aacd5e3188ad/src/main/resources/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /src/main/resources/static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dadoonet/legacy-search/7b7b3035df531248bfb2c93ccbb3aacd5e3188ad/src/main/resources/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Advanced search for your legacy application 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 64 | 65 |
66 | 67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/resources/static/init/init.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `init` component, along with its associated controller and template 4 | angular. 5 | module('init'). 6 | component('init', { 7 | templateUrl: 'init/init.template.html', 8 | controller: ['$http', '$interval', 'config', function InitController($http, $interval, config) { 9 | var self = this; 10 | self.persons = ""; 11 | self.status = ""; 12 | self.progress = { 13 | took: 0, 14 | rate: 0, 15 | current: 0 16 | }; 17 | self.remaining = 0; 18 | self.goal = 0; 19 | self.took = 0; 20 | self.result = null; 21 | 22 | var stop; 23 | self.startWatch = function() { 24 | if ( angular.isDefined(stop) ) return; 25 | 26 | stop = $interval(function() { 27 | // Poll the status API 28 | $http({method: 'GET', url: config.backend + '/api/1/person/_init_status' }) 29 | .then(function successCallback(response) { 30 | self.progress = response.data; 31 | // Remaining docs 32 | var remaining_docs = self.persons - self.progress.current; 33 | self.remaining = Math.round(remaining_docs / self.progress.rate); 34 | self.took = Math.round(self.progress.took / 1000); 35 | }); 36 | }, 100); 37 | }; 38 | 39 | self.stopWatch = function() { 40 | if (angular.isDefined(stop)) { 41 | $interval.cancel(stop); 42 | stop = undefined; 43 | } 44 | }; 45 | 46 | self.init = function() { 47 | self.status = ""; 48 | self.result = null; 49 | self.progress = { 50 | took: 0, 51 | rate: 0, 52 | current: 0 53 | }; 54 | self.remaining = 0; 55 | self.goal = self.persons; 56 | self.took = 0; 57 | self.startWatch(); 58 | $http({method: 'GET', url: config.backend + '/api/1/person/_init?size='+self.persons }) 59 | .then(function successCallback(response) { 60 | self.result = response.data; 61 | self.progress = self.result; 62 | self.status = "bg-success"; 63 | self.stopWatch(); 64 | }); 65 | } 66 | }] 67 | }); 68 | -------------------------------------------------------------------------------- /src/main/resources/static/init/init.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `init` module 4 | angular.module('init', []); 5 | -------------------------------------------------------------------------------- /src/main/resources/static/init/init.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 8 | 9 |
10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |
20 |
25 |

{{$ctrl.progress.current}} on {{$ctrl.goal}}

26 |
27 |
28 |
29 |
30 | 31 |
32 |
33 |

Elapsed

34 |
35 |
36 |

{{$ctrl.progress.took / 1000}} s

37 |
38 |
39 |
40 |
41 |

Estimated time left

42 |
43 |
44 |

{{$ctrl.remaining}} s

45 |
46 |
47 |
48 |
49 |

Rate

50 |
51 |
52 |

{{$ctrl.progress.rate}} persons per sec

53 |
54 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/resources/static/kibana/console.txt: -------------------------------------------------------------------------------- 1 | ### Step 0 : INIT 2 | DELETE test 3 | DELETE person 4 | 5 | ### Step 1 6 | 7 | GET / 8 | 9 | GET _cat/indices/person*?v&h=index,docs.count,store.size 10 | 11 | GET person/_search?track_total_hits=true 12 | 13 | GET person/_mapping 14 | 15 | 16 | ### Step 2 : Play with analyzers 17 | 18 | # What happens at index time? 19 | POST person/_analyze 20 | { 21 | "text": "JoE SMith", 22 | "analyzer": "standard" 23 | } 24 | # What happens at search time? 25 | POST person/_analyze 26 | { 27 | "text": "JO", 28 | "analyzer": "standard" 29 | } 30 | POST person/_analyze 31 | { 32 | "text": "JOE", 33 | "analyzer": "standard" 34 | } 35 | 36 | DELETE test 37 | PUT test 38 | { 39 | "settings": { 40 | "analysis": { 41 | "analyzer": { 42 | "ngram": { 43 | "tokenizer": "ngram_tokenizer" 44 | } 45 | }, 46 | "tokenizer": { 47 | "ngram_tokenizer": { 48 | "type": "edge_ngram", 49 | "min_gram": "1", 50 | "max_gram": "10", 51 | "token_chars": [ "letter", "digit" ] 52 | } 53 | } 54 | } 55 | } 56 | } 57 | # What happens at index time? 58 | POST test/_analyze 59 | { 60 | "text": "joe smith", 61 | "analyzer": "ngram" 62 | } 63 | # What happens at search time? 64 | POST test/_analyze 65 | { 66 | "text": "JO", 67 | "analyzer": "simple" 68 | } 69 | DELETE test 70 | 71 | DELETE person 72 | # We could manually run that 73 | PUT person 74 | { 75 | "settings": { 76 | "analysis": { 77 | "analyzer": { 78 | "ngram": { 79 | "tokenizer": "ngram_tokenizer", 80 | "filter": [ 81 | "lowercase" 82 | ] 83 | } 84 | }, 85 | "tokenizer": { 86 | "ngram_tokenizer": { 87 | "type": "edge_ngram", 88 | "min_gram": "1", 89 | "max_gram": "10", 90 | "token_chars": [ 91 | "letter", 92 | "digit" 93 | ] 94 | } 95 | } 96 | } 97 | }, 98 | "mappings": { 99 | "properties": { 100 | "address": { 101 | "properties": { 102 | "city": { 103 | "type": "text", 104 | "fields": { 105 | "ngram": { 106 | "type": "text", 107 | "analyzer": "ngram", 108 | "search_analyzer": "simple" 109 | }, 110 | "keyword": { 111 | "type": "keyword" 112 | } 113 | } 114 | }, 115 | "country": { 116 | "type": "text", 117 | "fields": { 118 | "ngram": { 119 | "type": "text", 120 | "analyzer": "ngram", 121 | "search_analyzer": "simple" 122 | }, 123 | "keyword": { 124 | "type": "keyword" 125 | } 126 | } 127 | }, 128 | "countrycode": { 129 | "type": "keyword" 130 | }, 131 | "location": { 132 | "type": "geo_point" 133 | }, 134 | "zipcode": { 135 | "type": "keyword" 136 | } 137 | } 138 | }, 139 | "children": { 140 | "type": "long" 141 | }, 142 | "dateOfBirth": { 143 | "type": "date", 144 | "format": "yyyy-MM-dd||yyyy" 145 | }, 146 | "gender": { 147 | "type": "text", 148 | "fields": { 149 | "ngram": { 150 | "type": "text", 151 | "analyzer": "ngram", 152 | "search_analyzer": "simple" 153 | }, 154 | "keyword": { 155 | "type": "keyword" 156 | } 157 | } 158 | }, 159 | "marketing": { 160 | "properties": { 161 | "cars": { 162 | "type": "long" 163 | }, 164 | "electronic": { 165 | "type": "long" 166 | }, 167 | "fashion": { 168 | "type": "long" 169 | }, 170 | "food": { 171 | "type": "long" 172 | }, 173 | "garden": { 174 | "type": "long" 175 | }, 176 | "hifi": { 177 | "type": "long" 178 | }, 179 | "music": { 180 | "type": "long" 181 | }, 182 | "shoes": { 183 | "type": "long" 184 | }, 185 | "toys": { 186 | "type": "long" 187 | } 188 | } 189 | }, 190 | "name": { 191 | "type": "text", 192 | "fields": { 193 | "ngram": { 194 | "type": "text", 195 | "analyzer": "ngram", 196 | "search_analyzer": "simple" 197 | } 198 | } 199 | }, 200 | "reference": { 201 | "type": "text" 202 | } 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/resources/static/kibana/kibana.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `kibana` component, along with its associated controller and template 4 | angular. 5 | module('kibana'). 6 | component('kibana', { 7 | templateUrl: 'kibana/kibana.template.html', 8 | controller: ['$http', function KibanaController($http) { 9 | }] 10 | }); 11 | -------------------------------------------------------------------------------- /src/main/resources/static/kibana/kibana.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `search` module 4 | angular.module('kibana', []); 5 | -------------------------------------------------------------------------------- /src/main/resources/static/kibana/kibana.ndjson: -------------------------------------------------------------------------------- 1 | {"attributes":{"allowHidden":false,"fieldAttrs":"{\"dateOfBirth\":{\"customLabel\":\"age\"},\"gender\":{},\"address.country\":{\"customLabel\":\"Country\"},\"flag\":{},\"address.city\":{\"customLabel\":\"City\"}}","fieldFormatMap":"{\"dateOfBirth\":{\"id\":\"relative_date\",\"params\":{}},\"gender\":{\"id\":\"color\",\"params\":{\"parsedUrl\":{\"origin\":\"http://0.0.0.0:5601\",\"pathname\":\"/app/home\",\"basePath\":\"\"},\"fieldType\":\"string\",\"colors\":[{\"range\":\"-Infinity:Infinity\",\"regex\":\"male\",\"text\":\"#fffbfb\",\"background\":\"#6092C0\"},{\"range\":\"-Infinity:Infinity\",\"regex\":\"female\",\"text\":\"#fffafa\",\"background\":\"#D36086\"}]}},\"flag\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://0.0.0.0:5601\",\"pathname\":\"/app/home\",\"basePath\":\"\"},\"type\":\"img\",\"urlTemplate\":\"https://flagicons.lipis.dev/flags/4x3/{{value}}.svg\",\"labelTemplate\":null,\"width\":\"20\",\"height\":\"15\"}}}","fields":"[]","name":"person*","runtimeFieldMap":"{\"flag\":{\"type\":\"keyword\",\"script\":{\"source\":\"emit(doc['address.countrycode'].value.toLowerCase());\"}}}","sourceFilters":"[]","timeFieldName":"dateOfBirth","title":"person*"},"coreMigrationVersion":"8.8.0","created_at":"2024-06-12T08:47:28.190Z","created_by":"u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0","id":"afe9e2f0-5138-11e9-9261-51158e23312b","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-06-12T18:13:26.302Z","version":"Wzc0LDFd"} 2 | {"attributes":{"color":"#54B399","description":"","name":"demo"},"coreMigrationVersion":"8.8.0","created_at":"2024-06-12T18:15:54.872Z","created_by":"u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0","id":"da2dff2c-34e2-4067-8b8f-e75c217afb40","managed":false,"references":[],"type":"tag","typeMigrationVersion":"8.0.0","updated_at":"2024-06-12T18:15:54.872Z","version":"WzI3OTcsMV0="} 3 | {"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"type\":\"lens\",\"gridData\":{\"x\":30,\"y\":0,\"w\":18,\"h\":10,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"attributes\":{\"title\":\"Persons total and average children per country (converted)\",\"visualizationType\":\"lnsDatatable\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-0ca9c871-0038-4696-b67f-5e3dbfa2fde2\"}],\"state\":{\"visualization\":{\"layerId\":\"0ca9c871-0038-4696-b67f-5e3dbfa2fde2\",\"layerType\":\"data\",\"columns\":[{\"columnId\":\"d2c6bf90-d880-469f-8d8e-898791cd6263\",\"alignment\":\"left\",\"colorMode\":\"cell\",\"palette\":{\"type\":\"palette\",\"name\":\"positive\",\"params\":{\"stops\":[{\"color\":\"#d6e9e4\",\"stop\":20},{\"color\":\"#aed3ca\",\"stop\":40},{\"color\":\"#85bdb1\",\"stop\":60},{\"color\":\"#5aa898\",\"stop\":80},{\"color\":\"#209280\",\"stop\":100}]}},\"summaryRow\":\"sum\"},{\"columnId\":\"abd80ba0-3775-4c86-8959-6619a19979b0\",\"alignment\":\"left\",\"colorMode\":\"text\",\"palette\":{\"type\":\"palette\",\"name\":\"status\",\"params\":{\"stops\":[{\"color\":\"#209280\",\"stop\":0},{\"color\":\"#54b399\",\"stop\":20},{\"color\":\"#d6bf57\",\"stop\":40},{\"color\":\"#e7664c\",\"stop\":60},{\"color\":\"#cc5642\",\"stop\":80}],\"name\":\"status\",\"continuity\":\"above\",\"reverse\":false,\"rangeMin\":0,\"rangeMax\":null}},\"summaryRow\":\"avg\"},{\"columnId\":\"462239cd-45a4-4ac9-ba90-bdb4833d5d71\",\"alignment\":\"center\",\"oneClickFilter\":true},{\"columnId\":\"2a2f155c-1669-4773-b62a-2c88c661d492\",\"alignment\":\"left\",\"colorMode\":\"cell\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":5,\"stops\":[{\"color\":\"#cc5642\",\"stop\":20},{\"color\":\"#e7664c\",\"stop\":40},{\"color\":\"#d6bf57\",\"stop\":60},{\"color\":\"#54b399\",\"stop\":80},{\"color\":\"#209280\",\"stop\":100}],\"name\":\"custom\",\"colorStops\":[{\"color\":\"#cc5642\",\"stop\":0},{\"color\":\"#e7664c\",\"stop\":20},{\"color\":\"#d6bf57\",\"stop\":40},{\"color\":\"#54b399\",\"stop\":60},{\"color\":\"#209280\",\"stop\":80}],\"continuity\":\"above\",\"reverse\":false,\"rangeMin\":0,\"rangeMax\":null}},\"summaryRow\":\"avg\"}],\"paging\":{\"enabled\":true,\"size\":4},\"rowHeight\":\"single\",\"headerRowHeight\":\"single\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"0ca9c871-0038-4696-b67f-5e3dbfa2fde2\":{\"ignoreGlobalFilters\":false,\"columns\":{\"462239cd-45a4-4ac9-ba90-bdb4833d5d71\":{\"label\":\"Country\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"address.country.keyword\",\"isBucketed\":true,\"params\":{\"size\":4,\"orderBy\":{\"type\":\"column\",\"columnId\":\"d2c6bf90-d880-469f-8d8e-898791cd6263\"},\"orderDirection\":\"desc\",\"otherBucket\":false,\"missingBucket\":false,\"parentFormat\":{\"id\":\"terms\"},\"include\":[],\"exclude\":[],\"includeIsRegex\":false,\"excludeIsRegex\":false},\"customLabel\":true},\"d2c6bf90-d880-469f-8d8e-898791cd6263\":{\"label\":\"Children\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"children\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":true},\"customLabel\":true},\"2a2f155c-1669-4773-b62a-2c88c661d492X0\":{\"label\":\"Part of Total children percentages\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"children\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"2a2f155c-1669-4773-b62a-2c88c661d492X1\":{\"label\":\"Part of Total children percentages\",\"dataType\":\"number\",\"operationType\":\"sum\",\"sourceField\":\"children\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"2a2f155c-1669-4773-b62a-2c88c661d492X2\":{\"label\":\"Part of Total children percentages\",\"dataType\":\"number\",\"operationType\":\"overall_sum\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"2a2f155c-1669-4773-b62a-2c88c661d492X1\"],\"customLabel\":true},\"2a2f155c-1669-4773-b62a-2c88c661d492X3\":{\"label\":\"Part of Total children percentages\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"divide\",\"args\":[\"2a2f155c-1669-4773-b62a-2c88c661d492X0\",\"2a2f155c-1669-4773-b62a-2c88c661d492X2\"],\"location\":{\"min\":0,\"max\":44},\"text\":\"(sum(children)) / overall_sum(sum(children))\"}},\"references\":[\"2a2f155c-1669-4773-b62a-2c88c661d492X0\",\"2a2f155c-1669-4773-b62a-2c88c661d492X2\"],\"customLabel\":true},\"2a2f155c-1669-4773-b62a-2c88c661d492\":{\"label\":\"Children %\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"format\":{\"id\":\"percent\",\"params\":{\"decimals\":1}},\"formula\":\"(sum(children)) / overall_sum(sum(children))\",\"isFormulaBroken\":false},\"references\":[\"2a2f155c-1669-4773-b62a-2c88c661d492X3\"],\"customLabel\":true},\"abd80ba0-3775-4c86-8959-6619a19979b0\":{\"label\":\"Children average\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"children\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":true},\"customLabel\":true}},\"columnOrder\":[\"462239cd-45a4-4ac9-ba90-bdb4833d5d71\",\"d2c6bf90-d880-469f-8d8e-898791cd6263\",\"2a2f155c-1669-4773-b62a-2c88c661d492X0\",\"2a2f155c-1669-4773-b62a-2c88c661d492X1\",\"2a2f155c-1669-4773-b62a-2c88c661d492X2\",\"2a2f155c-1669-4773-b62a-2c88c661d492X3\",\"2a2f155c-1669-4773-b62a-2c88c661d492\",\"abd80ba0-3775-4c86-8959-6619a19979b0\"],\"incompleteColumns\":{}}}},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{},\"hidePanelTitles\":true},\"title\":\"Persons total and average children per country\"},{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":0,\"w\":30,\"h\":10,\"i\":\"6749adb3-79e6-42f6-96cb-0415dbcb557a\"},\"panelIndex\":\"6749adb3-79e6-42f6-96cb-0415dbcb557a\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsXY\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-05fe61bc-817f-42bd-b26b-fe65d6b87085\"},{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-304df1c4-93b2-4102-850d-303e43e9d48b\"},{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-bdf7d430-2d74-4e7d-82b3-c399c4bd2710\"},{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"xy-visualization-layer-13080801-f3ee-4771-bb3a-04e4eda61c99\"}],\"state\":{\"visualization\":{\"legend\":{\"isVisible\":false,\"position\":\"right\",\"showSingleSeries\":false},\"valueLabels\":\"hide\",\"fittingFunction\":\"Carry\",\"axisTitlesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"tickLabelsVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"labelsOrientation\":{\"x\":0,\"yLeft\":0,\"yRight\":0},\"gridlinesVisibilitySettings\":{\"x\":true,\"yLeft\":true,\"yRight\":true},\"preferredSeriesType\":\"bar_stacked\",\"layers\":[{\"layerId\":\"05fe61bc-817f-42bd-b26b-fe65d6b87085\",\"accessors\":[\"28bdde79-281c-412f-a6b0-0d35875ad58b\"],\"position\":\"top\",\"seriesType\":\"line\",\"showGridlines\":false,\"layerType\":\"data\",\"colorMapping\":{\"assignments\":[{\"rule\":{\"type\":\"matchExactly\",\"values\":[\"female\"]},\"color\":{\"type\":\"categorical\",\"paletteId\":\"eui_amsterdam_color_blind\",\"colorIndex\":2},\"touched\":true},{\"rule\":{\"type\":\"matchExactly\",\"values\":[\"male\"]},\"color\":{\"type\":\"categorical\",\"paletteId\":\"eui_amsterdam_color_blind\",\"colorIndex\":1},\"touched\":true}],\"specialAssignments\":[{\"rule\":{\"type\":\"other\"},\"color\":{\"type\":\"loop\"},\"touched\":false}],\"paletteId\":\"eui_amsterdam_color_blind\",\"colorMode\":{\"type\":\"categorical\"}},\"xAccessor\":\"693589ae-5fbd-4af1-9aa4-321ec523db45\",\"collapseFn\":\"\",\"yConfig\":[{\"forAccessor\":\"28bdde79-281c-412f-a6b0-0d35875ad58b\",\"axisMode\":\"right\",\"color\":\"#6092c0\"}]},{\"layerId\":\"13080801-f3ee-4771-bb3a-04e4eda61c99\",\"layerType\":\"annotations\",\"annotations\":[{\"type\":\"manual\",\"id\":\"fcb6837c-27b1-4c5f-a3bb-f9f9253d08e9\",\"label\":\"David\",\"key\":{\"type\":\"point_in_time\",\"timestamp\":\"1971-12-26T22:30:00.000Z\"},\"color\":\"#54b399\",\"icon\":\"heart\",\"textVisibility\":true},{\"type\":\"manual\",\"label\":\"1970'\",\"key\":{\"type\":\"range\",\"timestamp\":\"1970-01-31T23:00:00.000Z\",\"endTimestamp\":\"1979-12-30T22:59:59.000Z\"},\"id\":\"51087434-b40a-474b-9847-96afa88db414\",\"color\":\"#54b3991a\",\"outside\":false}],\"ignoreGlobalFilters\":true,\"persistanceType\":\"byValue\",\"indexPatternId\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"},{\"layerId\":\"304df1c4-93b2-4102-850d-303e43e9d48b\",\"layerType\":\"data\",\"accessors\":[\"977f5493-34df-4c18-bec7-d221aa1684bb\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1ca\"],\"seriesType\":\"bar_stacked\",\"xAccessor\":\"6ef111a4-ebf5-42cb-8e0f-c5689d72367f\",\"yConfig\":[{\"forAccessor\":\"977f5493-34df-4c18-bec7-d221aa1684bb\",\"axisMode\":\"left\",\"color\":\"#54b399\"},{\"forAccessor\":\"5bb9c48c-29fb-425f-9ced-4472b6dbe1ca\",\"color\":\"#e7664c\",\"axisMode\":\"left\"}]},{\"layerId\":\"bdf7d430-2d74-4e7d-82b3-c399c4bd2710\",\"layerType\":\"referenceLine\",\"accessors\":[\"26435d9f-6d81-495e-a6a5-99c3996eee57\"],\"yConfig\":[{\"forAccessor\":\"26435d9f-6d81-495e-a6a5-99c3996eee57\",\"axisMode\":\"left\",\"lineWidth\":2,\"lineStyle\":\"dotted\",\"fill\":\"none\"}]}],\"curveType\":\"CURVE_MONOTONE_X\",\"endValue\":\"Nearest\",\"yTitle\":\"Cars\",\"xTitle\":\"Date of birth\",\"showCurrentTimeMarker\":false,\"yRightTitle\":\"Persons\",\"yRightExtent\":{\"mode\":\"full\"}},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"05fe61bc-817f-42bd-b26b-fe65d6b87085\":{\"columns\":{\"693589ae-5fbd-4af1-9aa4-321ec523db45\":{\"label\":\"date of birth\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"dateOfBirth\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true,\"dropPartials\":false},\"customLabel\":true},\"28bdde79-281c-412f-a6b0-0d35875ad58b\":{\"label\":\"Count\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"number\",\"params\":{\"decimals\":0,\"compact\":true}}},\"customLabel\":true}},\"columnOrder\":[\"693589ae-5fbd-4af1-9aa4-321ec523db45\",\"28bdde79-281c-412f-a6b0-0d35875ad58b\"],\"sampling\":1,\"ignoreGlobalFilters\":false,\"incompleteColumns\":{},\"indexPatternId\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"},\"304df1c4-93b2-4102-850d-303e43e9d48b\":{\"linkToLayers\":[],\"columns\":{\"6ef111a4-ebf5-42cb-8e0f-c5689d72367f\":{\"label\":\"age\",\"dataType\":\"date\",\"operationType\":\"date_histogram\",\"sourceField\":\"dateOfBirth\",\"isBucketed\":true,\"scale\":\"interval\",\"params\":{\"interval\":\"auto\",\"includeEmptyRows\":true,\"dropPartials\":false}},\"977f5493-34df-4c18-bec7-d221aa1684bbX0\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX1\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX2\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX3\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"overall_average\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX2\"],\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX4\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX5\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX6\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"overall_average\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX5\"],\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bbX7\":{\"label\":\"Part of Cars below\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"subtract\",\"args\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX0\",{\"type\":\"function\",\"name\":\"multiply\",\"args\":[{\"type\":\"function\",\"name\":\"clamp\",\"args\":[{\"type\":\"function\",\"name\":\"subtract\",\"args\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX1\",\"977f5493-34df-4c18-bec7-d221aa1684bbX3\"],\"location\":{\"min\":33,\"max\":99},\"text\":\"average(marketing.cars) - overall_average(average(marketing.cars))\"},0,1],\"location\":{\"min\":27,\"max\":104},\"text\":\"clamp(average(marketing.cars) - overall_average(average(marketing.cars)),0,1)\"},{\"type\":\"function\",\"name\":\"subtract\",\"args\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX4\",\"977f5493-34df-4c18-bec7-d221aa1684bbX6\"],\"location\":{\"min\":106,\"max\":170},\"text\":\"average(marketing.cars)-overall_average(average(marketing.cars))\"}],\"location\":{\"min\":25,\"max\":172},\"text\":\" (clamp(average(marketing.cars) - overall_average(average(marketing.cars)),0,1)*(average(marketing.cars)-overall_average(average(marketing.cars))))\"}],\"location\":{\"min\":0,\"max\":172},\"text\":\"average(marketing.cars) - (clamp(average(marketing.cars) - overall_average(average(marketing.cars)),0,1)*(average(marketing.cars)-overall_average(average(marketing.cars))))\"}},\"references\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX0\",\"977f5493-34df-4c18-bec7-d221aa1684bbX1\",\"977f5493-34df-4c18-bec7-d221aa1684bbX3\",\"977f5493-34df-4c18-bec7-d221aa1684bbX4\",\"977f5493-34df-4c18-bec7-d221aa1684bbX6\"],\"customLabel\":true},\"977f5493-34df-4c18-bec7-d221aa1684bb\":{\"label\":\"Cars below\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"average(marketing.cars) - (clamp(average(marketing.cars) - overall_average(average(marketing.cars)),0,1)*(average(marketing.cars)-overall_average(average(marketing.cars))))\",\"isFormulaBroken\":false},\"references\":[\"977f5493-34df-4c18-bec7-d221aa1684bbX7\"],\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX0\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX1\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX2\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"overall_average\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX1\"],\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX3\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX4\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX5\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"overall_average\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX4\"],\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX6\":{\"label\":\"Part of Cars above\",\"dataType\":\"number\",\"operationType\":\"math\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"tinymathAst\":{\"type\":\"function\",\"name\":\"multiply\",\"args\":[{\"type\":\"function\",\"name\":\"subtract\",\"args\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX0\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX2\"],\"location\":{\"min\":6,\"max\":78},\"text\":\"average(marketing.cars) - \\n overall_average(average(marketing.cars))\\n\"},{\"type\":\"function\",\"name\":\"clamp\",\"args\":[{\"type\":\"function\",\"name\":\"subtract\",\"args\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX3\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX5\"],\"location\":{\"min\":108,\"max\":183},\"text\":\"average(marketing.cars) - \\n overall_average(average(marketing.cars))\"},0,1],\"location\":{\"min\":93,\"max\":191},\"text\":\"clamp(\\n average(marketing.cars) - \\n overall_average(average(marketing.cars)), 0, 1)\\n\"}],\"location\":{\"min\":0,\"max\":192},\"text\":\"(\\n average(marketing.cars) - \\n overall_average(average(marketing.cars))\\n) \\n *\\n(\\n clamp(\\n average(marketing.cars) - \\n overall_average(average(marketing.cars)), 0, 1)\\n)\"}},\"references\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX0\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX2\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX3\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX5\"],\"customLabel\":true},\"5bb9c48c-29fb-425f-9ced-4472b6dbe1ca\":{\"label\":\"Cars above\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"(\\n average(marketing.cars) - \\n overall_average(average(marketing.cars))\\n) \\n *\\n(\\n clamp(\\n average(marketing.cars) - \\n overall_average(average(marketing.cars)), 0, 1)\\n)\",\"isFormulaBroken\":false},\"references\":[\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX6\"],\"customLabel\":true}},\"columnOrder\":[\"6ef111a4-ebf5-42cb-8e0f-c5689d72367f\",\"977f5493-34df-4c18-bec7-d221aa1684bb\",\"977f5493-34df-4c18-bec7-d221aa1684bbX0\",\"977f5493-34df-4c18-bec7-d221aa1684bbX1\",\"977f5493-34df-4c18-bec7-d221aa1684bbX2\",\"977f5493-34df-4c18-bec7-d221aa1684bbX3\",\"977f5493-34df-4c18-bec7-d221aa1684bbX4\",\"977f5493-34df-4c18-bec7-d221aa1684bbX5\",\"977f5493-34df-4c18-bec7-d221aa1684bbX6\",\"977f5493-34df-4c18-bec7-d221aa1684bbX7\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1ca\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX0\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX1\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX2\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX3\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX4\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX5\",\"5bb9c48c-29fb-425f-9ced-4472b6dbe1caX6\"],\"sampling\":1,\"ignoreGlobalFilters\":false,\"incompleteColumns\":{},\"indexPatternId\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"},\"bdf7d430-2d74-4e7d-82b3-c399c4bd2710\":{\"linkToLayers\":[],\"columns\":{\"26435d9f-6d81-495e-a6a5-99c3996eee57X0\":{\"label\":\"Part of Cars overall average\",\"dataType\":\"number\",\"operationType\":\"average\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":false},\"customLabel\":true},\"26435d9f-6d81-495e-a6a5-99c3996eee57X1\":{\"label\":\"Part of Cars overall average\",\"dataType\":\"number\",\"operationType\":\"overall_average\",\"isBucketed\":false,\"scale\":\"ratio\",\"references\":[\"26435d9f-6d81-495e-a6a5-99c3996eee57X0\"],\"customLabel\":true},\"26435d9f-6d81-495e-a6a5-99c3996eee57\":{\"label\":\"Cars overall average\",\"dataType\":\"number\",\"operationType\":\"formula\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"formula\":\"overall_average(average(marketing.cars))\",\"isFormulaBroken\":false},\"references\":[\"26435d9f-6d81-495e-a6a5-99c3996eee57X1\"],\"customLabel\":true}},\"columnOrder\":[\"26435d9f-6d81-495e-a6a5-99c3996eee57\",\"26435d9f-6d81-495e-a6a5-99c3996eee57X0\",\"26435d9f-6d81-495e-a6a5-99c3996eee57X1\"],\"sampling\":1,\"ignoreGlobalFilters\":false,\"incompleteColumns\":{},\"indexPatternId\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"}},\"currentIndexPatternId\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{},\"hidePanelTitles\":true},\"title\":\"Per year\"},{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":10,\"w\":24,\"h\":16,\"i\":\"82210c1a-47ec-4cdc-82fb-095150116ae7\"},\"panelIndex\":\"82210c1a-47ec-4cdc-82fb-095150116ae7\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsPie\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-f8de930d-49a1-4b8f-940d-4134b8defd92\"}],\"state\":{\"visualization\":{\"shape\":\"donut\",\"layers\":[{\"layerId\":\"f8de930d-49a1-4b8f-940d-4134b8defd92\",\"primaryGroups\":[\"93da4c60-d19f-4c12-9fdf-25697a5bd94c\",\"c0f95e07-3921-4318-8444-3f00d5e3e93d\"],\"metrics\":[\"66a9b2d7-1a7d-4e67-a3bd-7d7176240048\"],\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false,\"layerType\":\"data\",\"colorMapping\":{\"assignments\":[],\"specialAssignments\":[{\"rule\":{\"type\":\"other\"},\"color\":{\"type\":\"loop\"},\"touched\":false}],\"paletteId\":\"eui_amsterdam_color_blind\",\"colorMode\":{\"type\":\"categorical\"}}}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"f8de930d-49a1-4b8f-940d-4134b8defd92\":{\"columns\":{\"93da4c60-d19f-4c12-9fdf-25697a5bd94c\":{\"label\":\"Countries\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"address.country.keyword\",\"isBucketed\":true,\"params\":{\"size\":4,\"orderBy\":{\"type\":\"column\",\"columnId\":\"66a9b2d7-1a7d-4e67-a3bd-7d7176240048\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false,\"parentFormat\":{\"id\":\"terms\"},\"include\":[],\"exclude\":[],\"includeIsRegex\":false,\"excludeIsRegex\":false},\"customLabel\":true},\"c0f95e07-3921-4318-8444-3f00d5e3e93d\":{\"label\":\"Cities\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"address.city.keyword\",\"isBucketed\":true,\"params\":{\"size\":3,\"orderBy\":{\"type\":\"column\",\"columnId\":\"66a9b2d7-1a7d-4e67-a3bd-7d7176240048\"},\"orderDirection\":\"desc\",\"otherBucket\":true,\"missingBucket\":false,\"parentFormat\":{\"id\":\"terms\"},\"include\":[],\"exclude\":[],\"includeIsRegex\":false,\"excludeIsRegex\":false},\"customLabel\":true},\"66a9b2d7-1a7d-4e67-a3bd-7d7176240048\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"params\":{\"emptyAsNull\":true}}},\"columnOrder\":[\"93da4c60-d19f-4c12-9fdf-25697a5bd94c\",\"c0f95e07-3921-4318-8444-3f00d5e3e93d\",\"66a9b2d7-1a7d-4e67-a3bd-7d7176240048\"],\"incompleteColumns\":{},\"sampling\":1}}},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{},\"hidePanelTitles\":true},\"title\":\"Countries and cities\"},{\"type\":\"map\",\"gridData\":{\"x\":24,\"y\":10,\"w\":24,\"h\":16,\"i\":\"62f66f36-2a4d-4ee8-94b0-eec1a848c1ab\"},\"panelIndex\":\"62f66f36-2a4d-4ee8-94b0-eec1a848c1ab\",\"embeddableConfig\":{\"mapCenter\":{\"lat\":47.66083,\"lon\":4.8815,\"zoom\":3.65},\"isLayerTOCOpen\":false,\"openTOCDetails\":[],\"enhancements\":{},\"hiddenLayers\":[],\"mapBuffer\":{\"minLon\":-22.5,\"minLat\":21.94305,\"maxLon\":45,\"maxLat\":55.77657},\"attributes\":{\"title\":\"Person Map\",\"description\":\"\",\"mapStateJSON\":\"{\\\"adHocDataViews\\\":[],\\\"zoom\\\":4.45,\\\"center\\\":{\\\"lon\\\":4.8815,\\\"lat\\\":47.66083},\\\"timeFilters\\\":{\\\"from\\\":\\\"1940-01-01T00:00:00.000Z\\\",\\\"to\\\":\\\"2009-12-31T23:00:00.000Z\\\"},\\\"refreshConfig\\\":{\\\"isPaused\\\":false,\\\"interval\\\":0},\\\"query\\\":{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"lucene\\\"},\\\"filters\\\":[],\\\"settings\\\":{\\\"autoFitToDataBounds\\\":true,\\\"backgroundColor\\\":\\\"#ffffff\\\",\\\"customIcons\\\":[],\\\"disableInteractive\\\":false,\\\"disableTooltipControl\\\":false,\\\"hideToolbarOverlay\\\":false,\\\"hideLayerControl\\\":false,\\\"hideViewControl\\\":false,\\\"initialLocation\\\":\\\"AUTO_FIT_TO_BOUNDS\\\",\\\"fixedLocation\\\":{\\\"lat\\\":0,\\\"lon\\\":0,\\\"zoom\\\":2},\\\"browserLocation\\\":{\\\"zoom\\\":2},\\\"keydownScrollZoom\\\":false,\\\"maxZoom\\\":24,\\\"minZoom\\\":0,\\\"showScaleControl\\\":true,\\\"showSpatialFilters\\\":true,\\\"showTimesliderToggleButton\\\":true,\\\"spatialFiltersAlpa\\\":0.3,\\\"spatialFiltersFillColor\\\":\\\"#DA8B45\\\",\\\"spatialFiltersLineColor\\\":\\\"#DA8B45\\\"}}\",\"layerListJSON\":\"[{\\\"sourceDescriptor\\\":{\\\"type\\\":\\\"EMS_TMS\\\",\\\"id\\\":\\\"road_map\\\",\\\"lightModeDefault\\\":\\\"road_map\\\"},\\\"id\\\":\\\"d093292c-333c-45c3-a4f2-de7c54a6520e\\\",\\\"label\\\":null,\\\"minZoom\\\":0,\\\"maxZoom\\\":24,\\\"alpha\\\":1,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"TILE\\\",\\\"properties\\\":{}},\\\"type\\\":\\\"EMS_VECTOR_TILE\\\"},{\\\"sourceDescriptor\\\":{\\\"id\\\":\\\"c4149f7c-9566-4011-8c94-bafbf3e7657e\\\",\\\"type\\\":\\\"ES_SEARCH\\\",\\\"geoField\\\":\\\"address.location\\\",\\\"filterByMapBounds\\\":true,\\\"tooltipProperties\\\":[\\\"name\\\",\\\"dateOfBirth\\\",\\\"children\\\",\\\"address.city\\\"],\\\"applyGlobalQuery\\\":true,\\\"scalingType\\\":\\\"MVT\\\",\\\"applyGlobalTime\\\":true,\\\"applyForceRefresh\\\":true,\\\"sortField\\\":\\\"\\\",\\\"sortOrder\\\":\\\"desc\\\",\\\"topHitsGroupByTimeseries\\\":false,\\\"topHitsSplitField\\\":\\\"\\\",\\\"topHitsSize\\\":1,\\\"indexPatternRefName\\\":\\\"layer_1_source_index_pattern\\\"},\\\"id\\\":\\\"ad8f350b-02e9-4af2-b7e4-feaa7ff35f20\\\",\\\"label\\\":\\\"Women\\\",\\\"minZoom\\\":13,\\\"maxZoom\\\":24,\\\"alpha\\\":0.75,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"VECTOR\\\",\\\"properties\\\":{\\\"icon\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"marker\\\"}},\\\"fillColor\\\":{\\\"type\\\":\\\"DYNAMIC\\\",\\\"options\\\":{\\\"color\\\":\\\"Blues\\\",\\\"field\\\":{\\\"label\\\":\\\"children\\\",\\\"name\\\":\\\"children\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":true,\\\"sigma\\\":3},\\\"type\\\":\\\"ORDINAL\\\",\\\"useCustomColorRamp\\\":true,\\\"customColorRamp\\\":[{\\\"stop\\\":0,\\\"color\\\":\\\"#D36086\\\"}]}},\\\"lineColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"lineWidth\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":1}},\\\"iconSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":10}},\\\"iconOrientation\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"orientation\\\":0}},\\\"labelText\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"\\\"}},\\\"labelColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"}},\\\"labelSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":14}},\\\"labelZoomRange\\\":{\\\"options\\\":{\\\"useLayerZoomRange\\\":true,\\\"minZoom\\\":0,\\\"maxZoom\\\":24}},\\\"labelBorderColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}},\\\"labelPosition\\\":{\\\"options\\\":{\\\"position\\\":\\\"CENTER\\\"}}},\\\"isTimeAware\\\":true},\\\"type\\\":\\\"MVT_VECTOR\\\",\\\"query\\\":{\\\"query\\\":\\\"gender.keyword : \\\\\\\"female\\\\\\\" \\\",\\\"language\\\":\\\"kuery\\\"},\\\"includeInFitToBounds\\\":true,\\\"joins\\\":[],\\\"disableTooltips\\\":false},{\\\"sourceDescriptor\\\":{\\\"id\\\":\\\"ca24888d-f8e8-43b7-a8c4-0179809a6fe6\\\",\\\"type\\\":\\\"ES_SEARCH\\\",\\\"geoField\\\":\\\"address.location\\\",\\\"filterByMapBounds\\\":true,\\\"tooltipProperties\\\":[\\\"name\\\",\\\"dateOfBirth\\\",\\\"children\\\",\\\"address.city\\\"],\\\"applyGlobalQuery\\\":true,\\\"scalingType\\\":\\\"MVT\\\",\\\"indexPatternRefName\\\":\\\"layer_2_source_index_pattern\\\"},\\\"id\\\":\\\"54241d44-4d98-4cdb-b300-65eb5c8f5324\\\",\\\"label\\\":\\\"Men\\\",\\\"minZoom\\\":13,\\\"maxZoom\\\":24,\\\"alpha\\\":0.75,\\\"visible\\\":true,\\\"style\\\":{\\\"type\\\":\\\"VECTOR\\\",\\\"properties\\\":{\\\"icon\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"marker\\\"}},\\\"fillColor\\\":{\\\"type\\\":\\\"DYNAMIC\\\",\\\"options\\\":{\\\"color\\\":\\\"Blues\\\",\\\"field\\\":{\\\"label\\\":\\\"children\\\",\\\"name\\\":\\\"children\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":true,\\\"sigma\\\":3},\\\"type\\\":\\\"ORDINAL\\\",\\\"useCustomColorRamp\\\":false}},\\\"lineColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"lineWidth\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":1}},\\\"iconSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":10}},\\\"iconOrientation\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"orientation\\\":0}},\\\"labelText\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"\\\"}},\\\"labelColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"}},\\\"labelSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":14}},\\\"labelZoomRange\\\":{\\\"options\\\":{\\\"useLayerZoomRange\\\":true,\\\"minZoom\\\":0,\\\"maxZoom\\\":24}},\\\"labelBorderColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}},\\\"labelPosition\\\":{\\\"options\\\":{\\\"position\\\":\\\"CENTER\\\"}}},\\\"isTimeAware\\\":true},\\\"type\\\":\\\"MVT_VECTOR\\\",\\\"query\\\":{\\\"query\\\":\\\"gender.keyword : \\\\\\\"male\\\\\\\" \\\",\\\"language\\\":\\\"kuery\\\"}},{\\\"sourceDescriptor\\\":{\\\"geoField\\\":\\\"address.location\\\",\\\"requestType\\\":\\\"point\\\",\\\"resolution\\\":\\\"FINE\\\",\\\"id\\\":\\\"ec7d2783-f6a9-48f0-9b80-30a867f27e32\\\",\\\"type\\\":\\\"ES_GEO_GRID\\\",\\\"applyGlobalQuery\\\":true,\\\"applyGlobalTime\\\":true,\\\"applyForceRefresh\\\":true,\\\"metrics\\\":[{\\\"type\\\":\\\"count\\\"}],\\\"indexPatternRefName\\\":\\\"layer_3_source_index_pattern\\\"},\\\"style\\\":{\\\"type\\\":\\\"VECTOR\\\",\\\"properties\\\":{\\\"icon\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"value\\\":\\\"marker\\\"}},\\\"fillColor\\\":{\\\"type\\\":\\\"DYNAMIC\\\",\\\"options\\\":{\\\"color\\\":\\\"Green to Red\\\",\\\"colorCategory\\\":\\\"palette_0\\\",\\\"field\\\":{\\\"name\\\":\\\"doc_count\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":true,\\\"sigma\\\":3},\\\"type\\\":\\\"ORDINAL\\\",\\\"useCustomColorRamp\\\":false}},\\\"lineColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFF\\\"}},\\\"lineWidth\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":0}},\\\"iconSize\\\":{\\\"type\\\":\\\"DYNAMIC\\\",\\\"options\\\":{\\\"minSize\\\":8,\\\"maxSize\\\":24,\\\"field\\\":{\\\"name\\\":\\\"doc_count\\\",\\\"origin\\\":\\\"source\\\"},\\\"fieldMetaOptions\\\":{\\\"isEnabled\\\":true,\\\"sigma\\\":3}}},\\\"iconOrientation\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"orientation\\\":0}},\\\"labelText\\\":{\\\"type\\\":\\\"DYNAMIC\\\",\\\"options\\\":{\\\"field\\\":{\\\"name\\\":\\\"doc_count\\\",\\\"origin\\\":\\\"source\\\"}}},\\\"labelColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#000000\\\"}},\\\"labelSize\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"size\\\":14}},\\\"labelZoomRange\\\":{\\\"options\\\":{\\\"useLayerZoomRange\\\":true,\\\"minZoom\\\":0,\\\"maxZoom\\\":24}},\\\"labelBorderColor\\\":{\\\"type\\\":\\\"STATIC\\\",\\\"options\\\":{\\\"color\\\":\\\"#FFFFFF\\\"}},\\\"symbolizeAs\\\":{\\\"options\\\":{\\\"value\\\":\\\"circle\\\"}},\\\"labelBorderSize\\\":{\\\"options\\\":{\\\"size\\\":\\\"SMALL\\\"}}},\\\"isTimeAware\\\":true},\\\"id\\\":\\\"fba7b250-3185-4da6-9e25-0bb8596f46cb\\\",\\\"label\\\":\\\"Cluster\\\",\\\"minZoom\\\":0,\\\"maxZoom\\\":13,\\\"alpha\\\":0.75,\\\"visible\\\":true,\\\"includeInFitToBounds\\\":true,\\\"type\\\":\\\"GEOJSON_VECTOR\\\",\\\"joins\\\":[]}]\",\"uiStateJSON\":\"{\\\"isLayerTOCOpen\\\":true,\\\"openTOCDetails\\\":[\\\"54241d44-4d98-4cdb-b300-65eb5c8f5324\\\",\\\"ad8f350b-02e9-4af2-b7e4-feaa7ff35f20\\\"]}\"},\"hidePanelTitles\":true,\"description\":\"\"},\"title\":\"Person Map\"},{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":26,\"w\":13,\"h\":10,\"i\":\"79a28fbe-38f9-4153-8f79-d322be40e188\"},\"panelIndex\":\"79a28fbe-38f9-4153-8f79-d322be40e188\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsGauge\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-179b99d4-728a-4a88-98e8-0c2a3b369113\"}],\"state\":{\"visualization\":{\"layerId\":\"179b99d4-728a-4a88-98e8-0c2a3b369113\",\"layerType\":\"data\",\"shape\":\"arc\",\"palette\":{\"name\":\"custom\",\"type\":\"palette\",\"params\":{\"steps\":4,\"name\":\"custom\",\"reverse\":false,\"rangeType\":\"percent\",\"rangeMin\":0,\"rangeMax\":null,\"progression\":\"fixed\",\"stops\":[{\"color\":\"#CC564280\",\"stop\":25},{\"color\":\"#E7664C80\",\"stop\":50},{\"color\":\"#54B39980\",\"stop\":75},{\"color\":\"#20928080\",\"stop\":100}],\"colorStops\":[{\"color\":\"#CC564280\",\"stop\":0},{\"color\":\"#E7664C80\",\"stop\":25},{\"color\":\"#54B39980\",\"stop\":50},{\"color\":\"#20928080\",\"stop\":75}],\"continuity\":\"above\",\"maxSteps\":5}},\"ticksPosition\":\"bands\",\"labelMajorMode\":\"auto\",\"metricAccessor\":\"0a227900-7348-4192-b897-757dcfc89aac\",\"goalAccessor\":\"5f0dbf29-2709-4c54-a66f-13a4c07f6f25\",\"minAccessor\":\"4f778f91-1e60-4afa-b5a0-9d966a04b018\",\"maxAccessor\":\"4d268518-b83a-4989-a5d6-64f2238c3731\",\"colorMode\":\"palette\"},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"179b99d4-728a-4a88-98e8-0c2a3b369113\":{\"columns\":{\"0a227900-7348-4192-b897-757dcfc89aac\":{\"label\":\"Cars\",\"dataType\":\"number\",\"operationType\":\"median\",\"sourceField\":\"marketing.cars\",\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"emptyAsNull\":true,\"format\":{\"id\":\"number\",\"params\":{\"decimals\":0,\"compact\":false}}},\"customLabel\":true},\"5f0dbf29-2709-4c54-a66f-13a4c07f6f25\":{\"label\":\"800\",\"dataType\":\"number\",\"operationType\":\"static_value\",\"isStaticValue\":true,\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"value\":\"800\"},\"references\":[],\"customLabel\":true},\"4f778f91-1e60-4afa-b5a0-9d966a04b018\":{\"label\":\"0\",\"dataType\":\"number\",\"operationType\":\"static_value\",\"isStaticValue\":true,\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"value\":\"0\"},\"references\":[],\"customLabel\":true},\"4d268518-b83a-4989-a5d6-64f2238c3731\":{\"label\":\"1500\",\"dataType\":\"number\",\"operationType\":\"static_value\",\"isStaticValue\":true,\"isBucketed\":false,\"scale\":\"ratio\",\"params\":{\"value\":\"1500\"},\"references\":[],\"customLabel\":true}},\"columnOrder\":[\"0a227900-7348-4192-b897-757dcfc89aac\",\"5f0dbf29-2709-4c54-a66f-13a4c07f6f25\",\"4f778f91-1e60-4afa-b5a0-9d966a04b018\",\"4d268518-b83a-4989-a5d6-64f2238c3731\"],\"sampling\":1,\"ignoreGlobalFilters\":false,\"incompleteColumns\":{}}}},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{},\"hidePanelTitles\":true},\"title\":\"Cars (median)\"},{\"type\":\"search\",\"gridData\":{\"x\":13,\"y\":26,\"w\":35,\"h\":20,\"i\":\"ed04bd3e-8619-4a84-b0c5-c46c4ac81e20\"},\"panelIndex\":\"ed04bd3e-8619-4a84-b0c5-c46c4ac81e20\",\"embeddableConfig\":{\"enhancements\":{},\"attributes\":{\"sort\":[[\"_score\",\"desc\"]],\"columns\":[\"name\",\"gender\",\"flag\",\"address.country\",\"address.city\",\"children\"],\"grid\":{\"columns\":{\"flag\":{\"width\":48},\"gender\":{\"width\":94},\"address.country\":{\"width\":106},\"address.city\":{\"width\":107},\"children\":{\"width\":114},\"dateOfBirth\":{\"width\":129}}},\"hideChart\":false,\"isTextBasedQuery\":false,\"kibanaSavedObjectMeta\":{\"searchSourceJSON\":\"{\\\"query\\\":{\\\"query\\\":\\\"\\\",\\\"language\\\":\\\"kuery\\\"},\\\"filter\\\":[],\\\"indexRefName\\\":\\\"kibanaSavedObjectMeta.searchSourceJSON.index\\\"}\"},\"rowHeight\":0,\"headerRowHeight\":0,\"timeRestore\":true,\"timeRange\":{\"from\":\"1940-01-01T00:00:00.000Z\",\"to\":\"2009-12-31T23:00:00.000Z\"},\"refreshInterval\":{\"value\":0,\"pause\":true},\"references\":[{\"name\":\"kibanaSavedObjectMeta.searchSourceJSON.index\",\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\"}]},\"hidePanelTitles\":true,\"description\":\"\"},\"title\":\"Person\"},{\"type\":\"lens\",\"gridData\":{\"x\":0,\"y\":36,\"w\":13,\"h\":10,\"i\":\"54a2911c-15e7-4f49-90f4-2de4c23ba358\"},\"panelIndex\":\"54a2911c-15e7-4f49-90f4-2de4c23ba358\",\"embeddableConfig\":{\"attributes\":{\"title\":\"City tags\",\"description\":\"\",\"visualizationType\":\"lnsTagcloud\",\"type\":\"lens\",\"references\":[{\"type\":\"index-pattern\",\"id\":\"afe9e2f0-5138-11e9-9261-51158e23312b\",\"name\":\"indexpattern-datasource-layer-d3ba75fa-6c4e-4358-9f0e-cf51dba2cd2a\"}],\"state\":{\"visualization\":{\"layerId\":\"d3ba75fa-6c4e-4358-9f0e-cf51dba2cd2a\",\"tagAccessor\":\"85561591-cc86-47e0-9b30-f23b1ecdc5e6\",\"valueAccessor\":\"387c727a-6c3b-4d2e-93bb-083abafd79c6\",\"maxFontSize\":40,\"minFontSize\":8,\"orientation\":\"multiple\",\"showLabel\":false,\"colorMapping\":{\"assignments\":[],\"specialAssignments\":[{\"rule\":{\"type\":\"other\"},\"color\":{\"type\":\"loop\"},\"touched\":true}],\"paletteId\":\"eui_amsterdam_color_blind\",\"colorMode\":{\"type\":\"categorical\"}},\"layerType\":\"data\",\"palette\":{\"type\":\"palette\",\"name\":\"default\"}},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[],\"datasourceStates\":{\"formBased\":{\"layers\":{\"d3ba75fa-6c4e-4358-9f0e-cf51dba2cd2a\":{\"ignoreGlobalFilters\":false,\"columns\":{\"85561591-cc86-47e0-9b30-f23b1ecdc5e6\":{\"label\":\"Cities\",\"dataType\":\"string\",\"operationType\":\"terms\",\"scale\":\"ordinal\",\"sourceField\":\"address.city.keyword\",\"isBucketed\":true,\"params\":{\"size\":20,\"orderBy\":{\"type\":\"column\",\"columnId\":\"387c727a-6c3b-4d2e-93bb-083abafd79c6\"},\"orderDirection\":\"desc\",\"otherBucket\":false,\"missingBucket\":false,\"parentFormat\":{\"id\":\"terms\"},\"include\":[],\"exclude\":[],\"includeIsRegex\":false,\"excludeIsRegex\":false},\"customLabel\":true},\"387c727a-6c3b-4d2e-93bb-083abafd79c6\":{\"label\":\"Count\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\",\"params\":{\"emptyAsNull\":true},\"customLabel\":true}},\"columnOrder\":[\"85561591-cc86-47e0-9b30-f23b1ecdc5e6\",\"387c727a-6c3b-4d2e-93bb-083abafd79c6\"],\"incompleteColumns\":{}}}},\"indexpattern\":{\"layers\":{}},\"textBased\":{\"layers\":{}}},\"internalReferences\":[],\"adHocDataViews\":{}}},\"enhancements\":{},\"description\":\"\"},\"title\":\"Cities\"}]","refreshInterval":{"pause":true,"value":0},"timeFrom":"1940-01-01T00:00:00.000Z","timeRestore":true,"timeTo":"2009-12-31T23:00:00.000Z","title":"Persons dataset","version":2},"coreMigrationVersion":"8.8.0","created_at":"2024-06-12T08:47:28.190Z","created_by":"u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0","id":"1c5d2ee0-513b-11e9-9261-51158e23312b","managed":false,"references":[{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"9:indexpattern-datasource-layer-0ca9c871-0038-4696-b67f-5e3dbfa2fde2","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"6749adb3-79e6-42f6-96cb-0415dbcb557a:indexpattern-datasource-layer-05fe61bc-817f-42bd-b26b-fe65d6b87085","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"6749adb3-79e6-42f6-96cb-0415dbcb557a:indexpattern-datasource-layer-304df1c4-93b2-4102-850d-303e43e9d48b","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"6749adb3-79e6-42f6-96cb-0415dbcb557a:indexpattern-datasource-layer-bdf7d430-2d74-4e7d-82b3-c399c4bd2710","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"6749adb3-79e6-42f6-96cb-0415dbcb557a:xy-visualization-layer-13080801-f3ee-4771-bb3a-04e4eda61c99","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"82210c1a-47ec-4cdc-82fb-095150116ae7:indexpattern-datasource-layer-f8de930d-49a1-4b8f-940d-4134b8defd92","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"62f66f36-2a4d-4ee8-94b0-eec1a848c1ab:layer_1_source_index_pattern","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"62f66f36-2a4d-4ee8-94b0-eec1a848c1ab:layer_2_source_index_pattern","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"62f66f36-2a4d-4ee8-94b0-eec1a848c1ab:layer_3_source_index_pattern","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"79a28fbe-38f9-4153-8f79-d322be40e188:indexpattern-datasource-layer-179b99d4-728a-4a88-98e8-0c2a3b369113","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"ed04bd3e-8619-4a84-b0c5-c46c4ac81e20:kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"afe9e2f0-5138-11e9-9261-51158e23312b","name":"54a2911c-15e7-4f49-90f4-2de4c23ba358:indexpattern-datasource-layer-d3ba75fa-6c4e-4358-9f0e-cf51dba2cd2a","type":"index-pattern"},{"id":"da2dff2c-34e2-4067-8b8f-e75c217afb40","name":"tag-ref-da2dff2c-34e2-4067-8b8f-e75c217afb40","type":"tag"}],"type":"dashboard","typeMigrationVersion":"10.2.0","updated_at":"2024-06-12T19:34:01.689Z","version":"WzEyMywxXQ=="} 4 | {"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":3,"missingRefCount":0,"missingReferences":[]} -------------------------------------------------------------------------------- /src/main/resources/static/kibana/kibana.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |

Kibana

8 |
9 |
10 | Open Kibana 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 |

kibana.ndjson

19 |
20 |
21 | kibana.ndjson. You can import it from 22 | Saved Objects page. 23 |
24 |
25 |
26 | 27 |
28 | 29 |
30 |
31 |

console.txt

32 |
33 |
34 | console.txt. You can load it directly to the 35 | Dev 36 | Console. 37 |
38 |
39 |
40 | 41 |
42 | 43 |
44 | -------------------------------------------------------------------------------- /src/main/resources/static/person-detail/person-detail.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `personDetail` component, along with its associated controller and template 4 | angular. 5 | module('personDetail'). 6 | component('personDetail', { 7 | templateUrl: 'person-detail/person-detail.template.html', 8 | controller: ['$http', '$routeParams', '$location', 9 | function PersonDetailController($http, $routeParams, $location) { 10 | var self = this; 11 | 12 | $http.get('/api/1/person/'+ $routeParams.id).then(function(response) { 13 | console.log(response.data); 14 | self.person = response.data; 15 | }); 16 | 17 | self.save = function() { 18 | $http.put('/api/1/person/'+ $routeParams.id , self.person) 19 | .then(function() { console.log( self.person ); }); 20 | }; 21 | 22 | self.delete = function() { 23 | $http.delete('/api/1/person/'+ $routeParams.id) 24 | .then(function() { $location.path('/'); }); 25 | }; 26 | } 27 | ] 28 | }); 29 | -------------------------------------------------------------------------------- /src/main/resources/static/person-detail/person-detail.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the personDetail module 4 | angular.module('personDetail', [ 5 | 'ngRoute' 6 | ]); 7 | -------------------------------------------------------------------------------- /src/main/resources/static/person-detail/person-detail.template.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 |

Person Information

9 |
10 |
11 |
12 |
13 | 14 | 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 | 36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |

Marketing data

44 |
45 |
46 |
47 |
48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 |
62 | 63 | 64 |
65 |
66 | 67 | 68 |
69 |
70 | 71 | 72 |
73 |
74 |
75 |
76 | 77 | 78 |
79 |
80 | 81 | 82 |
83 |
84 | 85 | 86 |
87 |
88 |
89 |
90 | 91 | 92 | 93 |
94 |
-------------------------------------------------------------------------------- /src/main/resources/static/search/search.component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Register `search` component, along with its associated controller and template 4 | angular. 5 | module('search'). 6 | component('search', { 7 | templateUrl: 'search/search.template.html', 8 | controller: ['$http', function SearchController($http) { 9 | var self = this; 10 | self.query = ""; 11 | self.f_date = ""; 12 | self.f_country = ""; 13 | self.currentPage = 1; 14 | self.totalItems = 0; 15 | 16 | self.search = function(page) { 17 | self.currentPage = page; 18 | $http({method: 'GET', url: '/api/1/person/_search?size=10&q='+ self.query 19 | + '&f_date=' + self.f_date + '&f_country=' + self.f_country + '&from=' + (page-1)*10 }) 20 | .then(function successCallback(response) { 21 | self.error = null; 22 | self.result = response.data; 23 | self.totalItems = self.result.hits.total.value; 24 | // Group data every 10 years (facets don't support it yet) 25 | self.dates = new Array(); 26 | 27 | // If we have aggs, compute (for future use) 28 | if (self.result.aggregations) { 29 | var buckets = self.result.aggregations['date_histogram#by_year'].buckets; 30 | 31 | var i = -1; 32 | for (var idx in buckets) { 33 | var year = buckets[idx].key_as_string; 34 | var docs = buckets[idx].doc_count; 35 | var subyear = year.substr(0,3); 36 | 37 | if (i == -1 || subyear != self.dates[i].key) { 38 | i++; 39 | self.dates[i] = {}; 40 | self.dates[i].key = subyear; 41 | self.dates[i].docs = docs; 42 | } else { 43 | self.dates[i].docs += docs; 44 | } 45 | } 46 | } 47 | }, function errorCallback(response) { 48 | self.error = "Backend not available"; 49 | }); 50 | }; 51 | 52 | self.addFilterCountry = function(bucket) { 53 | console.log(bucket.key); 54 | self.f_country = bucket.key; 55 | self.search(1); 56 | }; 57 | 58 | self.addFilterDate = function(bucket) { 59 | console.log(bucket.key+"0"); 60 | self.f_date = bucket.key+"0"; 61 | self.search(1); 62 | }; 63 | 64 | self.changePage = function() { 65 | self.search(self.currentPage); 66 | }; 67 | 68 | self.search(1); 69 | }] 70 | }); 71 | -------------------------------------------------------------------------------- /src/main/resources/static/search/search.module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Define the `search` module 4 | angular.module('search', []); 5 | -------------------------------------------------------------------------------- /src/main/resources/static/search/search.template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 | 9 | 10 |
11 |
12 |
13 | 14 |
15 |
16 |

17 | Found {{$ctrl.result.hits.total.value}} hits in 18 | {{$ctrl.result.took}} ms 19 | {{$ctrl.f_country}} 20 | {{$ctrl.f_date}} 21 |

22 |

23 | {{$ctrl.error}} 24 |

25 |
26 |
27 | 28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
CountryCount
{{bucket.key}}{{bucket.doc_count}}
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
{{bucket.key}}0
{{bucket.docs}}
59 |
60 |
61 | 62 |
63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
NameGenderDate Of BirthCountryCityScore
{{entry._source.name}}{{entry._source.gender}}{{entry._source.dateOfBirth}}{{entry._source.address.country}}{{entry._source.address.city}}{{entry._score}}
87 |
88 |
89 | 90 |
91 |
92 | 99 |
100 |
101 | 102 |
103 | --------------------------------------------------------------------------------