All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opensearch.ingest.RandomDocumentPicks Maven / Gradle / Ivy

There is a newer version: 2.17.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.
 */

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.ingest;

import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;

import org.opensearch.index.VersionType;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

public final class RandomDocumentPicks {

    private RandomDocumentPicks() {

    }

    /**
     * Returns a random field name. Can be a leaf field name or the
     * path to refer to a field name using the dot notation.
     */
    public static String randomFieldName(Random random) {
        int numLevels = RandomNumbers.randomIntBetween(random, 1, 5);
        StringBuilder fieldName = new StringBuilder();
        for (int i = 0; i < numLevels - 1; i++) {
            if (i > 0) {
                fieldName.append('.');
            }
            fieldName.append(randomString(random));
        }
        if (numLevels > 1) {
            fieldName.append('.');
        }
        fieldName.append(randomLeafFieldName(random));
        return fieldName.toString();
    }

    /**
     * Returns a random field name that doesn't exist in the document.
     */
    public static String randomNonExistingFieldName(Random random, IngestDocument ingestDocument) {
        String fieldName;
        do {
            fieldName = randomFieldName(random);
        } while (canAddField(fieldName, ingestDocument) == false);
        return fieldName;
    }

    /**
     * Returns a random leaf field name.
     */
    public static String randomLeafFieldName(Random random) {
        // Never generates a dot:
        return RandomStrings.randomAsciiAlphanumOfLengthBetween(random, 1, 10);
    }

    /**
     * Returns a randomly selected existing field name out of the fields that are contained
     * in the document provided as an argument.
     */
    public static String randomExistingFieldName(Random random, IngestDocument ingestDocument) {
        Map source = new TreeMap<>(ingestDocument.getSourceAndMetadata());
        Map.Entry randomEntry = RandomPicks.randomFrom(random, source.entrySet());
        String key = randomEntry.getKey();
        while (randomEntry.getValue() instanceof Map) {
            @SuppressWarnings("unchecked")
            Map map = (Map) randomEntry.getValue();
            Map treeMap = new TreeMap<>(map);
            randomEntry = RandomPicks.randomFrom(random, treeMap.entrySet());
            key += "." + randomEntry.getKey();
        }
        assert ingestDocument.getFieldValue(key, Object.class) != null;
        return key;
    }

    /**
     * Adds a random non existing field to the provided document and associates it
     * with the provided value. The field will be added at a random position within the document,
     * not necessarily at the top level using a leaf field name.
     */
    public static String addRandomField(Random random, IngestDocument ingestDocument, Object value) {
        String fieldName;
        do {
            fieldName = randomFieldName(random);
        } while (canAddField(fieldName, ingestDocument) == false);
        ingestDocument.setFieldValue(fieldName, value);
        return fieldName;
    }

    /**
     * Checks whether the provided field name can be safely added to the provided document.
     * When the provided field name holds the path using the dot notation, we have to make sure
     * that each node of the tree either doesn't exist or is a map, otherwise new fields cannot be added.
     */
    public static boolean canAddField(String path, IngestDocument ingestDocument) {
        String[] pathElements = path.split("\\.");
        Map innerMap = ingestDocument.getSourceAndMetadata();
        if (pathElements.length > 1) {
            for (int i = 0; i < pathElements.length - 1; i++) {
                Object currentLevel = innerMap.get(pathElements[i]);
                if (currentLevel == null) {
                    return true;
                }
                if (currentLevel instanceof Map == false) {
                    return false;
                }
                @SuppressWarnings("unchecked")
                Map map = (Map) currentLevel;
                innerMap = map;
            }
        }
        String leafKey = pathElements[pathElements.length - 1];
        return innerMap.containsKey(leafKey) == false;
    }

    /**
     * Generates a random document and random metadata
     */
    public static IngestDocument randomIngestDocument(Random random) {
        return randomIngestDocument(random, randomSource(random));
    }

    /**
     * Generates a document that holds random metadata and the document provided as a map argument
     */
    public static IngestDocument randomIngestDocument(Random random, Map source) {
        String index = randomString(random);
        String id = randomString(random);
        String routing = null;
        Long version = randomNonNegtiveLong(random);
        VersionType versionType = RandomPicks.randomFrom(
            random,
            new VersionType[] { VersionType.INTERNAL, VersionType.EXTERNAL, VersionType.EXTERNAL_GTE }
        );
        if (random.nextBoolean()) {
            routing = randomString(random);
        }
        return new IngestDocument(index, id, routing, version, versionType, source);
    }

    public static Map randomSource(Random random) {
        Map document = new HashMap<>();
        addRandomFields(random, document, 0);
        return document;
    }

    /**
     * Generates a random field value, can be a string, a number, a list of an object itself.
     */
    public static Object randomFieldValue(Random random) {
        return randomFieldValue(random, 0);
    }

    private static Object randomFieldValue(Random random, int currentDepth) {
        switch (RandomNumbers.randomIntBetween(random, 0, 9)) {
            case 0:
                return randomString(random);
            case 1:
                return random.nextInt();
            case 2:
                return random.nextBoolean();
            case 3:
                return random.nextDouble();
            case 4:
                List stringList = new ArrayList<>();
                int numStringItems = RandomNumbers.randomIntBetween(random, 1, 10);
                for (int j = 0; j < numStringItems; j++) {
                    stringList.add(randomString(random));
                }
                return stringList;
            case 5:
                List intList = new ArrayList<>();
                int numIntItems = RandomNumbers.randomIntBetween(random, 1, 10);
                for (int j = 0; j < numIntItems; j++) {
                    intList.add(random.nextInt());
                }
                return intList;
            case 6:
                List booleanList = new ArrayList<>();
                int numBooleanItems = RandomNumbers.randomIntBetween(random, 1, 10);
                for (int j = 0; j < numBooleanItems; j++) {
                    booleanList.add(random.nextBoolean());
                }
                return booleanList;
            case 7:
                List doubleList = new ArrayList<>();
                int numDoubleItems = RandomNumbers.randomIntBetween(random, 1, 10);
                for (int j = 0; j < numDoubleItems; j++) {
                    doubleList.add(random.nextDouble());
                }
                return doubleList;
            case 8:
                Map newNode = new HashMap<>();
                addRandomFields(random, newNode, ++currentDepth);
                return newNode;
            case 9:
                byte[] byteArray = new byte[RandomNumbers.randomIntBetween(random, 1, 1024)];
                random.nextBytes(byteArray);
                return byteArray;
            default:
                throw new UnsupportedOperationException();
        }
    }

    public static String randomString(Random random) {
        if (random.nextBoolean()) {
            return RandomStrings.randomAsciiOfLengthBetween(random, 1, 10);
        }
        return RandomStrings.randomUnicodeOfCodepointLengthBetween(random, 1, 10);
    }

    private static Long randomNonNegtiveLong(Random random) {
        long randomLong = random.nextLong();
        return randomLong == Long.MIN_VALUE ? 0 : Math.abs(randomLong);
    }

    private static void addRandomFields(Random random, Map parentNode, int currentDepth) {
        if (currentDepth > 5) {
            return;
        }
        int numFields = RandomNumbers.randomIntBetween(random, 1, 10);
        for (int i = 0; i < numFields; i++) {
            String fieldName = randomLeafFieldName(random);
            Object fieldValue = randomFieldValue(random, currentDepth);
            parentNode.put(fieldName, fieldValue);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy