| 
 | 1 | +/*  | 
 | 2 | + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one  | 
 | 3 | + * or more contributor license agreements. Licensed under the "Elastic License  | 
 | 4 | + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side  | 
 | 5 | + * Public License v 1"; you may not use this file except in compliance with, at  | 
 | 6 | + * your election, the "Elastic License 2.0", the "GNU Affero General Public  | 
 | 7 | + * License v3.0 only", or the "Server Side Public License, v 1".  | 
 | 8 | + */  | 
 | 9 | + | 
 | 10 | +package org.elasticsearch.datageneration.queries;  | 
 | 11 | + | 
 | 12 | +import org.apache.lucene.search.join.ScoreMode;  | 
 | 13 | +import org.elasticsearch.index.query.QueryBuilder;  | 
 | 14 | +import org.elasticsearch.index.query.QueryBuilders;  | 
 | 15 | + | 
 | 16 | +import java.util.Arrays;  | 
 | 17 | +import java.util.ArrayList;  | 
 | 18 | +import java.util.List;  | 
 | 19 | +import java.util.Map;  | 
 | 20 | + | 
 | 21 | + | 
 | 22 | +public class QueryGenerator {  | 
 | 23 | + | 
 | 24 | +    private final Map<String, Map<String, Object>> mappingLookup;  | 
 | 25 | +    private final Map<String, Object> mappingRaw;  | 
 | 26 | + | 
 | 27 | +    public QueryGenerator(Map<String, Map<String, Object>> mappingLookup, Map<String, Object> mappingRaw) {  | 
 | 28 | +        this.mappingLookup = mappingLookup;  | 
 | 29 | +        this.mappingRaw = mappingRaw;  | 
 | 30 | +    }  | 
 | 31 | + | 
 | 32 | +    public List<QueryBuilder> generateQueries(  | 
 | 33 | +        String path,  | 
 | 34 | +        Map<String, Object> mapping,  | 
 | 35 | +        Object value  | 
 | 36 | +    ) {  | 
 | 37 | +        // This query generator cannot handle fields with periods in the name.  | 
 | 38 | +        if (path.equals("host.name")) {  | 
 | 39 | +            return List.of();  | 
 | 40 | +        }  | 
 | 41 | +        if (mapping == null || isEnabled(path) == false) {  | 
 | 42 | +            return List.of();  | 
 | 43 | +        }  | 
 | 44 | +        boolean isIndexed = (Boolean) mapping.getOrDefault("index", true);  | 
 | 45 | +        if (isIndexed == false) {  | 
 | 46 | +            return List.of();  | 
 | 47 | +        }  | 
 | 48 | +        var type = (String) mapping.get("type");  | 
 | 49 | +        var leafQueryGenerator = LeafQueryGenerator.buildForType(type);  | 
 | 50 | +        var leafQueries = leafQueryGenerator.generate(mapping, path, value);  | 
 | 51 | +        return leafQueries.stream().map(q -> wrapInNestedQuery(path, q)).toList();  | 
 | 52 | +    }  | 
 | 53 | + | 
 | 54 | +    private QueryBuilder wrapInNestedQuery(String path, QueryBuilder leafQuery) {  | 
 | 55 | +        String[] parts = path.split("\\.");  | 
 | 56 | +        List<String> nestedPaths = getNestedPathPrefixes(parts);  | 
 | 57 | +        QueryBuilder query = leafQuery;  | 
 | 58 | +        for (String nestedPath : nestedPaths.reversed()) {  | 
 | 59 | +            query = QueryBuilders.nestedQuery(nestedPath, query, ScoreMode.Max);  | 
 | 60 | +        }  | 
 | 61 | +        return query;  | 
 | 62 | +    }  | 
 | 63 | + | 
 | 64 | +    @SuppressWarnings("unchecked")  | 
 | 65 | +    private List<String> getNestedPathPrefixes(String[] path) {  | 
 | 66 | +        Map<String, Object> mapping = mappingRaw;  | 
 | 67 | +        mapping = (Map<String, Object>) mapping.get("_doc");  | 
 | 68 | +        mapping = (Map<String, Object>) mapping.get("properties");  | 
 | 69 | + | 
 | 70 | +        var result = new ArrayList<String>();  | 
 | 71 | +        for (int i = 0; i < path.length - 1; i++) {  | 
 | 72 | +            var field = path[i];  | 
 | 73 | +            mapping = (Map<String, Object>) mapping.get(field);  | 
 | 74 | +            boolean nested = "nested".equals(mapping.get("type"));  | 
 | 75 | +            if (nested) {  | 
 | 76 | +                result.add(String.join(".", Arrays.copyOfRange(path, 0, i + 1)));  | 
 | 77 | +            }  | 
 | 78 | +            mapping = (Map<String, Object>) mapping.get("properties");  | 
 | 79 | +        }  | 
 | 80 | + | 
 | 81 | +        mapping = (Map<String, Object>) mapping.get(path[path.length - 1]);  | 
 | 82 | +        assert mapping.containsKey("properties") == false;  | 
 | 83 | +        return result;  | 
 | 84 | +    }  | 
 | 85 | + | 
 | 86 | +    /**  | 
 | 87 | +     * Traverse down mapping tree and check that all objects are enabled in path  | 
 | 88 | +     */  | 
 | 89 | +    private boolean isEnabled(String path) {  | 
 | 90 | +        String[] parts = path.split("\\.");  | 
 | 91 | +        for (int i = 0; i < parts.length - 1; i++) {  | 
 | 92 | +            var pathToHere = String.join(".", Arrays.copyOfRange(parts, 0, i + 1));  | 
 | 93 | +            Map<String, Object> mapping = mappingLookup.get(pathToHere);  | 
 | 94 | + | 
 | 95 | +            boolean enabled = true;  | 
 | 96 | +            if (mapping.containsKey("enabled") && mapping.get("enabled") instanceof Boolean) {  | 
 | 97 | +                enabled = (Boolean) mapping.get("enabled");  | 
 | 98 | +            }  | 
 | 99 | +            if (mapping.containsKey("enabled") && mapping.get("enabled") instanceof String) {  | 
 | 100 | +                enabled = Boolean.parseBoolean((String) mapping.get("enabled"));  | 
 | 101 | +            }  | 
 | 102 | + | 
 | 103 | +            if (enabled == false) {  | 
 | 104 | +                return false;  | 
 | 105 | +            }  | 
 | 106 | +        }  | 
 | 107 | +        return true;  | 
 | 108 | +    }  | 
 | 109 | +}  | 
0 commit comments