Repository: nomic-ai/gpt4all
File: gpt4all-chat/src/database.cpp (C++)

Code before:
        sqlEmbeddings.append({e.model, e.folder_id, e.chunk_id, std::move(data)});
    }

    transaction();

    QSqlQuery q(m_db);
    QHash<EmbeddingFolder, EmbeddingStat> stats;
    if (!sqlAddEmbeddings(q, sqlEmbeddings, stats)) {
        qWarning() << "Database ERROR: failed to add embeddings:" << q.lastError();
        return rollback();
    }

    commit();

    // FIXME(jared): embedding counts are per-collectionitem, not per-folder
    for (const auto &[key, stat]: std::as_const(stats).asKeyValueRange()) {
        if (!m_collectionMap.contains(key.folder_id)) continue;
        CollectionItem item = guiCollectionItem(key.folder_id);
        item.currentEmbeddingsToIndex -= stat.nAdded + stat.nSkipped;
        item.totalEmbeddingsToIndex -= stat.nSkipped;
        if (!stat.lastFile.isNull())
            item.fileCurrentlyProcessing = stat.lastFile;

        // Set the last update if we are done
        Q_ASSERT(item.startUpdate > item.lastUpdate);
        if (!item.indexing && item.currentEmbeddingsToIndex == 0)
            setLastUpdateTime(item);

        updateGuiForCollectionItem(item);
    }
}

void Database::handleErrorGenerated(const QVector<EmbeddingChunk> &chunks, const QString &error)
{
    /* FIXME(jared): errors are actually collection-specific because they are conditioned
     * on the embedding model, but this sets the error on all collections for a given
     * folder */

    QSet<int> folder_ids;
    for (const auto &c: chunks) { folder_ids << c.folder_id; }

    for (int fid: folder_ids) {
        if (!m_collectionMap.contains(fid)) continue;
        CollectionItem item = guiCollectionItem(fid);
        item.error = error;
        updateGuiForCollectionItem(item);
    }
}

size_t Database::countOfDocuments(int folder_id) const
{

Review comment (bug):
Do we not trust this code enough to just do this?
```suggestion
        item.currentEmbeddingsToIndex -= qMax(item.currentEmbeddingsToIndex, stat.nAdded + stat.nSkipped);
```
Or at least, given the number of places where we can warn on underflow, we could make a function to do the subtraction.

Code after:
        );
        sqlEmbeddings.append({e.model, e.folder_id, e.chunk_id, std::move(data)});
    }

    transaction();

    QSqlQuery q(m_db);
    QHash<EmbeddingFolder, EmbeddingStat> stats;
    if (!sqlAddEmbeddings(q, sqlEmbeddings, stats)) {
        qWarning() << "Database ERROR: failed to add embeddings:" << q.lastError();
        return rollback();
    }

    commit();

    // FIXME(jared): embedding counts are per-collectionitem, not per-folder
    for (const auto &[key, stat]: std::as_const(stats).asKeyValueRange()) {
        if (!m_collectionMap.contains(key.folder_id)) continue;
        CollectionItem item = guiCollectionItem(key.folder_id);
        Q_ASSERT(item.currentEmbeddingsToIndex >= stat.nAdded + stat.nSkipped);
        if (item.currentEmbeddingsToIndex < stat.nAdded + stat.nSkipped) {
            qWarning() << "Database ERROR: underflow in current embeddings to index statistics";
            item.currentEmbeddingsToIndex = 0;
        } else {
            item.currentEmbeddingsToIndex -= stat.nAdded + stat.nSkipped;
        }

        Q_ASSERT(item.totalEmbeddingsToIndex >= stat.nSkipped);
        if (item.totalEmbeddingsToIndex < stat.nSkipped) {
            qWarning() << "Database ERROR: underflow in total embeddings to index statistics";
            item.totalEmbeddingsToIndex = 0;
        } else {
            item.totalEmbeddingsToIndex -= stat.nSkipped;
        }

        if (!stat.lastFile.isNull())
            item.fileCurrentlyProcessing = stat.lastFile;

        // Set the last update if we are done
        Q_ASSERT(item.startUpdate > item.lastUpdate);
        if (!item.indexing && item.currentEmbeddingsToIndex == 0)
            setLastUpdateTime(item);

        updateGuiForCollectionItem(item);
    }
}

void Database::handleErrorGenerated(const QVector<EmbeddingChunk> &chunks, const QString &error)
{
    /* FIXME(jared): errors are actually collection-specific because they are conditioned
     * on the embedding model, but this sets the error on all collections for a given