Elastic Search Parent-Child Data Search Java API

2020-06-23 07:59发布

I am new to ELastic Search.

Data in Elastic search is in Parent-Child Model.I want to perform search in this data using java api.

parent type contains author details and child type contains book details like book name,book publisher, book category.

While performing a search on child details,I need to get the parent details also and vice versa. Sometimes search conditions will be on parent type as well as child. eg search for books written by author1 and type Fiction.

How can i implement this in java? I have referred the elastic search documentation but not able to get a solution

Please help

3条回答
虎瘦雄心在
2楼-- · 2020-06-23 08:12

I've done something similar with "spring-data-elasticsearch" library. There are heaps of samples available on their test suite.

Follow this link on git : https://github.com/spring-projects/spring-data-elasticsearch/blob/master/src/test/java/org/springframework/data/elasticsearch/NestedObjectTests.java

    List<Car> cars = new ArrayList<Car>();

    Car saturn = new Car();
    saturn.setName("Saturn");
    saturn.setModel("SL");

    Car subaru = new Car();
    subaru.setName("Subaru");
    subaru.setModel("Imprezza");

    Car ford = new Car();
    ford.setName("Ford");
    ford.setModel("Focus");

    cars.add(saturn);
    cars.add(subaru);
    cars.add(ford);

    Person foo = new Person();
    foo.setName("Foo");
    foo.setId("1");
    foo.setCar(cars);


    Car car  = new Car();
    car.setName("Saturn");
    car.setModel("Imprezza");

    Person bar = new Person();
    bar.setId("2");
    bar.setName("Bar");
    bar.setCar(Arrays.asList(car));

    List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
    IndexQuery indexQuery1 = new IndexQuery();
    indexQuery1.setId(foo.getId());
    indexQuery1.setObject(foo);

    IndexQuery indexQuery2 = new IndexQuery();
    indexQuery2.setId(bar.getId());
    indexQuery2.setObject(bar);

    indexQueries.add(indexQuery1);
    indexQueries.add(indexQuery2);

    elasticsearchTemplate.putMapping(Person.class);
    elasticsearchTemplate.bulkIndex(indexQueries);
    elasticsearchTemplate.refresh(Person.class, true);

    SearchQuery searchQuery = new NativeSearchQueryBuilder().build();
    List<Person> persons = elasticsearchTemplate.queryForList(searchQuery, Person.class);
查看更多
可以哭但决不认输i
3楼-- · 2020-06-23 08:29

