Introduction
I am currently working on a project to achieve a bulk insertion in a Couchbase database.
For this, I chose Quarkus Reactive to get data from the client. And I used a Flux to insert all the data to database.
For each data inserted, I send a response, example of a response:
[
{
"data": {
"id": "1"
"created": "2022-02-22T00:28:34Z",
"lastName": "DUPOND",
"firstName": "Marianne",
"gender": "FEMALE",
"title": "MRS",
"addresses": [
{
"street1": "rue du Testeur",
"zipCode": "34000",
"city": "MONTPELLIER",
"country": "France",
"typeKey": "PERSONAL"
}
],
"phones": [
{
"number": "0610529856",
"typeKey": "PERSONAL"
}
],
"emails": [
{
"email": "marianne.dupond#test.com",
"typeKey": "PERSONAL"
}
],
"birthday": "1987-11-13",
"languages": [
"FR",
"EN"
],
"socialNetworks": [
{
"socialNetworkId": "marianne.dupond#twitter",
"typeKey": "PERSONAL"
}
],
"customFields": [
{
"customFieldKey": "MAIDEN_NAME",
"value": "DURAND"
}
]
},
"result": "201",
"id":"1"
},
{
"data": {
"id" : "2"
"created": "2022-02-22T00:28:34Z",
"lastName": "BOND",
"firstName": "James",
"gender": "MALE",
"title": "MR",
"addresses": [
{
"street1": "boulevard des services secrets",
"zipCode": "10000",
"city": "LONDRES",
"country": "Royaume-Uni",
"typeKey": "PERSONAL"
}
],
"phones": [
{
"number": "0600700700",
"typeKey": "PERSONAL"
}
],
"emails": [
{
"email": "james.bond#test.com",
"typeKey": "PERSONAL"
}
],
"birthday": "1950-07-07",
"languages": [
"EN"
],
"socialNetworks": [
{
"socialNetworkId": "james.bond#facebook",
"typeKey": "PERSONAL"
}
],
"customFields": [
{
"customFieldKey": "VIP",
"value": "1"
}
]
},
"result": "201",
"id": "2"
}
]
Problematic
But currently the returned response is:
[
{
"data": {
"id": "1"
"created": "2022-02-22T00:37:39Z",
"lastName": "DUPOND",
"firstName": "Marianne",
"gender": "FEMALE",
"title": "MRS",
"addresses": [
{
"street1": "rue du Testeur",
"zipCode": "34000",
"city": "MONTPELLIER",
"country": "France",
"typeKey": "PERSONAL"
}
],
"phones": [
{
"number": "0610529856",
"typeKey": "PERSONAL"
}
],
"emails": [
{
"email": "marianne.dupond#test.com",
"typeKey": "PERSONAL"
}
],
"birthday": "1987-11-13",
"languages": [
"FR",
"EN"
],
"socialNetworks": [
{
"socialNetworkId": "marianne.dupond#twitter",
"typeKey": "PERSONAL"
}
],
"customFields": [
{
"customFieldKey": "MAIDEN_NAME",
"value": "DURAND"
}
]
},
"result": "201"
},
{
"data": {
"created": "2022-02-22T00:37:39Z",
"lastName": "BOND",
"firstName": "James",
"gender": "MALE",
"title": "MR",
"addresses": [
{
"street1": "boulevard des services secrets",
"zipCode": "10000",
"city": "LONDRES",
"country": "Royaume-Uni",
"typeKey": "PERSONAL"
}
],
"phones": [
{
"number": "0600700700",
"typeKey": "PERSONAL"
}
],
"emails": [
{
"email": "james.bond#test.com",
"typeKey": "PERSONAL"
}
],
"birthday": "1950-07-07",
"languages": [
"EN"
],
"socialNetworks": [
{
"socialNetworkId": "james.bond#facebook",
"typeKey": "PERSONAL"
}
],
"customFields": [
{
"customFieldKey": "VIP",
"value": "1"
}
]
},
"result": "201"
}
]
The id field inside the data and the response field do not appear, and sometimes the id field inside the data of the first element appears, sometimes not.
Affected code
Bulk insertion(flux of single insertion) code:
public Flux<SingleResponseBulk> bulkInsertCustomerProfile(Multi<SingleResponseBulk> singleResponses) {
return Flux.from(singleResponses)
.doOnEach(signal -> {
if (signal.hasValue()) {
SingleResponseBulk singleResponseBulk = Objects.requireNonNull(signal.get());
insertCustomerProfileWithSingleResponseBulk(singleResponseBulk);
}
});
}
private void insertCustomerProfileWithSingleResponseBulk(SingleResponseBulk response) {
try {
insertCustomerProfile(response.getData()).subscribe();
response.setId(response.getData().getId());
response.setResult("201");
} catch (CouchbaseException e) {
response.setResult("400");
response.setError(e.getMessage());
}
}
Single insertion code:
public Mono<CustomerProfile> insertCustomerProfile(CustomerProfile customerProfile) {
customerProfile.setCreated(LocalDateTime.now());
return getNextId().flatMap(counterResult -> {
String id = String.valueOf(counterResult.content());
customerProfile.setId(id);
String key = getDocumentKey(id);
return collection.insert(key, customerProfile)
.flatMap(result -> {
customerProfile.setCas(result.cas());
return Mono.just(customerProfile);
})
.doOnError(CouchbaseException.class, mapCouchbaseExceptionConsumer(id));
});
}
Id generation code:
private Mono<CounterResult> getNextId() {
return collection.binary()
.increment("counter" + COLLECTION_DELIMITER + CUSTOMER_PROFILE_COLLECTION)
.doOnError(CouchbaseException.class, error -> {
String errorMessage = "An exception occurred during id generation.";
throw new RepositoryException(errorMessage, error);
});
}
Question
So I was wondering if it would be possible to solve the problem. If so, how can we do it?
Related
The response body is
{
"name": "1",
"widgets": {
"ijklmnop-abcd-1234-91f0-f51f1825b788": {
"id": "ijklmnop-abcd-1234-91f0-f51f1825b788",
"width": 1760,
"content": {
"params": {
"data": [
0
],
"aggregations": [
],
"valueTypes": [
"none"
]
}
}
},
"abcdefgh-5678-90ds-ae27-abae62385a55": {
"id": "abcdefgh-5678-90ds-ae27-abae62385a55",
"width": 1240,
"content": {
"params": {
"data": 1,
"values": [],
"timestamps": []
}
}
}
}
}
Here I want to extract as well as check id and other parameters inside abcdefgh-5678-90ds-ae27-abae62385a55(uuid) in the body?
I'm trying to add some tests with Rest-Assured to my application, but I can't figure out how to assert some nested values. The error message is :
Expected: (a collection containing "json")
Actual: [[json, spring, gulp, path etc...]]
Here is the code :
when().
get("/api/personsByID/{id}/{count}", 262, 2).
then().
statusCode(200).
body("personDependencies.name", hasItems("json"));
And here is the JSON file that is returned by rest controller:
[
{
"id": 346,
"verified": true,
"displayName": "eda656a2c3cb59ae840e40a28ba4ab50bfb9de0185abcb901c6af6dc59d6668f",
"emails": [
{
"email": "16a23f2e5477df0bbcad718c3abc235b2cb8a1b6648d14f58d42a7be13df2b6e"
}
],
"personDependencies": [
{
"name": "json"
},
{
"name": "spring"
},
{
"name": "gulp"
},
{
"name": "path"
},
{
"name": "junit"
},
{
"name": "activemq"
},
{
"name": "hibernate"
},
{
"name": "jstl"
},
{
"name": "phantomjs"
},
{
"name": "activiti"
},
{
"name": "commons"
},
{
"name": "h2"
},
{
"name": "joda"
},
{
"name": "log4j"
},
{
"name": "exec"
},
{
"name": "admin"
},
{
"name": "coveralls"
},
{
"name": "cxf"
},
{
"name": "cglib"
},
{
"name": "camel"
},
{
"name": "sugaronrest"
},
{
"name": "tslint"
},
{
"name": "httpclient"
},
{
"name": "guava"
},
{
"name": "inventory"
},
{
"name": "jackson"
},
{
"name": "gson"
},
{
"name": "event"
},
{
"name": "OTRS"
},
{
"name": "maven"
},
{
"name": "karma"
},
{
"name": "slf4j"
},
{
"name": "postgresql"
},
{
"name": "typescript"
},
{
"name": "jasmine"
},
{
"name": "spa"
},
{
"name": "javax.servlet"
}
],
"countries": [],
"member_of": [],
"projects": [],
"employee_type": [],
"languages": [
{
"language": "reStructuredText",
"sum": 575
},
{
"language": "JSON",
"sum": 21
},
{
"language": "JavaScript",
"sum": 4467
},
{
"language": "Java",
"sum": 7958
},
{
"language": "Python",
"sum": 2
},
{
"language": "XML",
"sum": 477
},
{
"language": "Plain Text",
"sum": 41
}
],
"distance": 0.6028837702084446
}
]
I have no idea how to make proper assertions, any help would be great. Thanks!
If I am reading your question right you need to check if a certain values are present in a list that is returned for a particular ID
The below should work for you
given().when().get().then().body("find {it.id == 346}.personDependencies.name", hasItems("json", "jackson"));
The first problem you don't need to check the presence of an item with hasItems, you should use hasItem
when().
get("/api/personsByID/{id}/{count}", 262, 2).
then().
statusCode(200).
body("personDependencies.name", hasItem("json"));
Then if you need to add more message to the assertion when the test fails you can do such way:
when().
get("/api/personsByID/{id}/{count}", 262, 2).
then().
statusCode(200).
body("personDependencies.name", describedAs("Array not containing the provided item",hasItem("json")));
In your case you can validate such a way:
when().
get("/api/personsByID/{id}/{count}", 262, 2).
then().
statusCode(200).
body("personDependencies[*].name", hasItem("json"));
I am trying to get the current status of a flight from a 3rd party API. I just need very few fields from the JSON. If I do it in the traditional way I would have to create many classes. And, map the json to the classes. Is there a better way of doing it. In the JSON response,I just need the information in the FlightStatuses field.
{
"request": {
"airline": {
"fsCode": "AA",
"requestedCode": "AA"
},
"flight": {
"requested": "100",
"interpreted": "100"
},
"utc": {
"requested": "false",
"interpreted": false
},
"url": "https://api.flightstats.com/flex/flightstatus/rest/v2/json/flight/status/AA/100/dep/2019/10/1?utc=false",
"nonstopOnly": {
"interpreted": false
},
"date": {
"year": "2019",
"month": "10",
"day": "1",
"interpreted": "2019-10-01"
}
},
"appendix": {
"airlines": [
{
"fs": "AA",
"iata": "AA",
"icao": "AAL",
"name": "American Airlines",
"phoneNumber": "08457-567-567",
"active": true
},
{
"fs": "AY",
"iata": "AY",
"icao": "FIN",
"name": "Finnair",
"phoneNumber": "+ 358 600 140 140",
"active": true
},
{
"fs": "IB",
"iata": "IB",
"icao": "IBE",
"name": "Iberia",
"phoneNumber": "1800 772 4642",
"active": true
},
{
"fs": "LY",
"iata": "LY",
"icao": "ELY",
"name": "El Al",
"phoneNumber": "+ 972-3-9771111",
"active": true
},
{
"fs": "BA",
"iata": "BA",
"icao": "BAW",
"name": "British Airways",
"phoneNumber": "1-800-AIRWAYS",
"active": true
},
{
"fs": "GF",
"iata": "GF",
"icao": "GFA",
"name": "Gulf Air",
"phoneNumber": "973 17 335 777",
"active": true
}
],
"airports": [
{
"fs": "LHR",
"iata": "LHR",
"icao": "EGLL",
"faa": "",
"name": "London Heathrow Airport",
"city": "London",
"cityCode": "LON",
"stateCode": "EN",
"countryCode": "GB",
"countryName": "United Kingdom",
"regionName": "Europe",
"timeZoneRegionName": "Europe/London",
"weatherZone": "",
"localTime": "2019-10-01T15:25:25.492",
"utcOffsetHours": 1.0,
"latitude": 51.469603,
"longitude": -0.453566,
"elevationFeet": 80,
"classification": 1,
"active": true,
"weatherUrl": "https://api.flightstats.com/flex/weather/rest/v1/json/all/LHR?codeType=fs",
"delayIndexUrl": "https://api.flightstats.com/flex/delayindex/rest/v1/json/airports/LHR?codeType=fs"
},
{
"fs": "JFK",
"iata": "JFK",
"icao": "KJFK",
"faa": "JFK",
"name": "John F. Kennedy International Airport",
"street1": "JFK Airport",
"city": "New York",
"cityCode": "NYC",
"stateCode": "NY",
"postalCode": "11430",
"countryCode": "US",
"countryName": "United States",
"regionName": "North America",
"timeZoneRegionName": "America/New_York",
"weatherZone": "NYZ178",
"localTime": "2019-10-01T10:25:25.493",
"utcOffsetHours": -4.0,
"latitude": 40.642335,
"longitude": -73.78817,
"elevationFeet": 13,
"classification": 1,
"active": true,
"weatherUrl": "https://api.flightstats.com/flex/weather/rest/v1/json/all/JFK?codeType=fs",
"delayIndexUrl": "https://api.flightstats.com/flex/delayindex/rest/v1/json/airports/JFK?codeType=fs"
}
],
"equipments": [
{
"iata": "77W",
"name": "Boeing 777-300ER",
"turboProp": false,
"jet": true,
"widebody": true,
"regional": false
}
]
},
"flightStatuses": [
{
"flightId": 1016157813,
"carrierFsCode": "AA",
"flightNumber": "100",
"departureAirportFsCode": "JFK",
"arrivalAirportFsCode": "LHR",
"departureDate": {
"dateUtc": "2019-10-01T22:15:00.000Z",
"dateLocal": "2019-10-01T18:15:00.000"
},
"arrivalDate": {
"dateUtc": "2019-10-02T05:20:00.000Z",
"dateLocal": "2019-10-02T06:20:00.000"
},
"status": "S",
"schedule": {
"flightType": "J",
"serviceClasses": "RFJY",
"restrictions": "",
"uplines": [],
"downlines": []
},
"operationalTimes": {
"publishedDeparture": {
"dateUtc": "2019-10-01T22:15:00.000Z",
"dateLocal": "2019-10-01T18:15:00.000"
},
"scheduledGateDeparture": {
"dateUtc": "2019-10-01T22:15:00.000Z",
"dateLocal": "2019-10-01T18:15:00.000"
},
"estimatedGateDeparture": {
"dateUtc": "2019-10-01T22:15:00.000Z",
"dateLocal": "2019-10-01T18:15:00.000"
},
"publishedArrival": {
"dateUtc": "2019-10-02T05:20:00.000Z",
"dateLocal": "2019-10-02T06:20:00.000"
},
"scheduledGateArrival": {
"dateUtc": "2019-10-02T05:20:00.000Z",
"dateLocal": "2019-10-02T06:20:00.000"
},
"estimatedGateArrival": {
"dateUtc": "2019-10-02T05:20:00.000Z",
"dateLocal": "2019-10-02T06:20:00.000"
}
},
"codeshares": [
{
"fsCode": "AY",
"flightNumber": "4012",
"relationship": "L"
},
{
"fsCode": "BA",
"flightNumber": "1511",
"relationship": "L"
},
{
"fsCode": "GF",
"flightNumber": "6654",
"relationship": "L"
},
{
"fsCode": "IB",
"flightNumber": "4218",
"relationship": "L"
},
{
"fsCode": "LY",
"flightNumber": "8051",
"relationship": "L"
}
],
"delays": {},
"flightDurations": {
"scheduledBlockMinutes": 425
},
"airportResources": {
"departureTerminal": "8",
"departureGate": "16",
"arrivalTerminal": "3"
},
"flightEquipment": {
"scheduledEquipmentIataCode": "77W",
"actualEquipmentIataCode": "77W",
"tailNumber": "N730AN"
}
}
]
}
You can parse your JSON as JsonNode.
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree("[your JSON here]");
After that get only fields, which you need. For example:
jsonNode.get("request").get("airline").get("requestedCode").asText()
Google's Gson could be used to get Json's Object, Array and other Wrapper class from Object.
Gson gson = new Gson();
JsonElement jsonElement = gson.toJsonTree(flightObject);
JsonArray jsonArray = jsonElement.getAsJsonObject().get("flightStatuses").getAsJsonArray();
System.out.println(gson.toJson(jsonArray));
You can just use an object with flightStatuses and ignore the rest essentially;
public class FlightStatusContainer {
private List<FlightStatus> flightStatuses;
// getter & setters
}
If you need only some parts of each FlightStatus object, you can ignore those fields as well in that object. For example let's say that you only need status;
public class FlightStatus {
private String status;
// getter & setters
}
and use these in your 3rd party API call (e.g. with RestTemplate);
FlightStatusContainer fsc = restTemplate.getForObject(targetUrl, FlightStatusContainer.class);
// can use fsc.getFlightStatuses(), and the rest is ignored
will get you a super simplified result;
{
"flightStatuses": [
{
"status": "S"
},
{
"status": "P"
}
]
}
Below is the original response:
{
"emails": [
{
"type": "work",
"value": "bjensen#example.com"
}
],
"id": "2819c223-7f76-453a-919d-413861904646",
"phoneNumbers": [
{
"type": "work",
"value": "555-555-8377"
},
{
"type": "business",
"value": "555-555-8377"
}
],
"schemas": [
"urn:scim:schemas:core:1.0"
],
"userName": "bjensen"
}
And in the above response I would pass excludedAttributes=phoneNumbers.type and the response should be like below:
{
"emails": [
{
"type": "work",
"value": "bjensen#example.com"
}
],
"id": "2819c223-7f76-453a-919d-413861904646",
"phoneNumbers": [
{
"value": "555-555-8377"
},
{
"value": "555-555-8377"
}
],
"schemas": [
"urn:scim:schemas:core:1.0"
],
"userName": "bjensen"
}
The ES index consists of 2 types that are implicitly mapped (default mapping). One type is "person" or an author, the 2nd type is "document".
The index has some 500k entries.
What I have to do is: implement an autocomplete (suggestions) functionality where only the fields "title", "classification" (document) and "name" (author) are relevant for the suggestions shown to the user.
Could it be done without changing the 500k docs in the index?
I found some tutorials that suggest preparing a specific mapping and also altering the documents (this I want to avoid if possible) and so on but I am new to this and I am not sure how to go about the this problem?
Below is the JSON for the index, and how the documents look:
//a Document
{
"rawsource": "Phys.Rev. D67 (2003) 084031",
"pubyear": 2003,
"citedFrom": 19,
"topics": [
{
"name": "General Relativity and Quantum Cosmology"
}
],
"cited": [
{
"ref": 0,
"id": "PN132433"
},
{
"ref": 1,
"id": "PN206900"
}
],
"id": "PN120001",
"collection": "PN",
"source": "Phys Rev D",
"classification": "Physics",
"title": "Observables in causal set cosmology",
"url": "http://arxiv.org/abs/gr-qc/0210061",
"authors": [
{
"name": "Brightwell, Graham"
},
{
"name": "Dowker, H. Fay"
},
{
"name": "Garcia, Raquel S."
},
{
"name": "Henson, Joe"
},
{
"name": "Sorkin, Rafael D."
}
]
}
//a Person (author)
{
"name": "Terasawa, M.",
"documents": [
{
"citedFrom": 0,
"id": "PN039187"
}
],
"coAuthors": [
{
"name": "Famiano, M. A.",
"count": "1"
},
{
"name": "Boyd, R. N.",
"count": "1"
}
],
"topics": [
{
"name": "Astrophysics",
"count": "1"
}
]
}
//the mapping (implicit/default)
{
"dlsnew": {
"aliases": {
},
"mappings": {
"person": {
"properties": {
"coAuthors": {
"properties": {
"count": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"documents": {
"properties": {
"citedFrom": {
"type": "long"
},
"id": {
"type": "string"
}
}
},
"name": {
"type": "string"
},
"referenced": {
"properties": {
"count": {
"type": "string"
},
"id": {
"type": "string"
}
}
},
"topics": {
"properties": {
"count": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
},
"document": {
"properties": {
"abstract": {
"type": "string"
},
"authors": {
"properties": {
"name": {
"type": "string"
}
}
},
"cited": {
"properties": {
"id": {
"type": "string"
},
"ref": {
"type": "long"
}
}
},
"citedFrom": {
"type": "long"
},
"classification": {
"type": "string"
},
"collection": {
"type": "string"
},
"id": {
"type": "string"
},
"pubyear": {
"type": "long"
},
"rawsource": {
"type": "string"
},
"source": {
"type": "string"
},
"title": {
"type": "string"
},
"topics": {
"properties": {
"name": {
"type": "string"
}
}
},
"url": {
"type": "string"
}
}
}
},
"settings": {
"index": {
"creation_date": "1454247029258",
"number_of_shards": "5",
"uuid": "k_CyQaxwSAaae67wW98HyQ",
"version": {
"created": "1050299"
},
"number_of_replicas": "1"
}
},
"warmers": {
}
}
}
The implementation is to be done using JAVA and the Vaadin Framework (this is not relevant at this point, but examples in Java/Vaadin will be most welcomed).
Thanks.
So, I think I solved my problem on the Elasticsearch side or at least to a good enough extend for me and the task at hand. I followed this ruby example.
I had to re-index all documents to accommodate the new settings for my index and to change my mapping explicitly.
They key is in defining proper analyzers and an edgeNGram filter in this case, like so:
"settings": {
"index": {
"analysis": {
"filter": {
"def_ngram_filter": {
"min_gram": "1",
"side": "front",
"type": "edgeNGram",
"max_gram": "16"
}
},
"analyzer": {
"def_search_analyzer": {
"filter": [
"lowercase",
"asciifolding"
],
"type": "custom",
"tokenizer": "def_tokenizer"
},
"def_ngram_analyzer": {
"filter": [
"lowercase",
"asciifolding",
"def_ngram_filter"
],
"type": "custom",
"tokenizer": "def_tokenizer"
},
"def_shingle_analyzer": {
"filter": [
"shingle",
"lowercase",
"asciifolding"
],
"type": "custom",
"tokenizer": "def_tokenizer"
},
"def_default_analyzer": {
"filter": [
"lowercase",
"asciifolding"
],
"type": "custom",
"tokenizer": "def_tokenizer"
}
},
"tokenizer": {
"def_tokenizer": {
"type": "whitespace"
}
}
}
}
}
and the use these in the mapping for the fields to be searched, like so:
"mappings": {
"person": {
"properties": {
"coAuthors": {
"properties": {
"count": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"documents": {
"properties": {
"citedFrom": {
"type": "long"
},
"id": {
"type": "string"
}
}
},
"name": {
"type": "string",
"analyzer": "def_default_analyzer",
"fields": {
"ngrams": {
"type": "string",
"index_analyzer": "def_ngram_analyzer",
"search_analyzer": "def_search_analyzer"
},
"shingles": {
"type": "string",
"analyzer": "def_shingle_analyzer"
},
"stemmed": {
"type": "string",
"analyzer": "def_snowball_analyzer"
}
}
},
"referenced": {
"properties": {
"count": {
"type": "string"
},
"id": {
"type": "string"
}
}
},
"topics": {
"properties": {
"count": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
},
"document": {
"properties": {
"abstract": {
"type": "string"
},
"authors": {
"properties": {
"name": {
"type": "string",
"analyzer": "def_default_analyzer",
"fields": {
"ngrams": {
"type": "string",
"index_analyzer": "def_ngram_analyzer",
"search_analyzer": "def_search_analyzer"
},
"shingles": {
"type": "string",
"analyzer": "def_shingle_analyzer"
},
"stemmed": {
"type": "string",
"analyzer": "def_snowball_analyzer"
}
}
}
}
},
"cited": {
"properties": {
"id": {
"type": "string"
},
"ref": {
"type": "long"
}
}
},
"citedFrom": {
"type": "long"
},
"classification": {
"type": "string"
},
"collection": {
"type": "string"
},
"id": {
"type": "string"
},
"pubyear": {
"type": "long"
},
"rawsource": {
"type": "string"
},
"source": {
"type": "string"
},
"title": {
"type": "string",
"analyzer": "def_default_analyzer",
"fields": {
"ngrams": {
"type": "string",
"index_analyzer": "def_ngram_analyzer",
"search_analyzer": "def_search_analyzer"
},
"shingles": {
"type": "string",
"analyzer": "def_shingle_analyzer"
},
"stemmed": {
"type": "string",
"analyzer": "def_snowball_analyzer"
}
}
},
"topics": {
"properties": {
"name": {
"type": "string",
"analyzer": "def_default_analyzer",
"fields": {
"ngrams": {
"type": "string",
"index_analyzer": "def_ngram_analyzer",
"search_analyzer": "def_search_analyzer"
},
"shingles": {
"type": "string",
"analyzer": "def_shingle_analyzer"
},
"stemmed": {
"type": "string",
"analyzer": "def_snowball_analyzer"
}
}
}
}
},
"url": {
"type": "string"
}
}
}
}
then querying the index with the following works as expected:
curl -XGET "http://localhost:9200/_search " -d'
{
"size": 5,
"query": {
"multi_match": {
"query": "physics",
"type": "most_fields",
"fields": [
"document.title^10",
"document.title.shingles^2",
"document.title.ngrams",
"person.name^10",
"person.name.shingles^2",
"person.name.ngrams",
"document.topics.name^10",
"document.topics.name.shingles^2",
"document.topics.name.ngrams"
],
"operator": "and"
}
}
}'
Hope this will help someone, it is probably not the best example as I am a complete noob to this, but it worked for me.
There exist different Autocomplete components for Vaadin.
Have a look at this link.
Depending on which Add-On you choose, the databinding is done differently, but you have to "connect" it to your index.