Similar to nested types, parent-child relationships also allow you to relate different entities together but they differ in the implementation and behavior. Unlike nested documents, they are not present within the same document, rather parent-child documents are completely separate documents. They follow the one to many relationship principle and allow you to define one type as parent and one or more as the child type.
To create a parent-child mapping, you just need to specify which type should be the parent of the child type. You do not need to define anything extra in the parent type mapping but before indexing the data in the child type, you need to specify in the child's mapping who will be its parent.
Let's create a new index, twitter_parent_child
:
PUT /twitter_parent_child
Now, put the mapping of the tweets type by specifying that the user
will be its parent. This is done using the _parent
keyword inside the mapping, but outside the properties:
PUT /twitter_parent_child/tweets/_mapping { "_parent": { "type": "users" }, "properties": { "text":{"type": "string"}, "created_at":{"type": "date"} } }
Next, put the mapping of the users type:
PUT /twitter_parent_child/users/_mapping { "properties": { "screen_name":{"type": "string"}, "created_at":{"type": "date"} } }
One parent can have multiple child types but one child can have only one parent type. It's also important to know the fact that you have to create the mappings for child type and specify the parent before creating the parent type. If you do it in reverse, you will get the exception: "Can't add a _parent
field that points to an already existing type". Also, note that parents cannot be updated for any child type.
Indexing parent documents is similar to what we have followed till now. However, while indexing children, you need to specify the unique ID of the parent document so that Elasticsearch can know which document is the parent of this document.
Python example
Indexing parent document:
A parent document is indexed in a similar way to any other document:
parent_doc = {}parent_doc['screen_name'] = 'd_bharvi'parent_doc['followers_count"'] = 2000parent_doc['create_at"'] = '2012-05-30' es.index(index='twitter_parent_child', doc_type= users, body=parent_doc, id='64995604')
Indexing a child document:
Indexing a child document requires specifying the _id
of the parent document type. In Python, it is done using the id
parameter inside the index
function:
child_doc = {}
child_doc['text'] = 'learning parent-child concepts'
child_doc['created_at'] = '2015-10-30'
es.index(index='twitter_parent_child', doc_type= 'tweets', body=child_doc, id = '2333', parent='64995604')
Java example
Include the following import statements:
import org.elasticsearch.action.index.IndexRequestBuilder;
The parent document can be indexed in the following way:
IndexRequestBuilder index = client.prepareIndex( "twitter_parent_child", "users"); Map<String, Object> parentDoc= new HashMap<String, Object>(); parentDoc.put("screen_name", "d_bharvi"); parentDoc.put("followers_count", 2000); parentDoc.put("create_at", "2012-05-30"); index.setId("64995604").setSource(parentDoc) .execute().actionGet();
The child document can be indexed in the following way:
IndexRequestBuilder index=client.prepareIndex("twitter_parent_child", "tweets");
Map<String, Object> childDoc= new HashMap<String, Object>();
childDoc.put("text", "learning parent-child concepts in elasticsearch");
childDoc.put("create_at", "2015-05-30");
index.setParent("64995604").setId("2333")
.setSource(childDoc).execute().actionGet();
Please note that while indexing the child document, we have used the setParent
method and passed the _id
of the parent document.
Elasticsearch offers two queries to search parent-child documents:
has_child
queryhas_parent
queryThe has_child
query allows you to find and return parent documents by querying the child type. For example, we can find all the users who have tweeted about Elasticsearch.
Python exam ple
query = {
"query": {
"has_child": {
"type": "tweets",
"query": {
"match": {
"text": "elasticsearch"
}
}
}
}
}
resp = es.search(index='twitter_parent_child', doc_type= 'users', body=query)
Java example
The same query can be applied using Java with the following code:
SearchResponse response = client.prepareSearch("twitter_parent_child").setTypes("users") .setQuery(QueryBuilders.hasChildQuery(childType, QueryBuilders.matchQuery("text","elasticsearch"))) .execute().actionGet();
The has_parent
query works in reverse compared to the has_child
query and allows you to find and return child documents by querying on the parent type. For example, we can find all the tweets tweeted by users who have a followers count greater than 200.
Python example
query = {
"query": {
"has_parent": {
"type": "users",
"query": {
"range": {
"followers_count": {
"gte": 200
}
}
}
}
}
}
resp = es.search(index='twitter_parent_child', doc_type= 'tweets', body=query)
Java example
The same query can be applied using Java with the following code:
SearchResponse response = client.prepareSearch("twitter_parent_child")
.setTypes("tweets")
.setQuery(QueryBuilders
.hasParentQuery(parentType, QueryBuilders.rangeQuery("followers_count")
.gt(200))).execute().actionGet();