/**
   * Returns the best nested {@link ObjectMapper} instances that is in the scope of the specified
   * nested docId.
   */
  public ObjectMapper findNestedObjectMapper(
      int nestedDocId, SearchContext sc, LeafReaderContext context) throws IOException {
    ObjectMapper nestedObjectMapper = null;
    for (ObjectMapper objectMapper : objectMappers().values()) {
      if (!objectMapper.nested().isNested()) {
        continue;
      }

      Filter filter = objectMapper.nestedTypeFilter();
      if (filter == null) {
        continue;
      }
      // We can pass down 'null' as acceptedDocs, because nestedDocId is a doc to be fetched and
      // therefor is guaranteed to be a live doc.
      DocIdSet nestedTypeSet = filter.getDocIdSet(context, null);
      if (nestedTypeSet == null) {
        continue;
      }
      DocIdSetIterator iterator = nestedTypeSet.iterator();
      if (iterator == null) {
        continue;
      }

      if (iterator.advance(nestedDocId) == nestedDocId) {
        if (nestedObjectMapper == null) {
          nestedObjectMapper = objectMapper;
        } else {
          if (nestedObjectMapper.fullPath().length() < objectMapper.fullPath().length()) {
            nestedObjectMapper = objectMapper;
          }
        }
      }
    }
    return nestedObjectMapper;
  }
  /** Resolves the closest inherited {@link ObjectMapper} that is nested. */
  public ObjectMapper resolveClosestNestedObjectMapper(String fieldName) {
    int indexOf = fieldName.lastIndexOf('.');
    if (indexOf == -1) {
      return null;
    } else {
      do {
        String objectPath = fieldName.substring(0, indexOf);
        ObjectMappers objectMappers = objectMapper(objectPath);
        if (objectMappers == null) {
          return null;
        }

        if (objectMappers.hasNested()) {
          for (ObjectMapper objectMapper : objectMappers) {
            if (objectMapper.nested().isNested()) {
              return objectMapper;
            }
          }
        }

        indexOf = objectPath.lastIndexOf('.');
      } while (indexOf != -1);
    }

    return null;
  }
 protected void addMappers(
     Collection<ObjectMapper> objectMappers, Collection<FieldMapper> fieldMappers) {
   assert mappingLock.isWriteLockedByCurrentThread();
   ImmutableOpenMap.Builder<String, ObjectMapper> fullPathObjectMappers =
       ImmutableOpenMap.builder(this.fullPathObjectMappers);
   for (ObjectMapper objectMapper : objectMappers) {
     fullPathObjectMappers.put(objectMapper.fullPath(), objectMapper);
     if (objectMapper.nested().isNested()) {
       hasNested = true;
     }
   }
   this.fullPathObjectMappers = fullPathObjectMappers.build();
   this.fieldTypes = this.fieldTypes.copyAndAddAll(fieldMappers);
 }
