<commit_before>/*
 * This file is part of ToroDB.
 *
 * ToroDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ToroDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with core. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2016 8Kdata.
 * 
 */

package com.torodb.core.transaction.metainf;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jooq.lambda.Seq;
import org.jooq.lambda.tuple.Tuple2;

import com.google.common.base.Preconditions;
import com.torodb.core.TableRef;
import com.torodb.core.annotations.DoNotChange;

/**
 *
 */
public class ImmutableMetaCollection implements MetaCollection {

    private final String name;
    private final String identifier;
    private final Map<TableRef, ImmutableMetaDocPart> docPartsByTableRef;
    private final Map<String, ImmutableMetaDocPart> docPartsByIdentifier;
    private final Map<String, ImmutableMetaIndex> indexesByName;

    public ImmutableMetaCollection(String colName, String colId, Iterable<ImmutableMetaDocPart> docParts, Iterable<ImmutableMetaIndex> indexes) {
        this.name = colName;
        this.identifier = colId;

        HashMap<TableRef, ImmutableMetaDocPart> byTableRef = new HashMap<>();
        HashMap<String, ImmutableMetaDocPart> byDbName = new HashMap<>();
        for (ImmutableMetaDocPart table : docParts) {
            byTableRef.put(table.getTableRef(), table);
            byDbName.put(table.getIdentifier(), table);
        }
        this.docPartsByTableRef = Collections.unmodifiableMap(byTableRef);
        this.docPartsByIdentifier = Collections.unmodifiableMap(byDbName);
        
        HashMap<String, ImmutableMetaIndex> indexesByName = new HashMap<>();
        for (ImmutableMetaIndex index : indexes) {
            indexesByName.put(index.getName(), index);
        }
        this.indexesByName = Collections.unmodifiableMap(indexesByName);
    }

    public ImmutableMetaCollection(String colName, String colId, @DoNotChange Map<String, ImmutableMetaDocPart> docPartsById, Map<String, ImmutableMetaIndex> indexesByName) {
        this.name = colName;
        this.identifier = colId;

        this.docPartsByIdentifier = docPartsById;
        this.docPartsByTableRef = new HashMap<>();
        for (ImmutableMetaDocPart table : docPartsById.values()) {
            docPartsByTableRef.put(table.getTableRef(), table);
        }

        this.indexesByName = indexesByName;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getIdentifier() {
        return identifier;
    }

    @Override
    public Stream<ImmutableMetaDocPart> streamContainedMetaDocParts() {
        return docPartsByIdentifier.values().stream();
    }

    @Override
    public ImmutableMetaDocPart getMetaDocPartByIdentifier(String tableDbName) {
        return docPartsByIdentifier.get(tableDbName);
    }

    @Override
    public ImmutableMetaDocPart getMetaDocPartByTableRef(TableRef tableRef) {
        return docPartsByTableRef.get(tableRef);
    }

    @Override
    public Stream<ImmutableMetaIndex> streamContainedMetaIndexes() {
        return indexesByName.values().stream();
    }

    @Override
    public ImmutableMetaIndex getMetaIndexByName(String indexName) {
        return indexesByName.get(indexName);
    }

    @Override
    public List<Tuple2<MetaIndex, List<String>>> getMissingIndexesForNewField(
            MutableMetaDocPart docPart, MetaField newField) {
        return getMissingIndexesForNewField(streamContainedMetaIndexes(), docPart, newField);
    }

    protected List<Tuple2<MetaIndex, List<String>>> getMissingIndexesForNewField(Stream<? extends MetaIndex> containedMetaIndexes,
            MutableMetaDocPart docPart, MetaField newField) {
        return containedMetaIndexes
            .filter(index -> index.getMetaIndexFieldByTableRefAndName(docPart.getTableRef(), newField.getName()) != null)
            .flatMap(index -> Seq.seq(index.iteratorMetaDocPartIndexesIdentifiers(docPart))
                    .filter(identifiers -> identifiers.contains(newField.getIdentifier()))
                    .map(identifiers -> new Tuple2<MetaIndex,List<String>>(index, identifiers)))
            .collect(Collectors.toList());
    }
   
    public static class Builder {
        private boolean built = false;
        private final String name;
        private final String identifier;
        private final Map<String, ImmutableMetaDocPart> docPartsByIdentifier;
        private final Map<String, ImmutableMetaIndex> indexesByName;

        public Builder(String name, String identifier) {
            this.name = name;
            this.identifier = identifier;
            this.docPartsByIdentifier = new HashMap<>();
            this.indexesByName = new HashMap<>();
        }

        public Builder(String name, String identifier, int expectedDocParts, int expectedIndexes) {
            this.name = name;
            this.identifier = identifier;
            this.docPartsByIdentifier = new HashMap<>(expectedDocParts);
            this.indexesByName = new HashMap<>(expectedIndexes);
        }

        public Builder(ImmutableMetaCollection other) {
            this.name = other.getName();
            this.identifier = other.getIdentifier();

            docPartsByIdentifier = new HashMap<>(other.docPartsByIdentifier);
            this.indexesByName = new HashMap<>(other.indexesByName);
        }

        public Builder put(ImmutableMetaDocPart.Builder tableBuilder) {
            return put(tableBuilder.build());
        }

        public Builder put(ImmutableMetaDocPart table) {
            Preconditions.checkState(!built, "This builder has already been built");
            docPartsByIdentifier.put(table.getIdentifier(), table);
            return this;
        }

        public Builder put(ImmutableMetaIndex.Builder indexBuilder) {
            return put(indexBuilder.build());
        }

        public Builder put(ImmutableMetaIndex index) {
            Preconditions.checkState(!built, "This builder has already been built");
            indexesByName.put(index.getName(), index);
            return this;
        }

        public Builder remove(MetaIndex index) {
            Preconditions.checkState(!built, "This builder has already been built");
            indexesByName.remove(index.getName());
            return this;
        }

        public ImmutableMetaCollection build() {
            Preconditions.checkState(!built, "This builder has already been built");
            Preconditions.checkState(
            		docPartsByIdentifier.values().isEmpty() || 
                    docPartsByIdentifier.values().stream().anyMatch((dp) -> dp.getTableRef().isRoot()),
                    "Tryng to create a MetaCollection without a root doc part"
            );
            built = true;
            return new ImmutableMetaCollection(name, identifier, docPartsByIdentifier, indexesByName);
        }
    }

    @Override
    public String toString() {
        return defautToString();
    }
}
<commit_msg>Fixed bug that allow multiple creation of same index on adding a column<commit_after>/*
 * This file is part of ToroDB.
 *
 * ToroDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ToroDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with core. If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) 2016 8Kdata.
 * 
 */

package com.torodb.core.transaction.metainf;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jooq.lambda.Seq;
import org.jooq.lambda.tuple.Tuple2;

import com.google.common.base.Preconditions;
import com.torodb.core.TableRef;
import com.torodb.core.annotations.DoNotChange;

/**
 *
 */
public class ImmutableMetaCollection implements MetaCollection {

