@Override public Query termsQuery(List values, @Nullable QueryShardContext context) { if (context == null) { return super.termsQuery(values, context); } List<String> types = new ArrayList<>(context.getMapperService().types().size()); for (DocumentMapper documentMapper : context.getMapperService().docMappers(false)) { if (!documentMapper.parentFieldMapper().active()) { types.add(documentMapper.type()); } } List<BytesRef> bValues = new ArrayList<>(values.size()); for (Object value : values) { BytesRef bValue = BytesRefs.toBytesRef(value); if (Uid.hasDelimiter(bValue)) { bValues.add(bValue); } else { // we use all non child types, cause we don't know if its exact or not... for (String type : types) { bValues.add(Uid.createUidAsBytes(type, bValue)); } } } return new TermsQuery(name(), bValues); }
@Override public GetResult get(Get get, Function<String, Searcher> searcherFactory) throws EngineException { try (ReleasableLock lock = readLock.acquire()) { ensureOpen(); if (get.realtime()) { VersionValue versionValue = versionMap.getUnderLock(get.uid().bytes()); if (versionValue != null) { if (versionValue.delete()) { return GetResult.NOT_EXISTS; } if (get.versionType().isVersionConflictForReads(versionValue.version(), get.version())) { Uid uid = Uid.createUid(get.uid().text()); throw new VersionConflictEngineException( shardId, uid.type(), uid.id(), get.versionType().explainConflictForReads(versionValue.version(), get.version())); } Translog.Operation op = translog.read(versionValue.translogLocation()); if (op != null) { return new GetResult(true, versionValue.version(), op.getSource()); } } } // no version, get the version from the index, we know that we refresh on flush return getFromSearcher(get, searcherFactory); } }
@Override public Filter regexpFilter( Object value, int flags, int maxDeterminizedStates, @Nullable QueryParseContext context) { if (fieldType.indexOptions() != IndexOptions.NONE || context == null) { return super.regexpFilter(value, flags, maxDeterminizedStates, context); } Collection<String> queryTypes = context.queryTypes(); if (queryTypes.size() == 1) { return new RegexpFilter( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes( Iterables.getFirst(queryTypes, null), BytesRefs.toBytesRef(value))), flags, maxDeterminizedStates); } XBooleanFilter filter = new XBooleanFilter(); for (String queryType : queryTypes) { filter.add( new RegexpFilter( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes(queryType, BytesRefs.toBytesRef(value))), flags, maxDeterminizedStates), BooleanClause.Occur.SHOULD); } return filter; }
@Override public Query prefixQuery( Object value, @Nullable MultiTermQuery.RewriteMethod method, @Nullable QueryParseContext context) { if (fieldType.indexOptions() != IndexOptions.NONE || context == null) { return super.prefixQuery(value, method, context); } Collection<String> queryTypes = context.queryTypes(); if (queryTypes.size() == 1) { PrefixQuery prefixQuery = new PrefixQuery( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes( Iterables.getFirst(queryTypes, null), BytesRefs.toBytesRef(value)))); if (method != null) { prefixQuery.setRewriteMethod(method); } return prefixQuery; } BooleanQuery query = new BooleanQuery(); for (String queryType : queryTypes) { PrefixQuery prefixQuery = new PrefixQuery( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes(queryType, BytesRefs.toBytesRef(value)))); if (method != null) { prefixQuery.setRewriteMethod(method); } query.add(prefixQuery, BooleanClause.Occur.SHOULD); } return query; }
@Override protected Field parseCreateField(ParseContext context) throws IOException { if (context.parser().currentName() != null && context.parser().currentName().equals(Defaults.NAME)) { // we are in the parsing of _parent phase String parentId = context.parser().text(); context.sourceToParse().parent(parentId); return new Field( names.indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType); } // otherwise, we are running it post processing of the xcontent String parsedParentId = context.doc().get(Defaults.NAME); if (context.sourceToParse().parent() != null) { String parentId = context.sourceToParse().parent(); if (parsedParentId == null) { if (parentId == null) { throw new MapperParsingException( "No parent id provided, not within the document, and not externally"); } // we did not add it in the parsing phase, add it now return new Field( names.indexName(), Uid.createUid(context.stringBuilder(), type, parentId), fieldType); } else if (parentId != null && !parsedParentId.equals(Uid.createUid(context.stringBuilder(), type, parentId))) { throw new MapperParsingException( "Parent id mismatch, document value is [" + Uid.createUid(parsedParentId).id() + "], while external value is [" + parentId + "]"); } } // we have parent mapping, yet no value was set, ignore it... return null; }
@Test public void testSimple() throws Exception { Directory directory = newDirectory(); RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); for (int parent = 1; parent <= 5; parent++) { Document document = new Document(); document.add( new StringField( UidFieldMapper.NAME, Uid.createUid("parent", Integer.toString(parent)), Field.Store.NO)); document.add(new StringField(TypeFieldMapper.NAME, "parent", Field.Store.NO)); indexWriter.addDocument(document); for (int child = 1; child <= 3; child++) { document = new Document(); document.add( new StringField( UidFieldMapper.NAME, Uid.createUid("child", Integer.toString(parent * 3 + child)), Field.Store.NO)); document.add(new StringField(TypeFieldMapper.NAME, "child", Field.Store.NO)); document.add( new StringField( ParentFieldMapper.NAME, Uid.createUid("parent", Integer.toString(parent)), Field.Store.NO)); document.add(new StringField("field1", "value" + child, Field.Store.NO)); indexWriter.addDocument(document); } } IndexReader indexReader = DirectoryReader.open(indexWriter.w, false); IndexSearcher searcher = new IndexSearcher(indexReader); TermQuery childQuery = new TermQuery(new Term("field1", "value" + (1 + random().nextInt(3)))); TermFilter parentFilter = new TermFilter(new Term(TypeFieldMapper.NAME, "parent")); int shortCircuitParentDocSet = random().nextInt(5); ChildrenConstantScoreQuery query = new ChildrenConstantScoreQuery( childQuery, "parent", "child", parentFilter, shortCircuitParentDocSet, null); BitSetCollector collector = new BitSetCollector(indexReader.maxDoc()); searcher.search(query, collector); FixedBitSet actualResult = collector.getResult(); assertThat(actualResult.cardinality(), equalTo(5)); indexWriter.close(); indexReader.close(); directory.close(); }
@Override protected void parseCreateField(ParseContext context, List<Field> fields) throws IOException { boolean parent = context.docMapper().isParent(context.type()); if (parent) { addJoinFieldIfNeeded(fields, parentJoinFieldType, context.id()); } if (!active()) { return; } if (context.parser().currentName() != null && context.parser().currentName().equals(Defaults.NAME)) { // we are in the parsing of _parent phase String parentId = context.parser().text(); context.sourceToParse().parent(parentId); fields.add( new Field( fieldType().name(), Uid.createUid(context.stringBuilder(), parentType, parentId), fieldType())); addJoinFieldIfNeeded(fields, childJoinFieldType, parentId); } else { // otherwise, we are running it post processing of the xcontent String parsedParentId = context.doc().get(Defaults.NAME); if (context.sourceToParse().parent() != null) { String parentId = context.sourceToParse().parent(); if (parsedParentId == null) { if (parentId == null) { throw new MapperParsingException( "No parent id provided, not within the document, and not externally"); } // we did not add it in the parsing phase, add it now fields.add( new Field( fieldType().name(), Uid.createUid(context.stringBuilder(), parentType, parentId), fieldType())); addJoinFieldIfNeeded(fields, childJoinFieldType, parentId); } else if (parentId != null && !parsedParentId.equals( Uid.createUid(context.stringBuilder(), parentType, parentId))) { throw new MapperParsingException( "Parent id mismatch, document value is [" + Uid.createUid(parsedParentId).id() + "], while external value is [" + parentId + "]"); } } } // we have parent mapping, yet no value was set, ignore it... }
@Override public void collect(int doc) { try { FieldsVisitor fieldsVisitor = new FieldsVisitor(false); context.reader().document(doc, fieldsVisitor); Uid uid = fieldsVisitor.uid(); final long version = Versions.loadVersion(context.reader(), new Term(UidFieldMapper.NAME, uid.toBytesRef())); docsToPurge.add(new DocToPurge(uid.type(), uid.id(), version, fieldsVisitor.routing())); } catch (Exception e) { logger.trace("failed to collect doc", e); } }
@Override public BytesRef indexedValueForSearch(Object value) { if (value instanceof BytesRef) { BytesRef bytesRef = (BytesRef) value; if (Uid.hasDelimiter(bytesRef)) { return bytesRef; } return Uid.createUidAsBytes(typeAsBytes, bytesRef); } String sValue = value.toString(); if (sValue.indexOf(Uid.DELIMITER) == -1) { return Uid.createUidAsBytes(type, sValue); } return super.indexedValueForSearch(value); }
@Nullable private Function rewriteAndValidateFields(Function function, Context context) { if (function.arguments().size() == 2) { Symbol left = function.arguments().get(0); Symbol right = function.arguments().get(1); if (left.symbolType() == SymbolType.REFERENCE && right.symbolType().isValueSymbol()) { Reference ref = (Reference) left; if (ref.info().ident().columnIdent().equals(DocSysColumns.ID)) { function.setArgument( 0, new Reference(DocSysColumns.forTable(ref.ident().tableIdent(), DocSysColumns.UID))); function.setArgument( 1, Literal.newLiteral( Uid.createUid( Constants.DEFAULT_MAPPING_TYPE, ValueSymbolVisitor.STRING.process(right)))); } else { String unsupportedMessage = context.unsupportedMessage(ref.info().ident().columnIdent().name()); if (unsupportedMessage != null) { throw new UnsupportedFeatureException(unsupportedMessage); } } } } return function; }
@Override public Filter termsFilter(List values, @Nullable QueryParseContext context) { if (fieldType.indexOptions() != IndexOptions.NONE || context == null) { return super.termsFilter(values, context); } return new TermsFilter(UidFieldMapper.NAME, Uid.createTypeUids(context.queryTypes(), values)); }
@Override public Uid value(Object value) { if (value == null) { return null; } return Uid.createUid(value.toString()); }
@Override public Query termQuery(Object value, @Nullable QueryParseContext context) { if (indexOptions() != IndexOptions.NONE || context == null) { return super.termQuery(value, context); } final BytesRef[] uids = Uid.createUidsForTypesAndId(context.queryTypes(), value); return new TermsQuery(UidFieldMapper.NAME, uids); }
@Override public Query termsQuery(List values, @Nullable QueryParseContext context) { if (indexOptions() != IndexOptions.NONE || context == null) { return super.termsQuery(values, context); } return new TermsQuery( UidFieldMapper.NAME, Uid.createUidsForTypesAndIds(context.queryTypes(), values)); }
@Override public void stringField(FieldInfo fieldInfo, String value) throws IOException { if (UidFieldMapper.NAME.equals(fieldInfo.name)) { uid = Uid.createUid(value); } else { addValue(fieldInfo.name, value); } }
@Override public Filter termFilter(Object value, @Nullable QueryParseContext context) { if (!fieldType.indexed()) { return new PrefixFilter( new Term(UidFieldMapper.NAME, Uid.typePrefixAsBytes(BytesRefs.toBytesRef(value)))); } return new TermFilter(names().createIndexNameTerm(BytesRefs.toBytesRef(value))); }
protected ExplainResponse shardOperation(ExplainRequest request, int shardId) throws ElasticSearchException { IndexService indexService = indicesService.indexService(request.index()); IndexShard indexShard = indexService.shardSafe(shardId); Term uidTerm = new Term(UidFieldMapper.NAME, Uid.createUidAsBytes(request.getType(), request.getId())); Engine.GetResult result = indexShard.get(new Engine.Get(false, uidTerm)); if (!result.exists()) { return new ExplainResponse(false); } SearchContext context = new SearchContext( 0, new ShardSearchRequest() .types(new String[] {request.getType()}) .filteringAliases(request.getFilteringAlias()), null, result.searcher(), indexService, indexShard, scriptService); SearchContext.setCurrent(context); try { context.parsedQuery(parseQuery(request, indexService)); context.preProcess(); int topLevelDocId = result.docIdAndVersion().docId + result.docIdAndVersion().reader.docBase; Explanation explanation; if (context.rescore() != null) { RescoreSearchContext ctx = context.rescore(); Rescorer rescorer = ctx.rescorer(); explanation = rescorer.explain(topLevelDocId, context, ctx); } else { explanation = context.searcher().explain(context.query(), topLevelDocId); } if (request.getFields() != null) { if (request.getFields().length == 1 && "_source".equals(request.getFields()[0])) { request.setFields(null); // Load the _source field } // Advantage is that we're not opening a second searcher to retrieve the _source. Also // because we are working in the same searcher in engineGetResult we can be sure that a // doc isn't deleted between the initial get and this call. GetResult getResult = indexShard .getService() .get(result, request.getId(), request.getType(), request.getFields()); return new ExplainResponse(true, explanation, getResult); } else { return new ExplainResponse(true, explanation); } } catch (IOException e) { throw new ElasticSearchException("Could not explain", e); } finally { context.release(); SearchContext.removeCurrent(); } }
@Override public void stringField(FieldInfo fieldInfo, byte[] bytes) throws IOException { final String value = new String(bytes, StandardCharsets.UTF_8); if (UidFieldMapper.NAME.equals(fieldInfo.name)) { uid = Uid.createUid(value); } else { addValue(fieldInfo.name, value); } }
@Override protected Field parseCreateField(ParseContext context) throws IOException { // so, caching uid stream and field is fine // since we don't do any mapping parsing without immediate indexing // and, when percolating, we don't index the uid UidField field = fieldCache.get(); field.setUid(Uid.createUid(context.stringBuilder(), context.type(), context.id())); context.uid(field); return field; // version get updated by the engine }
@Override public TopDocs topDocs(SearchContext context, FetchSubPhase.HitContext hitContext) throws IOException { final String field; final String term; if (isParentHit(hitContext.hit())) { field = ParentFieldMapper.NAME; term = Uid.createUid(hitContext.hit().type(), hitContext.hit().id()); } else if (isChildHit(hitContext.hit())) { field = UidFieldMapper.NAME; SearchHitField parentField = hitContext.hit().field(ParentFieldMapper.NAME); if (parentField != null) { term = parentField.getValue(); } else { SingleFieldsVisitor fieldsVisitor = new SingleFieldsVisitor(ParentFieldMapper.NAME); hitContext.reader().document(hitContext.docId(), fieldsVisitor); if (fieldsVisitor.fields().isEmpty()) { return Lucene.EMPTY_TOP_DOCS; } term = (String) fieldsVisitor.fields().get(ParentFieldMapper.NAME).get(0); } } else { return Lucene.EMPTY_TOP_DOCS; } // Only include docs that have the current hit as parent Filter filter = new TermFilter(new Term(field, term)); // Only include docs that have this inner hits type Filter typeFilter = documentMapper.typeFilter(); if (size() == 0) { TotalHitCountCollector collector = new TotalHitCountCollector(); context .searcher() .search( new XFilteredQuery(query, new AndFilter(Arrays.asList(filter, typeFilter))), collector); return new TopDocs(collector.getTotalHits(), Lucene.EMPTY_SCORE_DOCS, 0); } else { int topN = from() + size(); TopDocsCollector topDocsCollector; if (sort() != null) { topDocsCollector = TopFieldCollector.create(sort(), topN, true, trackScores(), trackScores(), false); } else { topDocsCollector = TopScoreDocCollector.create(topN, false); } context .searcher() .search( new XFilteredQuery(query, new AndFilter(Arrays.asList(filter, typeFilter))), topDocsCollector); return topDocsCollector.topDocs(from(), size()); } }
@Override public Query regexpQuery( String value, int flags, int maxDeterminizedStates, @Nullable MultiTermQuery.RewriteMethod method, @Nullable QueryParseContext context) { if (indexOptions() != IndexOptions.NONE || context == null) { return super.regexpQuery(value, flags, maxDeterminizedStates, method, context); } Collection<String> queryTypes = context.queryTypes(); if (queryTypes.size() == 1) { RegexpQuery regexpQuery = new RegexpQuery( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes( Iterables.getFirst(queryTypes, null), BytesRefs.toBytesRef(value))), flags, maxDeterminizedStates); if (method != null) { regexpQuery.setRewriteMethod(method); } return regexpQuery; } BooleanQuery.Builder query = new BooleanQuery.Builder(); for (String queryType : queryTypes) { RegexpQuery regexpQuery = new RegexpQuery( new Term( UidFieldMapper.NAME, Uid.createUidAsBytes(queryType, BytesRefs.toBytesRef(value))), flags, maxDeterminizedStates); if (method != null) { regexpQuery.setRewriteMethod(method); } query.add(regexpQuery, BooleanClause.Occur.SHOULD); } return query.build(); }
@Override public void collect(int doc) throws IOException { // the _source is the query Document document = reader.document(doc, new UidAndSourceFieldSelector()); String id = Uid.createUid(document.get(UidFieldMapper.NAME)).id(); byte[] source = document.getBinaryValue(SourceFieldMapper.NAME); try { queries.put(id, percolator.parseQuery(id, source, 0, source.length)); } catch (Exception e) { logger.warn("failed to add query [{}]", e, id); } }
@Override protected Fieldable parseCreateField(ParseContext context) throws IOException { if (context.id() == null) { throw new MapperParsingException("No id found while parsing the content source"); } context.uid(Uid.createUid(context.stringBuilder(), context.type(), context.id())); // so, caching uid stream and field is fine // since we don't do any mapping parsing without immediate indexing // and, when percolating, we don't index the uid UidField field = fieldCache.get(); field.setUid(context.uid()); return field; // version get updated by the engine }
@Override protected void parseCreateField(ParseContext context, List<Field> fields) throws IOException { Field uid = new Field( NAME, Uid.createUid(context.stringBuilder(), context.type(), context.id()), Defaults.FIELD_TYPE); context.uid(uid); fields.add(uid); if (fieldType().hasDocValues()) { fields.add(new BinaryDocValuesField(NAME, new BytesRef(uid.stringValue()))); } }
@Override public Filter termFilter(Object value, @Nullable QueryParseContext context) { if (context == null) { return super.termFilter(value, context); } BytesRef bValue = BytesRefs.toBytesRef(value); // we use all types, cause we don't know if its exact or not... BytesRef[] typesValues = new BytesRef[context.mapperService().types().size()]; int i = 0; for (String type : context.mapperService().types()) { typesValues[i++] = Uid.createUidAsBytes(type, bValue); } return new TermsFilter(names.indexName(), typesValues); }
@Override public Filter termsFilter(List values, @Nullable QueryParseContext context) { if (context == null) { return super.termsFilter(values, context); } List<BytesRef> bValues = new ArrayList<BytesRef>(values.size()); for (Object value : values) { BytesRef bValue = BytesRefs.toBytesRef(value); // we use all types, cause we don't know if its exact or not... for (String type : context.mapperService().types()) { bValues.add(Uid.createUidAsBytes(type, bValue)); } } return new TermsFilter(names.indexName(), bValues); }
public void testSimpleParserNoTypeNoId() throws Exception { String mapping = copyToStringFromClasspath("/org/elasticsearch/index/mapper/simple/test-mapping.json"); DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping); BytesReference json = new BytesArray( copyToBytesFromClasspath( "/org/elasticsearch/index/mapper/simple/test1-notype-noid.json")); Document doc = docMapper.parse("test", "person", "1", json).rootDoc(); assertThat( doc.get(docMapper.uidMapper().fieldType().names().indexName()), equalTo(Uid.createUid("person", "1"))); assertThat( doc.get(docMapper.mappers().getMapper("name.first").fieldType().names().indexName()), equalTo("shay")); }
private Set<Uid> getShardDocUIDs(final IndexShard shard) throws IOException { shard.refresh("get_uids"); try (Engine.Searcher searcher = shard.acquireSearcher("test")) { Set<Uid> ids = new HashSet<>(); for (LeafReaderContext leafContext : searcher.reader().leaves()) { LeafReader reader = leafContext.reader(); Bits liveDocs = reader.getLiveDocs(); for (int i = 0; i < reader.maxDoc(); i++) { if (liveDocs == null || liveDocs.get(i)) { Document uuid = reader.document(i, Collections.singleton(UidFieldMapper.NAME)); ids.add(Uid.createUid(uuid.get(UidFieldMapper.NAME))); } } } return ids; } }
@Test public void testSimpleParserNoTypeNoId() throws Exception { String mapping = copyToStringFromClasspath( "/org/elasticsearch/test/unit/index/mapper/simple/test-mapping.json"); DocumentMapper docMapper = MapperTests.newParser().parse(mapping); BytesReference json = new BytesArray( copyToBytesFromClasspath( "/org/elasticsearch/test/unit/index/mapper/simple/test1-notype-noid.json")); Document doc = docMapper.parse("person", "1", json).rootDoc(); assertThat( doc.get(docMapper.uidMapper().names().indexName()), equalTo(Uid.createUid("person", "1"))); assertThat((double) doc.getBoost(), closeTo(3.7, 0.01)); assertThat( doc.get(docMapper.mappers().name("first").mapper().names().indexName()), equalTo("shay")); // System.out.println("Document: " + doc); // System.out.println("Json: " + docMapper.sourceMapper().value(doc)); }
public void postProcess(MapperService mapperService) { if (uid != null) { DocumentMapper documentMapper = mapperService.documentMapper(uid.type()); if (documentMapper != null) { // we can derive the exact type for the mapping postProcess(documentMapper); return; } } // can't derive exact mapping type for (Map.Entry<String, List<Object>> entry : fields().entrySet()) { FieldMappers fieldMappers = mapperService.indexName(entry.getKey()); if (fieldMappers == null) { continue; } List<Object> fieldValues = entry.getValue(); for (int i = 0; i < fieldValues.size(); i++) { fieldValues.set(i, fieldMappers.mapper().valueForSearch(fieldValues.get(i))); } } }