コード例 #1
0
 @Override
 protected ShardIterator shards(ClusterState clusterState, UpdateRequest request)
     throws ElasticSearchException {
   if (request.shardId() != -1) {
     return clusterState
         .routingTable()
         .index(request.index())
         .shard(request.shardId())
         .primaryShardIt();
   }
   ShardIterator shardIterator =
       clusterService
           .operationRouting()
           .indexShards(
               clusterService.state(),
               request.index(),
               request.type(),
               request.id(),
               request.routing());
   ShardRouting shard;
   while ((shard = shardIterator.nextOrNull()) != null) {
     if (shard.primary()) {
       return new PlainShardIterator(shardIterator.shardId(), ImmutableList.of(shard));
     }
   }
   return new PlainShardIterator(shardIterator.shardId(), ImmutableList.<ShardRouting>of());
 }
コード例 #2
0
  @Override
  protected boolean resolveRequest(
      ClusterState state, UpdateRequest request, ActionListener<UpdateResponse> listener) {
    MetaData metaData = clusterService.state().metaData();
    String aliasOrIndex = request.index();
    request.routing((metaData.resolveIndexRouting(request.routing(), aliasOrIndex)));
    request.index(metaData.concreteSingleIndex(request.index(), request.indicesOptions()));

    // Fail fast on the node that received the request, rather than failing when translating on the
    // index or delete request.
    if (request.routing() == null
        && state.getMetaData().routingRequired(request.index(), request.type())) {
      throw new RoutingMissingException(request.index(), request.type(), request.id());
    }
    return true;
  }
コード例 #3
0
  @Nullable
  protected GetResult extractGetResult(
      final UpdateRequest request,
      long version,
      final Map<String, Object> source,
      XContentType sourceContentType,
      @Nullable final BytesReference sourceAsBytes) {
    if (request.fields() == null || request.fields().length == 0) {
      return null;
    }
    boolean sourceRequested = false;
    Map<String, GetField> fields = null;
    if (request.fields() != null && request.fields().length > 0) {
      SourceLookup sourceLookup = new SourceLookup();
      sourceLookup.setNextSource(source);
      for (String field : request.fields()) {
        if (field.equals("_source")) {
          sourceRequested = true;
          continue;
        }
        Object value = sourceLookup.extractValue(field);
        if (value != null) {
          if (fields == null) {
            fields = newHashMapWithExpectedSize(2);
          }
          GetField getField = fields.get(field);
          if (getField == null) {
            getField = new GetField(field, new ArrayList<Object>(2));
            fields.put(field, getField);
          }
          getField.getValues().add(value);
        }
      }
    }

    // TODO when using delete/none, we can still return the source as bytes by generating it (using
    // the sourceContentType)

    return new GetResult(
        request.index(),
        request.type(),
        request.id(),
        version,
        true,
        sourceRequested ? sourceAsBytes : null,
        fields);
  }
コード例 #4
0
 /**
  * Prepares an update request by converting it into an index or delete request or an update
  * response (no action).
  */
 @SuppressWarnings("unchecked")
 public Result prepare(UpdateRequest request, IndexShard indexShard) {
   final GetResult getResult =
       indexShard
           .getService()
           .get(
               request.type(),
               request.id(),
               new String[] {
                 RoutingFieldMapper.NAME,
                 ParentFieldMapper.NAME,
                 TTLFieldMapper.NAME,
                 TimestampFieldMapper.NAME
               },
               true,
               request.version(),
               request.versionType(),
               FetchSourceContext.FETCH_SOURCE,
               false);
   return prepare(indexShard.shardId(), request, getResult);
 }