    private final String name;
    private final String identifier;
    private final Map<TableRef, ImmutableMetaDocPart> docPartsByTableRef;
    private final Map<String, ImmutableMetaDocPart> docPartsByIdentifier;
    private final Map<String, ImmutableMetaIndex> indexesByName;

    public ImmutableMetaCollection(String colName, String colId, Iterable<ImmutableMetaDocPart> docParts, Iterable<ImmutableMetaIndex> indexes) {
        this.name = colName;
        this.identifier = colId;

        HashMap<TableRef, ImmutableMetaDocPart> byTableRef = new HashMap<>();
        HashMap<String, ImmutableMetaDocPart> byDbName = new HashMap<>();
        for (ImmutableMetaDocPart table : docParts) {
            byTableRef.put(table.getTableRef(), table);
            byDbName.put(table.getIdentifier(), table);
        }
        this.docPartsByTableRef = Collections.unmodifiableMap(byTableRef);
        this.docPartsByIdentifier = Collections.unmodifiableMap(byDbName);
        
        HashMap<String, ImmutableMetaIndex> indexesByName = new HashMap<>();
        for (ImmutableMetaIndex index : indexes) {
            indexesByName.put(index.getName(), index);
        }
        this.indexesByName = Collections.unmodifiableMap(indexesByName);
    }