Beispiel #4
0
 @Override
 public void objectMapper(ObjectMapper objectMapper) {
   ObjectMappers mappers = objectMappers.get(objectMapper.fullPath());
   if (mappers == null) {
     mappers = new ObjectMappers(objectMapper);
   } else {
     mappers = mappers.concat(objectMapper);
   }
   objectMappers =
       newMapBuilder(objectMappers).put(objectMapper.fullPath(), mappers).immutableMap();
   // update the hasNested flag
   if (objectMapper.nested().isNested()) {
     hasNested = true;
   }
 }
 private void addObjectMappers(ObjectMapper... objectMappers) {
   synchronized (mutex) {
     MapBuilder<String, ObjectMapper> builder = MapBuilder.newMapBuilder(this.objectMappers);
     for (ObjectMapper objectMapper : objectMappers) {
       builder.put(objectMapper.fullPath(), objectMapper);
       if (objectMapper.nested().isNested()) {
         hasNestedObjects = true;
       }
     }
     this.objectMappers = builder.immutableMap();
   }
   for (ObjectMapperListener objectMapperListener : objectMapperListeners) {
     objectMapperListener.objectMappers(objectMappers);
   }
 }
 private void checkNestedFieldsLimit(Map<String, ObjectMapper> fullPathObjectMappers) {
   long allowedNestedFields = indexSettings.getValue(INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING);
   long actualNestedFields = 0;
   for (ObjectMapper objectMapper : fullPathObjectMappers.values()) {
     if (objectMapper.nested().isNested()) {
       actualNestedFields++;
     }
   }
   if (actualNestedFields > allowedNestedFields) {
     throw new IllegalArgumentException(
         "Limit of nested fields ["
             + allowedNestedFields
             + "] in index ["
             + index().getName()
             + "] has been exceeded");
   }
 }
 public void setPath(String path) {
   this.path = path;
   MapperService.SmartNameObjectMapper smart = parseContext.smartObjectMapper(path);
   if (smart == null) {
     throw new QueryParsingException(
         parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
   }
   childDocumentMapper = smart.docMapper();
   nestedObjectMapper = smart.mapper();
   if (nestedObjectMapper == null) {
     throw new QueryParsingException(
         parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
   }
   if (!nestedObjectMapper.nested().isNested()) {
     throw new QueryParsingException(
         parseContext.index(),
         "[nested] nested object under path [" + path + "] is not of nested type");
   }
 }
 private void addObjectMappers(ObjectMapper[] objectMappers) {
   synchronized (mutex) {
     MapBuilder<String, ObjectMappers> fullPathObjectMappers =
         newMapBuilder(this.fullPathObjectMappers);
     for (ObjectMapper objectMapper : objectMappers) {
       ObjectMappers mappers = fullPathObjectMappers.get(objectMapper.fullPath());
       if (mappers == null) {
         mappers = new ObjectMappers(objectMapper);
       } else {
         mappers = mappers.concat(objectMapper);
       }
       fullPathObjectMappers.put(objectMapper.fullPath(), mappers);
       // update the hasNested flag
       if (objectMapper.nested().isNested()) {
         hasNested = true;
       }
     }
     this.fullPathObjectMappers = fullPathObjectMappers.map();
   }
 }
  private void addMappers(
      Collection<ObjectMapper> objectMappers, Collection<FieldMapper> fieldMappers) {
    assert mappingLock.isWriteLockedByCurrentThread();
    // first ensure we don't have any incompatible new fields
    mapperService.checkNewMappersCompatibility(objectMappers, fieldMappers, true);

    // update mappers for this document type
    MapBuilder<String, ObjectMapper> builder = MapBuilder.newMapBuilder(this.objectMappers);
    for (ObjectMapper objectMapper : objectMappers) {
      builder.put(objectMapper.fullPath(), objectMapper);
      if (objectMapper.nested().isNested()) {
        hasNestedObjects = true;
      }
    }
    this.objectMappers = builder.immutableMap();
    this.fieldMappers = this.fieldMappers.copyAndAllAll(fieldMappers);

    // finally update for the entire index
    mapperService.addMappers(objectMappers, fieldMappers);
  }
Beispiel #10
0
  public DocumentMapper(
      String index,
      @Nullable Settings indexSettings,
      DocumentMapperParser docMapperParser,
      RootObjectMapper rootObjectMapper,
      ImmutableMap<String, Object> meta,
      NamedAnalyzer indexAnalyzer,
      NamedAnalyzer searchAnalyzer,
      NamedAnalyzer searchQuoteAnalyzer,
      Map<Class<? extends RootMapper>, RootMapper> rootMappers) {
    this.index = index;
    this.indexSettings = indexSettings;
    this.type = rootObjectMapper.name();
    this.docMapperParser = docMapperParser;
    this.meta = meta;
    this.rootObjectMapper = rootObjectMapper;

    this.rootMappers = ImmutableMap.copyOf(rootMappers);
    this.rootMappersOrdered =
        rootMappers.values().toArray(new RootMapper[rootMappers.values().size()]);
    List<RootMapper> rootMappersNotIncludedInObjectLst = newArrayList();
    for (RootMapper rootMapper : rootMappersOrdered) {
      if (!rootMapper.includeInObject()) {
        rootMappersNotIncludedInObjectLst.add(rootMapper);
      }
    }
    this.rootMappersNotIncludedInObject =
        rootMappersNotIncludedInObjectLst.toArray(
            new RootMapper[rootMappersNotIncludedInObjectLst.size()]);

    this.indexAnalyzer = indexAnalyzer;
    this.searchAnalyzer = searchAnalyzer;
    this.searchQuoteAnalyzer = searchQuoteAnalyzer != null ? searchQuoteAnalyzer : searchAnalyzer;

    this.typeFilter = typeMapper().termFilter(type, null);

    if (rootMapper(ParentFieldMapper.class) != null) {
      // mark the routing field mapper as required
      rootMapper(RoutingFieldMapper.class).markAsRequired();
    }

    FieldMapperListener.Aggregator fieldMappersAgg = new FieldMapperListener.Aggregator();
    for (RootMapper rootMapper : rootMappersOrdered) {
      if (rootMapper.includeInObject()) {
        rootObjectMapper.putMapper(rootMapper);
      } else {
        if (rootMapper instanceof FieldMapper) {
          fieldMappersAgg.mappers.add((FieldMapper) rootMapper);
        }
      }
    }

    // now traverse and get all the statically defined ones
    rootObjectMapper.traverse(fieldMappersAgg);

    this.fieldMappers = new DocumentFieldMappers(this, fieldMappersAgg.mappers);

    final Map<String, ObjectMapper> objectMappers = Maps.newHashMap();
    rootObjectMapper.traverse(
        new ObjectMapperListener() {
          @Override
          public void objectMapper(ObjectMapper objectMapper) {
            objectMappers.put(objectMapper.fullPath(), objectMapper);
          }
        });
    this.objectMappers = ImmutableMap.copyOf(objectMappers);
    for (ObjectMapper objectMapper : objectMappers.values()) {
      if (objectMapper.nested().isNested()) {
        hasNestedObjects = true;
      }
    }

    refreshSource();
  }
  @Override
  public void merge(final Mapper mergeWith, final MergeResult mergeResult)
      throws MergeMappingException {
    if (!(mergeWith instanceof ObjectMapper)) {
      mergeResult.addConflict(
          "Can't merge a non object mapping ["
              + mergeWith.name()
              + "] with an object mapping ["
              + name()
              + "]");
      return;
    }
    ObjectMapper mergeWithObject = (ObjectMapper) mergeWith;

    if (nested().isNested()) {
      if (!mergeWithObject.nested().isNested()) {
        mergeResult.addConflict(
            "object mapping [" + name() + "] can't be changed from nested to non-nested");
        return;
      }
    } else {
      if (mergeWithObject.nested().isNested()) {
        mergeResult.addConflict(
            "object mapping [" + name() + "] can't be changed from non-nested to nested");
        return;
      }
    }

    if (!mergeResult.simulate()) {
      if (mergeWithObject.dynamic != null) {
        this.dynamic = mergeWithObject.dynamic;
      }
    }

    doMerge(mergeWithObject, mergeResult);

    List<Mapper> mappersToPut = new ArrayList<>();
    List<ObjectMapper> newObjectMappers = new ArrayList<>();
    List<FieldMapper> newFieldMappers = new ArrayList<>();
    for (Mapper mapper : mergeWithObject) {
      Mapper mergeWithMapper = mapper;
      Mapper mergeIntoMapper = mappers.get(mergeWithMapper.simpleName());
      if (mergeIntoMapper == null) {
        // no mapping, simply add it if not simulating
        if (!mergeResult.simulate()) {
          mappersToPut.add(mergeWithMapper);
          MapperUtils.collect(mergeWithMapper, newObjectMappers, newFieldMappers);
        }
      } else if (mergeIntoMapper instanceof MetadataFieldMapper == false) {
        // root mappers can only exist here for backcompat, and are merged in Mapping
        mergeIntoMapper.merge(mergeWithMapper, mergeResult);
      }
    }
    if (!newFieldMappers.isEmpty()) {
      mergeResult.addFieldMappers(newFieldMappers);
    }
    if (!newObjectMappers.isEmpty()) {
      mergeResult.addObjectMappers(newObjectMappers);
    }
    // add the mappers only after the administration have been done, so it will not be visible to
    // parser (which first try to read with no lock)
    for (Mapper mapper : mappersToPut) {
      putMapper(mapper);
    }
  }
  @Override
  public void merge(final Mapper mergeWith, final MergeContext mergeContext)
      throws MergeMappingException {
    if (!(mergeWith instanceof ObjectMapper)) {
      mergeContext.addConflict(
          "Can't merge a non object mapping ["
              + mergeWith.name()
              + "] with an object mapping ["
              + name()
              + "]");
      return;
    }
    ObjectMapper mergeWithObject = (ObjectMapper) mergeWith;

    if (nested().isNested()) {
      if (!mergeWithObject.nested().isNested()) {
        mergeContext.addConflict(
            "object mapping [" + name() + "] can't be changed from nested to non-nested");
        return;
      }
    } else {
      if (mergeWithObject.nested().isNested()) {
        mergeContext.addConflict(
            "object mapping [" + name() + "] can't be changed from non-nested to nested");
        return;
      }
    }

    doMerge(mergeWithObject, mergeContext);

    List<Mapper> mappersToTraverse = new ArrayList<Mapper>();
    synchronized (mutex) {
      for (Mapper mergeWithMapper : mergeWithObject.mappers.values()) {
        Mapper mergeIntoMapper = mappers.get(mergeWithMapper.name());
        if (mergeIntoMapper == null) {
          // no mapping, simply add it if not simulating
          if (!mergeContext.mergeFlags().simulate()) {
            putMapper(mergeWithMapper);
            mappersToTraverse.add(mergeWithMapper);
          }
        } else {
          if ((mergeWithMapper instanceof MultiFieldMapper)
              && !(mergeIntoMapper instanceof MultiFieldMapper)) {
            MultiFieldMapper mergeWithMultiField = (MultiFieldMapper) mergeWithMapper;
            mergeWithMultiField.merge(mergeIntoMapper, mergeContext);
            if (!mergeContext.mergeFlags().simulate()) {
              putMapper(mergeWithMultiField);
              // now, record mappers to traverse events for all mappers
              for (Mapper mapper : mergeWithMultiField.mappers().values()) {
                mappersToTraverse.add(mapper);
              }
            }
          } else {
            mergeIntoMapper.merge(mergeWithMapper, mergeContext);
          }
        }
      }
    }
    // call this outside of the mutex
    for (Mapper mapper : mappersToTraverse) {
      mapper.traverse(mergeContext.newFieldMappers());
      mapper.traverse(mergeContext.newObjectMappers());
    }
  }
  /** Creates an copy of the current field with given field name and boost */
  private static void parseCopy(String field, ParseContext context) throws IOException {
    FieldMapper fieldMapper = context.docMapper().mappers().getMapper(field);
    if (fieldMapper != null) {
      fieldMapper.parse(context);
    } else {
      // The path of the dest field might be completely different from the current one so we need to
      // reset it
      context = context.overridePath(new ContentPath(0));

      String[] paths = Strings.splitStringToArray(field, '.');
      String fieldName = paths[paths.length - 1];
      ObjectMapper mapper = context.root();
      ObjectMapper[] mappers = new ObjectMapper[paths.length - 1];
      if (paths.length > 1) {
        ObjectMapper parent = context.root();
        for (int i = 0; i < paths.length - 1; i++) {
          mapper = context.docMapper().objectMappers().get(context.path().pathAsText(paths[i]));
          if (mapper == null) {
            // One mapping is missing, check if we are allowed to create a dynamic one.
            ObjectMapper.Dynamic dynamic = parent.dynamic();
            if (dynamic == null) {
              dynamic = dynamicOrDefault(context.root().dynamic());
            }

            switch (dynamic) {
              case STRICT:
                throw new StrictDynamicMappingException(parent.fullPath(), paths[i]);
              case TRUE:
                Mapper.Builder builder =
                    context.root().findTemplateBuilder(context, paths[i], "object");
                if (builder == null) {
                  // if this is a non root object, then explicitly set the dynamic behavior if set
                  if (!(parent instanceof RootObjectMapper)
                      && parent.dynamic() != ObjectMapper.Defaults.DYNAMIC) {
                    ((ObjectMapper.Builder) builder).dynamic(parent.dynamic());
                  }
                  builder = MapperBuilders.object(paths[i]).enabled(true);
                }
                Mapper.BuilderContext builderContext =
                    new Mapper.BuilderContext(context.indexSettings(), context.path());
                mapper = (ObjectMapper) builder.build(builderContext);
                if (mapper.nested() != ObjectMapper.Nested.NO) {
                  throw new MapperParsingException(
                      "It is forbidden to create dynamic nested objects (["
                          + context.path().pathAsText(paths[i])
                          + "]) through `copy_to`");
                }
                break;
              case FALSE:
                // Maybe we should log something to tell the user that the copy_to is ignored in
                // this case.
                break;
              default:
                throw new AssertionError("Unexpected dynamic type " + dynamic);
            }
          }
          context.path().add(paths[i]);
          mappers[i] = mapper;
          parent = mapper;
        }
      }
      ObjectMapper update =
          parseDynamicValue(context, mapper, fieldName, context.parser().currentToken());
      assert update
          != null; // we are parsing a dynamic value so we necessarily created a new mapping

      if (paths.length > 1) {
        for (int i = paths.length - 2; i >= 0; i--) {
          ObjectMapper parent = context.root();
          if (i > 0) {
            parent = mappers[i - 1];
          }
          assert parent != null;
          update = parent.mappingUpdate(update);
        }
      }
      context.addDynamicMappingsUpdate(update);
    }
  }
  private void addSortField(
      SearchContext context,
      List<SortField> sortFields,
      String fieldName,
      boolean reverse,
      boolean ignoreUnmapped,
      @Nullable final String missing,
      SortMode sortMode,
      String nestedPath,
      Filter nestedFilter) {
    if (SCORE_FIELD_NAME.equals(fieldName)) {
      if (reverse) {
        sortFields.add(SORT_SCORE_REVERSE);
      } else {
        sortFields.add(SORT_SCORE);
      }
    } else if (DOC_FIELD_NAME.equals(fieldName)) {
      if (reverse) {
        sortFields.add(SORT_DOC_REVERSE);
      } else {
        sortFields.add(SORT_DOC);
      }
    } else {
      FieldMapper fieldMapper = context.smartNameFieldMapper(fieldName);
      if (fieldMapper == null) {
        if (ignoreUnmapped) {
          return;
        }
        throw new SearchParseException(
            context, "No mapping found for [" + fieldName + "] in order to sort on");
      }

      // Enable when we also know how to detect fields that do tokenize, but only emit one token
      /*if (fieldMapper instanceof StringFieldMapper) {
          StringFieldMapper stringFieldMapper = (StringFieldMapper) fieldMapper;
          if (stringFieldMapper.fieldType().tokenized()) {
              // Fail early
              throw new SearchParseException(context, "Can't sort on tokenized string field[" + fieldName + "]");
          }
      }*/

      // We only support AVG and SUM on number based fields
      if (!(fieldMapper instanceof NumberFieldMapper)
          && (sortMode == SortMode.SUM || sortMode == SortMode.AVG)) {
        sortMode = null;
      }
      if (sortMode == null) {
        sortMode = resolveDefaultSortMode(reverse);
      }

      IndexFieldData.XFieldComparatorSource fieldComparatorSource =
          context.fieldData().getForField(fieldMapper).comparatorSource(missing, sortMode);
      ObjectMapper objectMapper;
      if (nestedPath != null) {
        ObjectMappers objectMappers = context.mapperService().objectMapper(nestedPath);
        if (objectMappers == null) {
          throw new ElasticSearchIllegalArgumentException(
              "failed to find nested object mapping for explicit nested path [" + nestedPath + "]");
        }
        objectMapper = objectMappers.mapper();
        if (!objectMapper.nested().isNested()) {
          throw new ElasticSearchIllegalArgumentException(
              "mapping for explicit nested path is not mapped as nested: [" + nestedPath + "]");
        }
      } else {
        objectMapper = context.mapperService().resolveClosestNestedObjectMapper(fieldName);
      }
      if (objectMapper != null && objectMapper.nested().isNested()) {
        Filter rootDocumentsFilter = context.filterCache().cache(NonNestedDocsFilter.INSTANCE);
        Filter innerDocumentsFilter;
        if (nestedFilter != null) {
          innerDocumentsFilter = context.filterCache().cache(nestedFilter);
        } else {
          innerDocumentsFilter = context.filterCache().cache(objectMapper.nestedTypeFilter());
        }
        fieldComparatorSource =
            new NestedFieldComparatorSource(
                sortMode, fieldComparatorSource, rootDocumentsFilter, innerDocumentsFilter);
      }
      sortFields.add(
          new SortField(fieldMapper.names().indexName(), fieldComparatorSource, reverse));
    }
  }
  public DocumentMapper(
      MapperService mapperService,
      String index,
      @Nullable Settings indexSettings,
      DocumentMapperParser docMapperParser,
      RootObjectMapper rootObjectMapper,
      ImmutableMap<String, Object> meta,
      Map<Class<? extends MetadataFieldMapper>, MetadataFieldMapper> rootMappers,
      List<SourceTransform> sourceTransforms,
      ReentrantReadWriteLock mappingLock) {
    this.mapperService = mapperService;
    this.type = rootObjectMapper.name();
    this.typeText = new StringAndBytesText(this.type);
    this.mapping =
        new Mapping(
            Version.indexCreated(indexSettings),
            rootObjectMapper,
            rootMappers.values().toArray(new MetadataFieldMapper[rootMappers.values().size()]),
            sourceTransforms.toArray(new SourceTransform[sourceTransforms.size()]),
            meta);
    this.documentParser =
        new DocumentParser(
            index,
            indexSettings,
            docMapperParser,
            this,
            new ReleasableLock(mappingLock.readLock()));

    this.typeFilter = typeMapper().fieldType().termQuery(type, null);
    this.mappingWriteLock = new ReleasableLock(mappingLock.writeLock());
    this.mappingLock = mappingLock;

    if (rootMapper(ParentFieldMapper.class).active()) {
      // mark the routing field mapper as required
      rootMapper(RoutingFieldMapper.class).markAsRequired();
    }

    // collect all the mappers for this type
    List<ObjectMapper> newObjectMappers = new ArrayList<>();
    List<FieldMapper> newFieldMappers = new ArrayList<>();
    for (MetadataFieldMapper metadataMapper : this.mapping.metadataMappers) {
      if (metadataMapper instanceof FieldMapper) {
        newFieldMappers.add((FieldMapper) metadataMapper);
      }
    }
    MapperUtils.collect(this.mapping.root, newObjectMappers, newFieldMappers);

    this.fieldMappers =
        new DocumentFieldMappers(docMapperParser.analysisService).copyAndAllAll(newFieldMappers);
    this.objectMappers =
        Maps.uniqueIndex(
            newObjectMappers,
            new Function<ObjectMapper, String>() {
              @Override
              public String apply(ObjectMapper mapper) {
                return mapper.fullPath();
              }
            });
    for (ObjectMapper objectMapper : newObjectMappers) {
      if (objectMapper.nested().isNested()) {
        hasNestedObjects = true;
      }
    }

    refreshSource();
  }
  @Override
  public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
    XContentParser parser = parseContext.parser();

    Query query = null;
    Filter filter = null;
    float boost = 1.0f;
    String scope = null;
    String path = null;
    boolean cache = false;
    String filterName = null;

    // we need a late binding filter so we can inject a parent nested filter inner nested queries
    NestedQueryParser.LateBindingParentFilter currentParentFilterContext =
        NestedQueryParser.parentFilterContext.get();

    NestedQueryParser.LateBindingParentFilter usAsParentFilter =
        new NestedQueryParser.LateBindingParentFilter();
    NestedQueryParser.parentFilterContext.set(usAsParentFilter);

    String currentFieldName = null;
    XContentParser.Token token;
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if ("query".equals(currentFieldName)) {
          query = parseContext.parseInnerQuery();
        } else if ("filter".equals(currentFieldName)) {
          filter = parseContext.parseInnerFilter();
        }
      } else if (token.isValue()) {
        if ("path".equals(currentFieldName)) {
          path = parser.text();
        } else if ("boost".equals(currentFieldName)) {
          boost = parser.floatValue();
        } else if ("_scope".equals(currentFieldName)) {
          scope = parser.text();
        } else if ("_name".equals(currentFieldName)) {
          filterName = parser.text();
        } else if ("_cache".equals(currentFieldName)) {
          cache = parser.booleanValue();
        }
      }
    }
    if (query == null && filter == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] requires either 'query' or 'filter' field");
    }
    if (path == null) {
      throw new QueryParsingException(parseContext.index(), "[nested] requires 'path' field");
    }

    if (filter != null) {
      query = new DeletionAwareConstantScoreQuery(filter);
    }

    query.setBoost(boost);

    MapperService.SmartNameObjectMapper mapper =
        parseContext.mapperService().smartNameObjectMapper(path);
    if (mapper == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
    }
    ObjectMapper objectMapper = mapper.mapper();
    if (objectMapper == null) {
      throw new QueryParsingException(
          parseContext.index(), "[nested] failed to find nested object under path [" + path + "]");
    }
    if (!objectMapper.nested().isNested()) {
      throw new QueryParsingException(
          parseContext.index(),
          "[nested] nested object under path [" + path + "] is not of nested type");
    }

    Filter childFilter = parseContext.cacheFilter(objectMapper.nestedTypeFilter());
    usAsParentFilter.filter = childFilter;
    // wrap the child query to only work on the nested path type
    query = new FilteredQuery(query, childFilter);

    Filter parentFilter = currentParentFilterContext;
    if (parentFilter == null) {
      parentFilter = NonNestedDocsFilter.INSTANCE;
      if (mapper.hasDocMapper()) {
        // filter based on the type...
        parentFilter = mapper.docMapper().typeFilter();
      }
      parentFilter = parseContext.cacheFilter(parentFilter);
    }

    // restore the thread local one...
    NestedQueryParser.parentFilterContext.set(currentParentFilterContext);

    BlockJoinQuery joinQuery =
        new BlockJoinQuery(query, parentFilter, BlockJoinQuery.ScoreMode.None);

    if (scope != null) {
      SearchContext.current().addNestedQuery(scope, joinQuery);
    }

    Filter joinFilter = new QueryWrapperFilter(joinQuery);
    if (cache) {
      joinFilter = parseContext.cacheFilter(joinFilter);
    }
    if (filterName != null) {
      parseContext.addNamedFilter(filterName, joinFilter);
    }
    return joinFilter;
  }
  private synchronized DocumentMapper merge(
      DocumentMapper mapper, MergeReason reason, boolean updateAllTypes) {
    if (mapper.type().length() == 0) {
      throw new InvalidTypeNameException("mapping type name is empty");
    }
    if (mapper.type().length() > 255) {
      throw new InvalidTypeNameException(
          "mapping type name ["
              + mapper.type()
              + "] is too long; limit is length 255 but was ["
              + mapper.type().length()
              + "]");
    }
    if (mapper.type().charAt(0) == '_') {
      throw new InvalidTypeNameException(
          "mapping type name [" + mapper.type() + "] can't start with '_'");
    }
    if (mapper.type().contains("#")) {
      throw new InvalidTypeNameException(
          "mapping type name [" + mapper.type() + "] should not include '#' in it");
    }
    if (mapper.type().contains(",")) {
      throw new InvalidTypeNameException(
          "mapping type name [" + mapper.type() + "] should not include ',' in it");
    }
    if (mapper.type().equals(mapper.parentFieldMapper().type())) {
      throw new IllegalArgumentException("The [_parent.type] option can't point to the same type");
    }
    if (typeNameStartsWithIllegalDot(mapper)) {
      throw new IllegalArgumentException(
          "mapping type name [" + mapper.type() + "] must not start with a '.'");
    }

    // 1. compute the merged DocumentMapper
    DocumentMapper oldMapper = mappers.get(mapper.type());
    DocumentMapper newMapper;
    if (oldMapper != null) {
      newMapper = oldMapper.merge(mapper.mapping(), updateAllTypes);
    } else {
      newMapper = mapper;
    }

    // 2. check basic sanity of the new mapping
    List<ObjectMapper> objectMappers = new ArrayList<>();
    List<FieldMapper> fieldMappers = new ArrayList<>();
    Collections.addAll(fieldMappers, newMapper.mapping().metadataMappers);
    MapperUtils.collect(newMapper.mapping().root(), objectMappers, fieldMappers);
    checkFieldUniqueness(newMapper.type(), objectMappers, fieldMappers);
    checkObjectsCompatibility(newMapper.type(), objectMappers, fieldMappers, updateAllTypes);

    // 3. update lookup data-structures
    // this will in particular make sure that the merged fields are compatible with other types
    FieldTypeLookup fieldTypes =
        this.fieldTypes.copyAndAddAll(newMapper.type(), fieldMappers, updateAllTypes);

    boolean hasNested = this.hasNested;
    Map<String, ObjectMapper> fullPathObjectMappers = new HashMap<>(this.fullPathObjectMappers);
    for (ObjectMapper objectMapper : objectMappers) {
      fullPathObjectMappers.put(objectMapper.fullPath(), objectMapper);
      if (objectMapper.nested().isNested()) {
        hasNested = true;
      }
    }
    fullPathObjectMappers = Collections.unmodifiableMap(fullPathObjectMappers);

    if (reason == MergeReason.MAPPING_UPDATE) {
      // this check will only be performed on the master node when there is
      // a call to the update mapping API. For all other cases like
      // the master node restoring mappings from disk or data nodes
      // deserializing cluster state that was sent by the master node,
      // this check will be skipped.
      checkNestedFieldsLimit(fullPathObjectMappers);
      checkTotalFieldsLimit(objectMappers.size() + fieldMappers.size());
      checkDepthLimit(fullPathObjectMappers.keySet());
      checkPercolatorFieldLimit(fieldTypes);
    }

    Set<String> parentTypes = this.parentTypes;
    if (oldMapper == null && newMapper.parentFieldMapper().active()) {
      parentTypes = new HashSet<>(parentTypes.size() + 1);
      parentTypes.addAll(this.parentTypes);
      parentTypes.add(mapper.parentFieldMapper().type());
      parentTypes = Collections.unmodifiableSet(parentTypes);
    }

    Map<String, DocumentMapper> mappers = new HashMap<>(this.mappers);
    mappers.put(newMapper.type(), newMapper);
    for (Map.Entry<String, DocumentMapper> entry : mappers.entrySet()) {
      if (entry.getKey().equals(DEFAULT_MAPPING)) {
        continue;
      }
      DocumentMapper m = entry.getValue();
      // apply changes to the field types back
      m = m.updateFieldType(fieldTypes.fullNameToFieldType);
      entry.setValue(m);
    }
    mappers = Collections.unmodifiableMap(mappers);

    // 4. commit the change
    this.mappers = mappers;
    this.fieldTypes = fieldTypes;
    this.hasNested = hasNested;
    this.fullPathObjectMappers = fullPathObjectMappers;
    this.parentTypes = parentTypes;

    assert assertSerialization(newMapper);
    assert assertMappersShareSameFieldType();

    return newMapper;
  }
  static ObjectMapper parseObject(ParseContext context, ObjectMapper mapper, boolean atRoot)
      throws IOException {
    if (mapper.isEnabled() == false) {
      context.parser().skipChildren();
      return null;
    }
    XContentParser parser = context.parser();

    String currentFieldName = parser.currentName();
    if (atRoot
        && MapperService.isMetadataField(currentFieldName)
        && Version.indexCreated(context.indexSettings()).onOrAfter(Version.V_2_0_0_beta1)) {
      throw new MapperParsingException(
          "Field ["
              + currentFieldName
              + "] is a metadata field and cannot be added inside a document. Use the index API request parameters.");
    }
    XContentParser.Token token = parser.currentToken();
    if (token == XContentParser.Token.VALUE_NULL) {
      // the object is null ("obj1" : null), simply bail
      return null;
    }

    if (token.isValue()) {
      throw new MapperParsingException(
          "object mapping for ["
              + mapper.name()
              + "] tried to parse field ["
              + currentFieldName
              + "] as object, but found a concrete value");
    }

    ObjectMapper.Nested nested = mapper.nested();
    if (nested.isNested()) {
      context = context.createNestedContext(mapper.fullPath());
      ParseContext.Document nestedDoc = context.doc();
      ParseContext.Document parentDoc = nestedDoc.getParent();
      // pre add the uid field if possible (id was already provided)
      IndexableField uidField = parentDoc.getField(UidFieldMapper.NAME);
      if (uidField != null) {
        // we don't need to add it as a full uid field in nested docs, since we don't need
        // versioning
        // we also rely on this for UidField#loadVersion

        // this is a deeply nested field
        nestedDoc.add(
            new Field(
                UidFieldMapper.NAME,
                uidField.stringValue(),
                UidFieldMapper.Defaults.NESTED_FIELD_TYPE));
      }
      // the type of the nested doc starts with __, so we can identify that its a nested one in
      // filters
      // note, we don't prefix it with the type of the doc since it allows us to execute a nested
      // query
      // across types (for example, with similar nested objects)
      nestedDoc.add(
          new Field(
              TypeFieldMapper.NAME,
              mapper.nestedTypePathAsString(),
              TypeFieldMapper.Defaults.FIELD_TYPE));
    }

    // if we are at the end of the previous object, advance
    if (token == XContentParser.Token.END_OBJECT) {
      token = parser.nextToken();
    }
    if (token == XContentParser.Token.START_OBJECT) {
      // if we are just starting an OBJECT, advance, this is the object we are parsing, we need the
      // name first
      token = parser.nextToken();
    }

    ObjectMapper update = null;
    while (token != XContentParser.Token.END_OBJECT) {
      ObjectMapper newUpdate = null;
      if (token == XContentParser.Token.START_OBJECT) {
        newUpdate = parseObject(context, mapper, currentFieldName);
      } else if (token == XContentParser.Token.START_ARRAY) {
        newUpdate = parseArray(context, mapper, currentFieldName);
      } else if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.VALUE_NULL) {
        parseNullValue(context, mapper, currentFieldName);
      } else if (token == null) {
        throw new MapperParsingException(
            "object mapping for ["
                + mapper.name()
                + "] tried to parse field ["
                + currentFieldName
                + "] as object, but got EOF, has a concrete value been provided to it?");
      } else if (token.isValue()) {
        newUpdate = parseValue(context, mapper, currentFieldName, token);
      }
      token = parser.nextToken();
      if (newUpdate != null) {
        if (update == null) {
          update = newUpdate;
        } else {
          update = update.merge(newUpdate, false);
        }
      }
    }
    // restore the enable path flag
    if (nested.isNested()) {
      ParseContext.Document nestedDoc = context.doc();
      ParseContext.Document parentDoc = nestedDoc.getParent();
      if (nested.isIncludeInParent()) {
        for (IndexableField field : nestedDoc.getFields()) {
          if (field.name().equals(UidFieldMapper.NAME)
              || field.name().equals(TypeFieldMapper.NAME)) {
            continue;
          } else {
            parentDoc.add(field);
          }
        }
      }
      if (nested.isIncludeInRoot()) {
        ParseContext.Document rootDoc = context.rootDoc();
        // don't add it twice, if its included in parent, and we are handling the master doc...
        if (!nested.isIncludeInParent() || parentDoc != rootDoc) {
          for (IndexableField field : nestedDoc.getFields()) {
            if (field.name().equals(UidFieldMapper.NAME)
                || field.name().equals(TypeFieldMapper.NAME)) {
              continue;
            } else {
              rootDoc.add(field);
            }
          }
        }
      }
    }
    return update;
  }