コード例 #5
0
  /**
   * Prepares an update request by converting it into an index or delete request or an update
   * response (no action).
   */
  @SuppressWarnings("unchecked")
  protected Result prepare(ShardId shardId, UpdateRequest request, final GetResult getResult) {
    long getDateNS = System.nanoTime();
    if (!getResult.isExists()) {
      if (request.upsertRequest() == null && !request.docAsUpsert()) {
        throw new DocumentMissingException(shardId, request.type(), request.id());
      }
      IndexRequest indexRequest = request.docAsUpsert() ? request.doc() : request.upsertRequest();
      TimeValue ttl = indexRequest.ttl();
      if (request.scriptedUpsert() && request.script() != null) {
        // Run the script to perform the create logic
        IndexRequest upsert = request.upsertRequest();
        Map<String, Object> upsertDoc = upsert.sourceAsMap();
        Map<String, Object> ctx = new HashMap<>(2);
        // Tell the script that this is a create and not an update
        ctx.put("op", "create");
        ctx.put("_source", upsertDoc);
        ctx = executeScript(request.script, ctx);
        // Allow the script to set TTL using ctx._ttl
        if (ttl == null) {
          ttl = getTTLFromScriptContext(ctx);
        }

        // Allow the script to abort the create by setting "op" to "none"
        String scriptOpChoice = (String) ctx.get("op");

        // Only valid options for an upsert script are "create"
        // (the default) or "none", meaning abort upsert
        if (!"create".equals(scriptOpChoice)) {
          if (!"none".equals(scriptOpChoice)) {
            logger.warn(
                "Used upsert operation [{}] for script [{}], doing nothing...",
                scriptOpChoice,
                request.script.getScript());
          }
          UpdateResponse update =
              new UpdateResponse(
                  shardId, getResult.getType(), getResult.getId(), getResult.getVersion(), false);
          update.setGetResult(getResult);
          return new Result(update, Operation.NONE, upsertDoc, XContentType.JSON);
        }
        indexRequest.source((Map) ctx.get("_source"));
      }

      indexRequest
          .index(request.index())
          .type(request.type())
          .id(request.id())
          // it has to be a "create!"
          .create(true)
          .ttl(ttl)
          .refresh(request.refresh())
          .routing(request.routing())
          .parent(request.parent())
          .consistencyLevel(request.consistencyLevel());
      if (request.versionType() != VersionType.INTERNAL) {
        // in all but the internal versioning mode, we want to create the new document using the
        // given version.
        indexRequest.version(request.version()).versionType(request.versionType());
      }
      return new Result(indexRequest, Operation.UPSERT, null, null);
    }

    long updateVersion = getResult.getVersion();

    if (request.versionType() != VersionType.INTERNAL) {
      assert request.versionType() == VersionType.FORCE;
      updateVersion = request.version(); // remember, match_any is excluded by the conflict test
    }

    if (getResult.internalSourceRef() == null) {
      // no source, we can't do nothing, through a failure...
      throw new DocumentSourceMissingException(shardId, request.type(), request.id());
    }

    Tuple<XContentType, Map<String, Object>> sourceAndContent =
        XContentHelper.convertToMap(getResult.internalSourceRef(), true);
    String operation = null;
    String timestamp = null;
    TimeValue ttl = null;
    final Map<String, Object> updatedSourceAsMap;
    final XContentType updateSourceContentType = sourceAndContent.v1();
    String routing =
        getResult.getFields().containsKey(RoutingFieldMapper.NAME)
            ? getResult.field(RoutingFieldMapper.NAME).getValue().toString()
            : null;
    String parent =
        getResult.getFields().containsKey(ParentFieldMapper.NAME)
            ? getResult.field(ParentFieldMapper.NAME).getValue().toString()
            : null;

    if (request.script() == null && request.doc() != null) {
      IndexRequest indexRequest = request.doc();
      updatedSourceAsMap = sourceAndContent.v2();
      if (indexRequest.ttl() != null) {
        ttl = indexRequest.ttl();
      }
      timestamp = indexRequest.timestamp();
      if (indexRequest.routing() != null) {
        routing = indexRequest.routing();
      }
      if (indexRequest.parent() != null) {
        parent = indexRequest.parent();
      }
      boolean noop =
          !XContentHelper.update(
              updatedSourceAsMap, indexRequest.sourceAsMap(), request.detectNoop());
      // noop could still be true even if detectNoop isn't because update detects empty maps as
      // noops.  BUT we can only
      // actually turn the update into a noop if detectNoop is true to preserve backwards
      // compatibility and to handle
      // cases where users repopulating multi-fields or adding synonyms, etc.
      if (request.detectNoop() && noop) {
        operation = "none";
      }
    } else {
      Map<String, Object> ctx = new HashMap<>(16);
      Long originalTtl =
          getResult.getFields().containsKey(TTLFieldMapper.NAME)
              ? (Long) getResult.field(TTLFieldMapper.NAME).getValue()
              : null;
      Long originalTimestamp =
          getResult.getFields().containsKey(TimestampFieldMapper.NAME)
              ? (Long) getResult.field(TimestampFieldMapper.NAME).getValue()
              : null;
      ctx.put("_index", getResult.getIndex());
      ctx.put("_type", getResult.getType());
      ctx.put("_id", getResult.getId());
      ctx.put("_version", getResult.getVersion());
      ctx.put("_routing", routing);
      ctx.put("_parent", parent);
      ctx.put("_timestamp", originalTimestamp);
      ctx.put("_ttl", originalTtl);
      ctx.put("_source", sourceAndContent.v2());

      ctx = executeScript(request.script, ctx);

      operation = (String) ctx.get("op");

      Object fetchedTimestamp = ctx.get("_timestamp");
      if (fetchedTimestamp != null) {
        timestamp = fetchedTimestamp.toString();
      } else if (originalTimestamp != null) {
        // No timestamp has been given in the update script, so we keep the previous timestamp if
        // there is one
        timestamp = originalTimestamp.toString();
      }

      ttl = getTTLFromScriptContext(ctx);

      updatedSourceAsMap = (Map<String, Object>) ctx.get("_source");
    }

    // apply script to update the source
    // No TTL has been given in the update script so we keep previous TTL value if there is one
    if (ttl == null) {
      Long ttlAsLong =
          getResult.getFields().containsKey(TTLFieldMapper.NAME)
              ? (Long) getResult.field(TTLFieldMapper.NAME).getValue()
              : null;
      if (ttlAsLong != null) {
        ttl =
            new TimeValue(
                ttlAsLong
                    - TimeValue.nsecToMSec(
                        System.nanoTime()
                            - getDateNS)); // It is an approximation of exact TTL value, could be
                                           // improved
      }
    }

    if (operation == null || "index".equals(operation)) {
      final IndexRequest indexRequest =
          Requests.indexRequest(request.index())
              .type(request.type())
              .id(request.id())
              .routing(routing)
              .parent(parent)
              .source(updatedSourceAsMap, updateSourceContentType)
              .version(updateVersion)
              .versionType(request.versionType())
              .consistencyLevel(request.consistencyLevel())
              .timestamp(timestamp)
              .ttl(ttl)
              .refresh(request.refresh());
      return new Result(indexRequest, Operation.INDEX, updatedSourceAsMap, updateSourceContentType);
    } else if ("delete".equals(operation)) {
      DeleteRequest deleteRequest =
          Requests.deleteRequest(request.index())
              .type(request.type())
              .id(request.id())
              .routing(routing)
              .parent(parent)
              .version(updateVersion)
              .versionType(request.versionType())
              .consistencyLevel(request.consistencyLevel());
      return new Result(
          deleteRequest, Operation.DELETE, updatedSourceAsMap, updateSourceContentType);
    } else if ("none".equals(operation)) {
      UpdateResponse update =
          new UpdateResponse(
              shardId, getResult.getType(), getResult.getId(), getResult.getVersion(), false);
      update.setGetResult(
          extractGetResult(
              request,
              request.index(),
              getResult.getVersion(),
              updatedSourceAsMap,
              updateSourceContentType,
              getResult.internalSourceRef()));
      return new Result(update, Operation.NONE, updatedSourceAsMap, updateSourceContentType);
    } else {
      logger.warn(
          "Used update operation [{}] for script [{}], doing nothing...",
          operation,
          request.script.getScript());
      UpdateResponse update =
          new UpdateResponse(
              shardId, getResult.getType(), getResult.getId(), getResult.getVersion(), false);
      return new Result(update, Operation.NONE, updatedSourceAsMap, updateSourceContentType);
    }
  }
