Skip to content

Commit

Permalink
Parent / Child Support, closes #553.
Browse files Browse the repository at this point in the history
  • Loading branch information
kimchy committed Dec 7, 2010
1 parent 1a8017d commit 54437c1
Show file tree
Hide file tree
Showing 67 changed files with 3,360 additions and 66 deletions.
@@ -0,0 +1,190 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.benchmark.search.child;

import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.action.bulk.BulkRequestBuilder;
import org.elasticsearch.common.StopWatch;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.SizeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.node.Node;

import java.io.IOException;

import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.cluster.metadata.IndexMetaData.*;
import static org.elasticsearch.common.settings.ImmutableSettings.*;
import static org.elasticsearch.common.xcontent.XContentFactory.*;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.node.NodeBuilder.*;

/**
* @author kimchy (shay.banon)
*/
public class ChildSearchBenchmark {

public static void main(String[] args) throws Exception {
Settings settings = settingsBuilder()
.put("index.engine.robin.refreshInterval", "-1")
.put("gateway.type", "local")
.put(SETTING_NUMBER_OF_SHARDS, 2)
.put(SETTING_NUMBER_OF_REPLICAS, 1)
.build();

Node node1 = nodeBuilder().settings(settingsBuilder().put(settings).put("name", "node1")).node();
Node node2 = nodeBuilder().settings(settingsBuilder().put(settings).put("name", "node2")).node();

Node clientNode = nodeBuilder().settings(settingsBuilder().put(settings).put("name", "client")).client(true).node();

Client client = clientNode.client();

long COUNT = SizeValue.parseSizeValue("1m").singles();
int CHILD_COUNT = 5;
int BATCH = 100;
int QUERY_COUNT = 500;

Thread.sleep(10000);
try {
client.admin().indices().create(createIndexRequest("test")).actionGet();
client.admin().indices().preparePutMapping("test").setType("child").setSource(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("_parent").field("type", "parent").endObject()
.endObject().endObject()).execute().actionGet();
Thread.sleep(5000);

StopWatch stopWatch = new StopWatch().start();

System.out.println("--> Indexing [" + COUNT + "] ...");
long ITERS = COUNT / BATCH;
long i = 1;
int counter = 0;
for (; i <= ITERS; i++) {
BulkRequestBuilder request = client.prepareBulk();
for (int j = 0; j < BATCH; j++) {
counter++;
request.add(Requests.indexRequest("test").type("parent").id(Integer.toString(counter))
.source(parentSource(Integer.toString(counter), "test" + counter)));
for (int k = 0; k < CHILD_COUNT; k++) {
request.add(Requests.indexRequest("test").type("child").id(Integer.toString(counter) + "_" + k)
.parent(Integer.toString(counter))
.source(childSource(Integer.toString(counter), "tag" + k)));
}
}
BulkResponse response = request.execute().actionGet();
if (response.hasFailures()) {
System.err.println("--> failures...");
}
if (((i * BATCH) % 10000) == 0) {
System.out.println("--> Indexed " + (i * BATCH) * (1 + CHILD_COUNT) + " took " + stopWatch.stop().lastTaskTime());
stopWatch.start();
}
}
System.out.println("--> Indexing took " + stopWatch.totalTime() + ", TPS " + (((double) (COUNT * (1 + CHILD_COUNT))) / stopWatch.totalTime().secondsFrac()));
} catch (Exception e) {
System.out.println("--> Index already exists, ignoring indexing phase, waiting for green");
ClusterHealthResponse clusterHealthResponse = client.admin().cluster().prepareHealth().setWaitForGreenStatus().setTimeout("10m").execute().actionGet();
if (clusterHealthResponse.timedOut()) {
System.err.println("--> Timed out waiting for cluster health");
}
}
client.admin().indices().prepareRefresh().execute().actionGet();
System.out.println("--> Number of docs in index: " + client.prepareCount().setQuery(matchAllQuery()).execute().actionGet().count());

System.out.println("--> Running just child query");
// run just the child query, warm up first
for (int j = 0; j < 100; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(termQuery("child.tag", "tag1")).execute().actionGet();
if (j == 0) {
System.out.println("--> Warmup took: " + searchResponse.took());
}
if (searchResponse.hits().totalHits() != COUNT) {
System.err.println("--> mismatch on hits");
}
}

long totalQueryTime = 0;
for (int j = 0; j < QUERY_COUNT; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(termQuery("child.tag", "tag1")).execute().actionGet();
if (searchResponse.hits().totalHits() != COUNT) {
System.err.println("--> mismatch on hits");
}
totalQueryTime += searchResponse.tookInMillis();
}
System.out.println("--> Just Child Query Avg: " + (totalQueryTime / QUERY_COUNT) + "ms");

System.out.println("--> Running has_child query");
// run parent child constant query
for (int j = 0; j < 100; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(hasChildQuery("child", termQuery("tag", "tag1"))).execute().actionGet();
if (searchResponse.hits().totalHits() != COUNT) {
System.err.println("--> mismatch on hits");
}
}

totalQueryTime = 0;
for (int j = 0; j < QUERY_COUNT; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(hasChildQuery("child", termQuery("tag", "tag1"))).execute().actionGet();
if (searchResponse.hits().totalHits() != COUNT) {
System.err.println("--> mismatch on hits");
}
totalQueryTime += searchResponse.tookInMillis();
}
System.out.println("--> has_child Query Avg: " + (totalQueryTime / QUERY_COUNT) + "ms");

System.out.println("--> Running top_children query");
// run parent child score query
for (int j = 0; j < 100; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(topChildrenQuery("child", termQuery("tag", "tag1"))).execute().actionGet();
// we expect to have mismatch on hits here
// if (searchResponse.hits().totalHits() != COUNT) {
// System.err.println("mismatch on hits");
// }
}

totalQueryTime = 0;
for (int j = 0; j < QUERY_COUNT; j++) {
SearchResponse searchResponse = client.prepareSearch().setQuery(topChildrenQuery("child", termQuery("tag", "tag1"))).execute().actionGet();
// we expect to have mismatch on hits here
// if (searchResponse.hits().totalHits() != COUNT) {
// System.err.println("mismatch on hits");
// }
totalQueryTime += searchResponse.tookInMillis();
}
System.out.println("--> top_children Query Avg: " + (totalQueryTime / QUERY_COUNT) + "ms");

clientNode.close();

node1.close();
node2.close();
}

private static XContentBuilder parentSource(String id, String nameValue) throws IOException {
return jsonBuilder().startObject().field("id", id).field("name", nameValue).endObject();
}

private static XContentBuilder childSource(String id, String tag) throws IOException {
return jsonBuilder().startObject().field("id", id).field("tag", tag).endObject();
}
}
Expand Up @@ -85,7 +85,7 @@ public static void main(String[] args) throws Exception {
System.err.println("failures...");
}
if (((i * BATCH) % 10000) == 0) {
System.out.println("Indexed " + (i * 100) + " took " + stopWatch.stop().lastTaskTime());
System.out.println("Indexed " + (i * BATCH) + " took " + stopWatch.stop().lastTaskTime());
stopWatch.start();
}
}
Expand Down
Expand Up @@ -110,6 +110,7 @@ public BulkRequest add(byte[] data, int from, int length, boolean contentUnsafe)
String type = null;
String id = null;
String routing = null;
String parent = null;
String opType = null;