First set up your index with the parent/child mapping. In the mapping below I have also added a untokenized field for categories so you can execute filter queries on that field. (For creating the index and documents I'm using the JSON API not the Java API as that was not part of the question.)

POST /test
{
    "mappings": {
        "book": {
            "_parent": {
                "type": "author"
            },
            "properties":{
                "category":{
                    "type":"string",
                    "fields":{
                        "raw":{
                            "type":"string",
                            "index": "not_analyzed"
                        }
                    }
                }
            }
        }
    }
}

Create some author documents:

POST /test/author/1
{
    "name": "jon doe"
}


POST /test/author/2
{
    "name": "jane smith"
}

Create some book documents, specifying the relationship between book and author in the request.

POST /test/book/12?parent=1
{
    "name": "fictional book",
    "category": "Fiction",
    "publisher": "publisher1"
}

POST /test/book/16?parent=2
{
    "name": "book of history",
    "category": "historical",
    "publisher": "publisher2"
}

POST /test/book/20?parent=2
{
    "name": "second fictional book",
    "category": "Fiction",
    "publisher": "publisher2"
}

The Java class below executes 3 queries:

  1. Search on all books that have the term 'book' in the title and return the authors.
  2. Search on all authors that have the terms 'jon doe' in the name and return the books.
  3. Search for books written by 'jane smith' and that are of type Fiction.

You can run the class from the command line, or import into Eclipse and right click on the class and select 'Run As > Java Application'. (You'll need to have the Elasticsearch library in the classpath.)

import java.util.concurrent.ExecutionException;

import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.HasChildQueryBuilder;
import org.elasticsearch.index.query.HasParentQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermFilterBuilder;

public class ParentChildQueryExample {

  public static void main(String args[]) throws InterruptedException, ExecutionException {

    //Set the Transport client which is used to communicate with your ES cluster. It is also possible to set this up using the Client Node.
    Settings settings = ImmutableSettings.settingsBuilder()
        .put("cluster.name", "elasticsearch").build();
    Client client = new TransportClient(settings)
        .addTransportAddress(new InetSocketTransportAddress(
        "localhost",
        9300));

    //create the searchRequestBuilder object.
    SearchRequestBuilder searchRequestBuilder = new SearchRequestBuilder(client).setIndices("test");

    //Query 1. Search on all books that have the term 'book' in the title and return the 'authors'.
    HasChildQueryBuilder bookNameQuery = QueryBuilders.hasChildQuery("book", QueryBuilders.matchQuery("name", "book"));
    System.out.println("Exectuing Query 1");
    SearchResponse searchResponse1 = searchRequestBuilder.setQuery(bookNameQuery).execute().actionGet();
    System.out.println("There were " + searchResponse1.getHits().getTotalHits()  + " results found for Query 1.");
    System.out.println(searchResponse1.toString());
    System.out.println();

    //Query 2. Search on all authors that have the terms 'jon doe' in the name and return the 'books'.
    HasParentQueryBuilder authorNameQuery = QueryBuilders.hasParentQuery("author", QueryBuilders.matchQuery("name", "jon doe"));
    System.out.println("Exectuing Query 2");
    SearchResponse searchResponse2 = searchRequestBuilder.setQuery(authorNameQuery).execute().actionGet();
    System.out.println("There were " + searchResponse2.getHits().getTotalHits()  + " results found for Query 2.");
    System.out.println(searchResponse2.toString());
    System.out.println();

    //Query 3. Search for books written by 'jane smith' and type Fiction.
    TermFilterBuilder termFilter = FilterBuilders.termFilter("category.raw", "Fiction");
    HasParentQueryBuilder authorNameQuery2 = QueryBuilders.hasParentQuery("author", QueryBuilders.matchQuery("name", "jane smith"));
    SearchResponse searchResponse3 = searchRequestBuilder.setQuery(QueryBuilders.filteredQuery(authorNameQuery2, termFilter)).execute().actionGet();
    System.out.println("There were " + searchResponse3.getHits().getTotalHits()  + " results found for Query 3.");
    System.out.println(searchResponse3.toString());
    System.out.println();
  }
}
查看更多
做个烂人
4楼-- · 2020-06-23 08:30

You can use Parent-Child documents for this.

Let's create an index bookstore with simple mappings for author documents and book documents. You can add more fields as per your requirements. See this for more information about indexing parent/child documents.

PUT bookstore
{
   "mappings": {
      "author": {
         "properties": {
            "authorname": {
               "type": "string"
            }
         }
      },
      "book": {
         "_parent": {
            "type": "author"
         },
         "properties": {
            "bookname": {
               "type": "string"
            }
         }
      }
   }
}

Now let's add two authors:

PUT bookstore/author/1
{
   "authorname": "author1"
}

PUT bookstore/author/2
{
   "authorname": "author2"
}

Now let's add two books of author author1:

PUT bookstore/book/11?parent=1
{
   "bookname": "book11"
}

PUT bookstore/book/12?parent=1
{
   "bookname": "book12"
}

Now let's add two books of author author2:

PUT bookstore/book/21?parent=2
{
   "bookname": "book21"
}

PUT bookstore/book/22?parent=2
{
   "bookname": "book22"
}

We're done indexing documents. Now let's start searching.

Search all books authored by author author1 (Read more about this here)

POST bookstore/book/_search
{
   "query": {
      "has_parent": {
         "type": "author",
         "query": {
            "term": {
               "authorname": "author1"
            }
         }
      }
   }
}

Search the author of book book11 (Read more about this here)

POST bookstore/author/_search
{
   "query": {
      "has_child": {
         "type": "book",
         "query": {
            "term": {
               "bookname": "book11"
            }
         }
      }
   }
}

Search for books named book12 and authored by author1. You need to use bool queries to achieve this. (There can be a better example for this scenario with more fields in the documents)

POST bookstore/book/_search
{
   "query": {
      "bool": {
         "must": [
            {
               "has_parent": {
                  "type": "author",
                  "query": {
                     "term": {
                        "authorname": "author1"
                     }
                  }
               }
            },
            {
               "term": {
                  "bookname": {
                     "value": "book12"
                  }
               }
            }
         ]
      }
   }
}
查看更多
登录 后发表回答