コード例 #6
0
  protected void shardOperation(
      final UpdateRequest request,
      final ActionListener<UpdateResponse> listener,
      final int retryCount)
      throws ElasticSearchException {
    IndexService indexService = indicesService.indexServiceSafe(request.index());
    IndexShard indexShard = indexService.shardSafe(request.shardId());

    long getDate = System.currentTimeMillis();
    final GetResult getResult =
        indexShard
            .getService()
            .get(
                request.type(),
                request.id(),
                new String[] {
                  SourceFieldMapper.NAME,
                  RoutingFieldMapper.NAME,
                  ParentFieldMapper.NAME,
                  TTLFieldMapper.NAME
                },
                true);

    // no doc, what to do, what to do...
    if (!getResult.isExists()) {
      if (request.upsertRequest() == null) {
        listener.onFailure(
            new DocumentMissingException(
                new ShardId(request.index(), request.shardId()), request.type(), request.id()));
        return;
      }
      final IndexRequest indexRequest = request.upsertRequest();
      indexRequest
          .index(request.index())
          .type(request.type())
          .id(request.id())
          // it has to be a "create!"
          .create(true)
          .routing(request.routing())
          .percolate(request.percolate())
          .refresh(request.refresh())
          .replicationType(request.replicationType())
          .consistencyLevel(request.consistencyLevel());
      indexRequest.operationThreaded(false);
      // we fetch it from the index request so we don't generate the bytes twice, its already done
      // in the index request
      final BytesReference updateSourceBytes = indexRequest.source();
      indexAction.execute(
          indexRequest,
          new ActionListener<IndexResponse>() {
            @Override
            public void onResponse(IndexResponse response) {
              UpdateResponse update =
                  new UpdateResponse(
                      response.getIndex(),
                      response.getType(),
                      response.getId(),
                      response.getVersion());
              update.setMatches(response.getMatches());
              if (request.fields() != null && request.fields().length > 0) {
                Tuple<XContentType, Map<String, Object>> sourceAndContent =
                    XContentHelper.convertToMap(updateSourceBytes, true);
                update.setGetResult(
                    extractGetResult(
                        request,
                        response.getVersion(),
                        sourceAndContent.v2(),
                        sourceAndContent.v1(),
                        updateSourceBytes));
              } else {
                update.setGetResult(null);
              }
              listener.onResponse(update);
            }

            @Override
            public void onFailure(Throwable e) {
              e = ExceptionsHelper.unwrapCause(e);
              if (e instanceof VersionConflictEngineException
                  || e instanceof DocumentAlreadyExistsException) {
                if (retryCount < request.retryOnConflict()) {
                  threadPool
                      .executor(executor())
                      .execute(
                          new Runnable() {
                            @Override
                            public void run() {
                              shardOperation(request, listener, retryCount + 1);
                            }
                          });
                  return;
                }
              }
              listener.onFailure(e);
            }
          });
      return;
    }

    if (getResult.internalSourceRef() == null) {
      // no source, we can't do nothing, through a failure...
      listener.onFailure(
          new DocumentSourceMissingException(
              new ShardId(request.index(), request.shardId()), request.type(), request.id()));
      return;
    }

    Tuple<XContentType, Map<String, Object>> sourceAndContent =
        XContentHelper.convertToMap(getResult.internalSourceRef(), true);
    String operation = null;
    String timestamp = null;
    Long ttl = null;
    Object fetchedTTL = null;
    final Map<String, Object> updatedSourceAsMap;
    final XContentType updateSourceContentType = sourceAndContent.v1();
    String routing =
        getResult.getFields().containsKey(RoutingFieldMapper.NAME)
            ? getResult.field(RoutingFieldMapper.NAME).getValue().toString()
            : null;
    String parent =
        getResult.getFields().containsKey(ParentFieldMapper.NAME)
            ? getResult.field(ParentFieldMapper.NAME).getValue().toString()
            : null;

    if (request.script() == null && request.doc() != null) {
      IndexRequest indexRequest = request.doc();
      updatedSourceAsMap = sourceAndContent.v2();
      if (indexRequest.ttl() > 0) {
        ttl = indexRequest.ttl();
      }
      timestamp = indexRequest.timestamp();
      if (indexRequest.routing() != null) {
        routing = indexRequest.routing();
      }
      if (indexRequest.parent() != null) {
        parent = indexRequest.parent();
      }
      XContentHelper.update(updatedSourceAsMap, indexRequest.sourceAsMap());
    } else {
      Map<String, Object> ctx = new HashMap<String, Object>(2);
      ctx.put("_source", sourceAndContent.v2());

      try {
        ExecutableScript script =
            scriptService.executable(request.scriptLang, request.script, request.scriptParams);
        script.setNextVar("ctx", ctx);
        script.run();
        // we need to unwrap the ctx...
        ctx = (Map<String, Object>) script.unwrap(ctx);
      } catch (Exception e) {
        throw new ElasticSearchIllegalArgumentException("failed to execute script", e);
      }

      operation = (String) ctx.get("op");
      timestamp = (String) ctx.get("_timestamp");
      fetchedTTL = ctx.get("_ttl");
      if (fetchedTTL != null) {
        if (fetchedTTL instanceof Number) {
          ttl = ((Number) fetchedTTL).longValue();
        } else {
          ttl = TimeValue.parseTimeValue((String) fetchedTTL, null).millis();
        }
      }

      updatedSourceAsMap = (Map<String, Object>) ctx.get("_source");
    }

    // apply script to update the source
    // No TTL has been given in the update script so we keep previous TTL value if there is one
    if (ttl == null) {
      ttl =
          getResult.getFields().containsKey(TTLFieldMapper.NAME)
              ? (Long) getResult.field(TTLFieldMapper.NAME).getValue()
              : null;
      if (ttl != null) {
        ttl =
            ttl
                - (System.currentTimeMillis()
                    - getDate); // It is an approximation of exact TTL value, could be improved
      }
    }

    // TODO: external version type, does it make sense here? does not seem like it...

    if (operation == null || "index".equals(operation)) {
      final IndexRequest indexRequest =
          Requests.indexRequest(request.index())
              .type(request.type())
              .id(request.id())
              .routing(routing)
              .parent(parent)
              .source(updatedSourceAsMap, updateSourceContentType)
              .version(getResult.getVersion())
              .replicationType(request.replicationType())
              .consistencyLevel(request.consistencyLevel())
              .timestamp(timestamp)
              .ttl(ttl)
              .percolate(request.percolate())
              .refresh(request.refresh());
      indexRequest.operationThreaded(false);
      // we fetch it from the index request so we don't generate the bytes twice, its already done
      // in the index request
      final BytesReference updateSourceBytes = indexRequest.source();
      indexAction.execute(
          indexRequest,
          new ActionListener<IndexResponse>() {
            @Override
            public void onResponse(IndexResponse response) {
              UpdateResponse update =
                  new UpdateResponse(
                      response.getIndex(),
                      response.getType(),
                      response.getId(),
                      response.getVersion());
              update.setMatches(response.getMatches());
              update.setGetResult(
                  extractGetResult(
                      request,
                      response.getVersion(),
                      updatedSourceAsMap,
                      updateSourceContentType,
                      updateSourceBytes));
              listener.onResponse(update);
            }

            @Override
            public void onFailure(Throwable e) {
              e = ExceptionsHelper.unwrapCause(e);
              if (e instanceof VersionConflictEngineException) {
                if (retryCount < request.retryOnConflict()) {
                  threadPool
                      .executor(executor())
                      .execute(
                          new Runnable() {
                            @Override
                            public void run() {
                              shardOperation(request, listener, retryCount + 1);
                            }
                          });
                  return;
                }
              }
              listener.onFailure(e);
            }
          });
    } else if ("delete".equals(operation)) {
      DeleteRequest deleteRequest =
          Requests.deleteRequest(request.index())
              .type(request.type())
              .id(request.id())
              .routing(routing)
              .parent(parent)
              .version(getResult.getVersion())
              .replicationType(request.replicationType())
              .consistencyLevel(request.consistencyLevel());
      deleteRequest.operationThreaded(false);
      deleteAction.execute(
          deleteRequest,
          new ActionListener<DeleteResponse>() {
            @Override
            public void onResponse(DeleteResponse response) {
              UpdateResponse update =
                  new UpdateResponse(
                      response.getIndex(),
                      response.getType(),
                      response.getId(),
                      response.getVersion());
              update.setGetResult(
                  extractGetResult(
                      request,
                      response.getVersion(),
                      updatedSourceAsMap,
                      updateSourceContentType,
                      null));
              listener.onResponse(update);
            }

            @Override
            public void onFailure(Throwable e) {
              e = ExceptionsHelper.unwrapCause(e);
              if (e instanceof VersionConflictEngineException) {
                if (retryCount < request.retryOnConflict()) {
                  threadPool
                      .executor(executor())
                      .execute(
                          new Runnable() {
                            @Override
                            public void run() {
                              shardOperation(request, listener, retryCount + 1);
                            }
                          });
                  return;
                }
              }
              listener.onFailure(e);
            }
          });
    } else if ("none".equals(operation)) {
      UpdateResponse update =
          new UpdateResponse(
              getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion());
      update.setGetResult(
          extractGetResult(
              request, getResult.getVersion(), updatedSourceAsMap, updateSourceContentType, null));
      listener.onResponse(update);
    } else {
      logger.warn(
          "Used update operation [{}] for script [{}], doing nothing...",
          operation,
          request.script);
      listener.onResponse(
          new UpdateResponse(
              getResult.getIndex(),
              getResult.getType(),
              getResult.getId(),
              getResult.getVersion()));
    }
  }