    public ImmutableMetaCollection(String colName, String colId, @DoNotChange Map<String, ImmutableMetaDocPart> docPartsById, Map<String, ImmutableMetaIndex> indexesByName) {
        this.name = colName;
        this.identifier = colId;

        this.docPartsByIdentifier = docPartsById;
        this.docPartsByTableRef = new HashMap<>();
        for (ImmutableMetaDocPart table : docPartsById.values()) {
            docPartsByTableRef.put(table.getTableRef(), table);
        }

        this.indexesByName = indexesByName;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getIdentifier() {
        return identifier;
    }

    @Override
    public Stream<ImmutableMetaDocPart> streamContainedMetaDocParts() {
        return docPartsByIdentifier.values().stream();
    }

    @Override
    public ImmutableMetaDocPart getMetaDocPartByIdentifier(String tableDbName) {
        return docPartsByIdentifier.get(tableDbName);
    }

    @Override
    public ImmutableMetaDocPart getMetaDocPartByTableRef(TableRef tableRef) {
        return docPartsByTableRef.get(tableRef);
    }

    @Override
    public Stream<ImmutableMetaIndex> streamContainedMetaIndexes() {
        return indexesByName.values().stream();
    }

    @Override
    public ImmutableMetaIndex getMetaIndexByName(String indexName) {
        return indexesByName.get(indexName);
    }

    @Override
    public List<Tuple2<MetaIndex, List<String>>> getMissingIndexesForNewField(
            MutableMetaDocPart docPart, MetaField newField) {
        return getMissingIndexesForNewField(streamContainedMetaIndexes(), docPart, newField);
    }

    protected List<Tuple2<MetaIndex, List<String>>> getMissingIndexesForNewField(Stream<? extends MetaIndex> containedMetaIndexes,
            MutableMetaDocPart docPart, MetaField newField) {
        return containedMetaIndexes
            .filter(index -> index.getMetaIndexFieldByTableRefAndName(docPart.getTableRef(), newField.getName()) != null)
            .flatMap(index -> Seq.seq(index.iteratorMetaDocPartIndexesIdentifiers(docPart))
                    .filter(identifiers -> identifiers.contains(newField.getIdentifier()))
                    .map(identifiers -> new Tuple2<MetaIndex,List<String>>(index, identifiers)))
            .collect(Collectors.groupingBy(missingIndexEntry -> missingIndexEntry.v2()))
                .entrySet()
                .stream()
                .map(groupedMissingIndexEntries -> groupedMissingIndexEntries.getValue().get(0))
                .collect(Collectors.toList());
    }
   
    public static class Builder {
        private boolean built = false;
        private final String name;
        private final String identifier;
        private final Map<String, ImmutableMetaDocPart> docPartsByIdentifier;
        private final Map<String, ImmutableMetaIndex> indexesByName;

        public Builder(String name, String identifier) {
            this.name = name;
            this.identifier = identifier;
            this.docPartsByIdentifier = new HashMap<>();
            this.indexesByName = new HashMap<>();
        }

        public Builder(String name, String identifier, int expectedDocParts, int expectedIndexes) {
            this.name = name;
            this.identifier = identifier;
            this.docPartsByIdentifier = new HashMap<>(expectedDocParts);
            this.indexesByName = new HashMap<>(expectedIndexes);
        }

        public Builder(ImmutableMetaCollection other) {
            this.name = other.getName();
            this.identifier = other.getIdentifier();

            docPartsByIdentifier = new HashMap<>(other.docPartsByIdentifier);
            this.indexesByName = new HashMap<>(other.indexesByName);
        }

        public Builder put(ImmutableMetaDocPart.Builder tableBuilder) {
            return put(tableBuilder.build());
        }

        public Builder put(ImmutableMetaDocPart table) {
            Preconditions.checkState(!built, "This builder has already been built");
            docPartsByIdentifier.put(table.getIdentifier(), table);
            return this;
        }

        public Builder put(ImmutableMetaIndex.Builder indexBuilder) {
            return put(indexBuilder.build());
        }

        public Builder put(ImmutableMetaIndex index) {
            Preconditions.checkState(!built, "This builder has already been built");
            indexesByName.put(index.getName(), index);
            return this;
        }

        public Builder remove(MetaIndex index) {
            Preconditions.checkState(!built, "This builder has already been built");
            indexesByName.remove(index.getName());
            return this;
        }

        public ImmutableMetaCollection build() {
            Preconditions.checkState(!built, "This builder has already been built");
            Preconditions.checkState(
            		docPartsByIdentifier.values().isEmpty() || 
                    docPartsByIdentifier.values().stream().anyMatch((dp) -> dp.getTableRef().isRoot()),
                    "Tryng to create a MetaCollection without a root doc part"
            );
            built = true;
            return new ImmutableMetaCollection(name, identifier, docPartsByIdentifier, indexesByName);
        }
    }

    @Override
    public String toString() {
        return defautToString();
    }
}

{'fraction_non_alphanumeric': 0.088023644307376, 'fraction_numerical': 0.0014135183757388846, 'mean_word_length': 2.955527318932656, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2565472', 'n_tokens_mistral': 4074, 'n_tokens_neox': 3982, 'n_words': 998}