String currentFieldName = null;
Expand All @@ -125,6 +126,8 @@ public BulkRequest add(byte[] data, int from, int length, boolean contentUnsafe)
id = parser.text();
} else if ("_routing".equals(currentFieldName)) {
routing = parser.text();
} else if ("_parent".equals(currentFieldName)) {
parent = parser.text();
} else if ("op_type".equals(currentFieldName) || "opType".equals(currentFieldName)) {
opType = parser.text();
}
Expand All @@ -138,17 +141,18 @@ public BulkRequest add(byte[] data, int from, int length, boolean contentUnsafe)
if (nextMarker == -1) {
break;
}
// order is important, we set parent after routing, so routing will be set to parent if not set explicitly
if ("index".equals(action)) {
if (opType == null) {
add(new IndexRequest(index, type, id).routing(routing)
add(new IndexRequest(index, type, id).routing(routing).parent(parent)
.source(data, from, nextMarker - from, contentUnsafe));
} else {
add(new IndexRequest(index, type, id).routing(routing)
add(new IndexRequest(index, type, id).routing(routing).parent(parent)
.create("create".equals(opType))
.source(data, from, nextMarker - from, contentUnsafe));
}
} else if ("create".equals(action)) {
add(new IndexRequest(index, type, id).routing(routing)
add(new IndexRequest(index, type, id).routing(routing).parent(parent)
.create(true)
.source(data, from, nextMarker - from, contentUnsafe));
}
Expand Down
Expand Up @@ -116,7 +116,8 @@ public class TransportShardBulkAction extends TransportShardReplicationOperation
}
}

SourceToParse sourceToParse = SourceToParse.source(indexRequest.source()).type(indexRequest.type()).id(indexRequest.id()).routing(indexRequest.routing());
SourceToParse sourceToParse = SourceToParse.source(indexRequest.source()).type(indexRequest.type()).id(indexRequest.id())
.routing(indexRequest.routing()).parent(indexRequest.parent());
if (indexRequest.opType() == IndexRequest.OpType.INDEX) {
ops[i] = indexShard.prepareIndex(sourceToParse);
} else {
Expand Down Expand Up @@ -198,7 +199,8 @@ public class TransportShardBulkAction extends TransportShardReplicationOperation
if (item.request() instanceof IndexRequest) {
IndexRequest indexRequest = (IndexRequest) item.request();
try {
SourceToParse sourceToParse = SourceToParse.source(indexRequest.source()).type(indexRequest.type()).id(indexRequest.id()).routing(indexRequest.routing());
SourceToParse sourceToParse = SourceToParse.source(indexRequest.source()).type(indexRequest.type()).id(indexRequest.id())
.routing(indexRequest.routing()).parent(indexRequest.parent());
if (indexRequest.opType() == IndexRequest.OpType.INDEX) {
ops[i] = indexShard.prepareIndex(sourceToParse);
} else {
Expand Down
Expand Up @@ -113,6 +113,7 @@ public static OpType fromId(byte id) {
private String type;
private String id;
@Nullable private String routing;
@Nullable private String parent;

private byte[] source;
private int sourceOffset;
Expand Down Expand Up @@ -255,6 +256,22 @@ public String routing() {
return this.routing;
}

/**
* Sets the parent id of this document. If routing is not set, automatically set it as the
* routing as well.
*/
public IndexRequest parent(String parent) {
this.parent = parent;
if (routing == null) {
routing = parent;
}
return this;
}

public String parent() {
return this.parent;
}

/**
* The source of the document to index, recopied to a new array if it has an offset or unsafe.
*/
Expand Down Expand Up @@ -532,6 +549,9 @@ public void processRouting(MappingMetaData mappingMd) throws ElasticSearchExcept
if (in.readBoolean()) {
routing = in.readUTF();
}
if (in.readBoolean()) {
parent = in.readUTF();
}

sourceUnsafe = false;
sourceOffset = 0;
Expand All @@ -558,6 +578,12 @@ public void processRouting(MappingMetaData mappingMd) throws ElasticSearchExcept
out.writeBoolean(true);
out.writeUTF(routing);
}
if (parent == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeUTF(parent);
}
out.writeVInt(sourceLength);
out.writeBytes(source, sourceOffset, sourceLength);
out.writeByte(opType.id());
Expand Down
Expand Up @@ -160,7 +160,8 @@ private void innerExecute(final IndexRequest request, final ActionListener<Index
}

IndexShard indexShard = indexShard(shardRequest);
SourceToParse sourceToParse = SourceToParse.source(request.source()).type(request.type()).id(request.id()).routing(request.routing());
SourceToParse sourceToParse = SourceToParse.source(request.source()).type(request.type()).id(request.id())
.routing(request.routing()).parent(request.parent());
ParsedDocument doc;
if (request.opType() == IndexRequest.OpType.INDEX) {
Engine.Index index = indexShard.prepareIndex(sourceToParse);
Expand All @@ -180,7 +181,8 @@ private void innerExecute(final IndexRequest request, final ActionListener<Index
@Override protected void shardOperationOnReplica(ShardOperationRequest shardRequest) {
IndexShard indexShard = indexShard(shardRequest);
IndexRequest request = shardRequest.request;
SourceToParse sourceToParse = SourceToParse.source(request.source()).type(request.type()).id(request.id()).routing(request.routing());
SourceToParse sourceToParse = SourceToParse.source(request.source()).type(request.type()).id(request.id())
.routing(request.routing()).parent(request.parent());
if (request.opType() == IndexRequest.OpType.INDEX) {
Engine.Index index = indexShard.prepareIndex(sourceToParse);
index.refresh(request.refresh());
Expand Down
Expand Up @@ -78,6 +78,15 @@ public IndexRequestBuilder setRouting(String routing) {
return this;
}

/**
* Sets the parent id of this document. If routing is not set, automatically set it as the
* routing as well.
*/
public IndexRequestBuilder setParent(String parent) {
request.parent(parent);
return this;
}

/**
* Index the Map as a JSON.
*
Expand Down

0 comments on commit 54437c1

Please sign in to comment.