Beispiel #19
0
  private InternalSearchHit.InternalNestedIdentity getInternalNestedIdentity(
      SearchContext context,
      int nestedSubDocId,
      LeafReaderContext subReaderContext,
      DocumentMapper documentMapper,
      ObjectMapper nestedObjectMapper)
      throws IOException {
    int currentParent = nestedSubDocId;
    ObjectMapper nestedParentObjectMapper;
    StringBuilder field = new StringBuilder();
    ObjectMapper current = nestedObjectMapper;
    InternalSearchHit.InternalNestedIdentity nestedIdentity = null;
    do {
      Filter parentFilter;
      nestedParentObjectMapper = documentMapper.findParentObjectMapper(current);
      if (field.length() != 0) {
        field.insert(0, '.');
      }
      field.insert(0, current.name());
      if (nestedParentObjectMapper != null) {
        if (nestedParentObjectMapper.nested().isNested() == false) {
          current = nestedParentObjectMapper;
          continue;
        }
        parentFilter = nestedParentObjectMapper.nestedTypeFilter();
      } else {
        parentFilter = Queries.newNonNestedFilter();
      }

      Filter childFilter = nestedObjectMapper.nestedTypeFilter();
      if (childFilter == null) {
        current = nestedParentObjectMapper;
        continue;
      }
      // We can pass down 'null' as acceptedDocs, because we're fetching matched docId that matched
      // in the query phase.
      DocIdSet childDocSet = childFilter.getDocIdSet(subReaderContext, null);
      if (childDocSet == null) {
        current = nestedParentObjectMapper;
        continue;
      }
      DocIdSetIterator childIter = childDocSet.iterator();
      if (childIter == null) {
        current = nestedParentObjectMapper;
        continue;
      }

      BitDocIdSet parentBitSet =
          context
              .bitsetFilterCache()
              .getBitDocIdSetFilter(parentFilter)
              .getDocIdSet(subReaderContext);
      BitSet parentBits = parentBitSet.bits();

      int offset = 0;
      int nextParent = parentBits.nextSetBit(currentParent);
      for (int docId = childIter.advance(currentParent + 1);
          docId < nextParent && docId != DocIdSetIterator.NO_MORE_DOCS;
          docId = childIter.nextDoc()) {
        offset++;
      }
      currentParent = nextParent;
      current = nestedObjectMapper = nestedParentObjectMapper;
      nestedIdentity =
          new InternalSearchHit.InternalNestedIdentity(field.toString(), offset, nestedIdentity);
      field = new StringBuilder();
    } while (current != null);
    return nestedIdentity;
  }
  @Override
  public void merge(final Mapper mergeWith, final MergeContext mergeContext)
      throws MergeMappingException {
    if (!(mergeWith instanceof ObjectMapper)) {
      mergeContext.addConflict(
          "Can't merge a non object mapping ["
              + mergeWith.name()
              + "] with an object mapping ["
              + name()
              + "]");
      return;
    }
    ObjectMapper mergeWithObject = (ObjectMapper) mergeWith;

    if (nested().isNested()) {
      if (!mergeWithObject.nested().isNested()) {
        mergeContext.addConflict(
            "object mapping [" + name() + "] can't be changed from nested to non-nested");
        return;
      }
    } else {
      if (mergeWithObject.nested().isNested()) {
        mergeContext.addConflict(
            "object mapping [" + name() + "] can't be changed from non-nested to nested");
        return;
      }
    }

    if (!mergeContext.mergeFlags().simulate()) {
      if (mergeWithObject.dynamic != null) {
        this.dynamic = mergeWithObject.dynamic;
      }
    }

    doMerge(mergeWithObject, mergeContext);

    List<Mapper> mappersToPut = new ArrayList<>();
    FieldMapperListener.Aggregator newFieldMappers = new FieldMapperListener.Aggregator();
    ObjectMapperListener.Aggregator newObjectMappers = new ObjectMapperListener.Aggregator();
    synchronized (mutex) {
      for (Mapper mapper : mergeWithObject.mappers.values()) {
        Mapper mergeWithMapper = mapper;
        Mapper mergeIntoMapper = mappers.get(mergeWithMapper.name());
        if (mergeIntoMapper == null) {
          // no mapping, simply add it if not simulating
          if (!mergeContext.mergeFlags().simulate()) {
            mappersToPut.add(mergeWithMapper);
            mergeWithMapper.traverse(newFieldMappers);
            mergeWithMapper.traverse(newObjectMappers);
          }
        } else {
          mergeIntoMapper.merge(mergeWithMapper, mergeContext);
        }
      }
      if (!newFieldMappers.mappers.isEmpty()) {
        mergeContext.docMapper().addFieldMappers(newFieldMappers.mappers);
      }
      if (!newObjectMappers.mappers.isEmpty()) {
        mergeContext.docMapper().addObjectMappers(newObjectMappers.mappers);
      }
      // and the mappers only after the administration have been done, so it will not be visible to
      // parser (which first try to read with no lock)
      for (Mapper mapper : mappersToPut) {
        putMapper(mapper);
      }
    }
  }