Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,334 @@
// monorepo/cloud/maplefile-backend/internal/maplefile/repo/collection/count.go
package collection
import (
"context"
"fmt"
"time"
"github.com/gocql/gocql"
"go.uber.org/zap"
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
)
// CountOwnedCollections counts all collections (folders + albums) owned by the user
func (impl *collectionRepositoryImpl) CountOwnedCollections(ctx context.Context, userID gocql.UUID) (int, error) {
return impl.countCollectionsByUserAndType(ctx, userID, "owner", "")
}
// CountSharedCollections counts all collections (folders + albums) shared with the user
func (impl *collectionRepositoryImpl) CountSharedCollections(ctx context.Context, userID gocql.UUID) (int, error) {
return impl.countCollectionsByUserAndType(ctx, userID, "member", "")
}
// CountOwnedFolders counts only folders owned by the user
func (impl *collectionRepositoryImpl) CountOwnedFolders(ctx context.Context, userID gocql.UUID) (int, error) {
return impl.countCollectionsByUserAndType(ctx, userID, "owner", dom_collection.CollectionTypeFolder)
}
// CountSharedFolders counts only folders shared with the user
func (impl *collectionRepositoryImpl) CountSharedFolders(ctx context.Context, userID gocql.UUID) (int, error) {
return impl.countCollectionsByUserAndType(ctx, userID, "member", dom_collection.CollectionTypeFolder)
}
// countCollectionsByUserAndType is a helper method that efficiently counts collections
// filterType: empty string for all types, or specific type like "folder"
func (impl *collectionRepositoryImpl) countCollectionsByUserAndType(ctx context.Context, userID gocql.UUID, accessType, filterType string) (int, error) {
// Use the access-type-specific table for efficient querying
query := `SELECT collection_id FROM maplefile.collections_by_user_id_and_access_type_with_desc_modified_at_and_asc_collection_id
WHERE user_id = ? AND access_type = ?`
impl.Logger.Debug("Starting collection count query",
zap.String("user_id", userID.String()),
zap.String("access_type", accessType),
zap.String("filter_type", filterType))
iter := impl.Session.Query(query, userID, accessType).WithContext(ctx).Iter()
count := 0
totalScanned := 0
var collectionID gocql.UUID
var debugCollectionIDs []string
// Iterate through results and count based on criteria
for iter.Scan(&collectionID) {
totalScanned++
debugCollectionIDs = append(debugCollectionIDs, collectionID.String())
impl.Logger.Debug("Processing collection for count",
zap.String("collection_id", collectionID.String()),
zap.Int("total_scanned", totalScanned),
zap.String("access_type", accessType))
// Get the collection to check state and type
collection, err := impl.getBaseCollection(ctx, collectionID)
if err != nil {
impl.Logger.Warn("failed to get collection for counting",
zap.String("collection_id", collectionID.String()),
zap.Error(err))
continue
}
if collection == nil {
impl.Logger.Warn("collection not found for counting",
zap.String("collection_id", collectionID.String()))
continue
}
impl.Logger.Debug("Collection details for counting",
zap.String("collection_id", collectionID.String()),
zap.String("state", collection.State),
zap.String("collection_type", collection.CollectionType),
zap.String("owner_id", collection.OwnerID.String()),
zap.String("querying_user_id", userID.String()),
zap.String("access_type", accessType),
zap.String("required_filter_type", filterType))
// Only count active collections
if collection.State != dom_collection.CollectionStateActive {
impl.Logger.Debug("Skipping collection due to non-active state",
zap.String("collection_id", collectionID.String()),
zap.String("state", collection.State))
continue
}
// Filter by type if specified
if filterType != "" && collection.CollectionType != filterType {
impl.Logger.Debug("Skipping collection due to type filter",
zap.String("collection_id", collectionID.String()),
zap.String("collection_type", collection.CollectionType),
zap.String("required_type", filterType))
continue
}
count++
impl.Logger.Info("Collection counted",
zap.String("collection_id", collectionID.String()),
zap.String("access_type", accessType),
zap.String("owner_id", collection.OwnerID.String()),
zap.String("querying_user_id", userID.String()),
zap.Bool("is_owner", collection.OwnerID == userID),
zap.Int("current_count", count))
}
if err := iter.Close(); err != nil {
impl.Logger.Error("failed to count collections",
zap.String("user_id", userID.String()),
zap.String("access_type", accessType),
zap.String("filter_type", filterType),
zap.Error(err))
return 0, fmt.Errorf("failed to count collections: %w", err)
}
impl.Logger.Info("Collection count completed",
zap.String("user_id", userID.String()),
zap.String("access_type", accessType),
zap.String("filter_type", filterType),
zap.Int("final_count", count),
zap.Int("total_scanned", totalScanned),
zap.Strings("scanned_collection_ids", debugCollectionIDs))
return count, nil
}
// FIXED DEBUG: Query both access types separately to avoid ALLOW FILTERING
func (impl *collectionRepositoryImpl) DebugCollectionRecords(ctx context.Context, userID gocql.UUID) error {
impl.Logger.Info("=== DEBUG: Checking OWNER records ===")
// Check owner records
ownerQuery := `SELECT user_id, access_type, modified_at, collection_id, permission_level, state
FROM maplefile.collections_by_user_id_and_access_type_with_desc_modified_at_and_asc_collection_id
WHERE user_id = ? AND access_type = ?`
ownerIter := impl.Session.Query(ownerQuery, userID, "owner").WithContext(ctx).Iter()
var (
resultUserID gocql.UUID
accessType string
modifiedAt time.Time
collectionID gocql.UUID
permissionLevel string
state string
)
ownerCount := 0
for ownerIter.Scan(&resultUserID, &accessType, &modifiedAt, &collectionID, &permissionLevel, &state) {
ownerCount++
impl.Logger.Info("DEBUG: Found OWNER record",
zap.Int("record_number", ownerCount),
zap.String("user_id", resultUserID.String()),
zap.String("access_type", accessType),
zap.Time("modified_at", modifiedAt),
zap.String("collection_id", collectionID.String()),
zap.String("permission_level", permissionLevel),
zap.String("state", state))
}
ownerIter.Close()
impl.Logger.Info("=== DEBUG: Checking MEMBER records ===")
// Check member records
memberIter := impl.Session.Query(ownerQuery, userID, "member").WithContext(ctx).Iter()
memberCount := 0
for memberIter.Scan(&resultUserID, &accessType, &modifiedAt, &collectionID, &permissionLevel, &state) {
memberCount++
impl.Logger.Info("DEBUG: Found MEMBER record",
zap.Int("record_number", memberCount),
zap.String("user_id", resultUserID.String()),
zap.String("access_type", accessType),
zap.Time("modified_at", modifiedAt),
zap.String("collection_id", collectionID.String()),
zap.String("permission_level", permissionLevel),
zap.String("state", state))
}
memberIter.Close()
impl.Logger.Info("DEBUG: Total records summary",
zap.String("user_id", userID.String()),
zap.Int("owner_records", ownerCount),
zap.Int("member_records", memberCount),
zap.Int("total_records", ownerCount+memberCount))
return nil
}
// Alternative optimized implementation for when you need both owned and shared counts
// This reduces database round trips by querying once and separating in memory
func (impl *collectionRepositoryImpl) countCollectionsSummary(ctx context.Context, userID gocql.UUID, filterType string) (ownedCount, sharedCount int, err error) {
// Query all collections for the user using the general table
query := `SELECT collection_id, access_type FROM maplefile.collections_by_user_id_with_desc_modified_at_and_asc_collection_id
WHERE user_id = ?`
iter := impl.Session.Query(query, userID).WithContext(ctx).Iter()
var collectionID gocql.UUID
var accessType string
for iter.Scan(&collectionID, &accessType) {
// Get the collection to check state and type
collection, getErr := impl.getBaseCollection(ctx, collectionID)
if getErr != nil {
impl.Logger.Warn("failed to get collection for counting summary",
zap.String("collection_id", collectionID.String()),
zap.Error(getErr))
continue
}
if collection == nil {
continue
}
// Only count active collections
if collection.State != dom_collection.CollectionStateActive {
continue
}
// Filter by type if specified
if filterType != "" && collection.CollectionType != filterType {
continue
}
// Count based on access type
switch accessType {
case "owner":
ownedCount++
case "member":
sharedCount++
}
}
if err = iter.Close(); err != nil {
impl.Logger.Error("failed to count collections summary",
zap.String("user_id", userID.String()),
zap.String("filter_type", filterType),
zap.Error(err))
return 0, 0, fmt.Errorf("failed to count collections summary: %w", err)
}
impl.Logger.Debug("counted collections summary successfully",
zap.String("user_id", userID.String()),
zap.String("filter_type", filterType),
zap.Int("owned_count", ownedCount),
zap.Int("shared_count", sharedCount))
return ownedCount, sharedCount, nil
}
// CountTotalUniqueFolders counts unique folders accessible to the user (deduplicates owned+shared)
func (impl *collectionRepositoryImpl) CountTotalUniqueFolders(ctx context.Context, userID gocql.UUID) (int, error) {
// Use a set to track unique collection IDs to avoid double-counting
uniqueCollectionIDs := make(map[gocql.UUID]bool)
// Query all collections for the user using the general table
query := `SELECT collection_id FROM maplefile.collections_by_user_id_with_desc_modified_at_and_asc_collection_id
WHERE user_id = ?`
iter := impl.Session.Query(query, userID).WithContext(ctx).Iter()
var collectionID gocql.UUID
totalScanned := 0
for iter.Scan(&collectionID) {
totalScanned++
// Get the collection to check state and type
collection, err := impl.getBaseCollection(ctx, collectionID)
if err != nil {
impl.Logger.Warn("failed to get collection for unique counting",
zap.String("collection_id", collectionID.String()),
zap.Error(err))
continue
}
if collection == nil {
continue
}
impl.Logger.Debug("Processing collection for unique count",
zap.String("collection_id", collectionID.String()),
zap.String("state", collection.State),
zap.String("collection_type", collection.CollectionType),
zap.Int("total_scanned", totalScanned))
// Only count active folders
if collection.State != dom_collection.CollectionStateActive {
impl.Logger.Debug("Skipping collection due to non-active state",
zap.String("collection_id", collectionID.String()),
zap.String("state", collection.State))
continue
}
// Filter by folder type
if collection.CollectionType != dom_collection.CollectionTypeFolder {
impl.Logger.Debug("Skipping collection due to type filter",
zap.String("collection_id", collectionID.String()),
zap.String("collection_type", collection.CollectionType))
continue
}
// Add to unique set (automatically deduplicates)
uniqueCollectionIDs[collectionID] = true
impl.Logger.Debug("Added unique folder to count",
zap.String("collection_id", collectionID.String()),
zap.Int("current_unique_count", len(uniqueCollectionIDs)))
}
if err := iter.Close(); err != nil {
impl.Logger.Error("failed to count unique folders",
zap.String("user_id", userID.String()),
zap.Error(err))
return 0, fmt.Errorf("failed to count unique folders: %w", err)
}
uniqueCount := len(uniqueCollectionIDs)
impl.Logger.Info("Unique folder count completed",
zap.String("user_id", userID.String()),
zap.Int("total_scanned", totalScanned),
zap.Int("unique_folders", uniqueCount))
return uniqueCount, nil
}