Skip to content

Proposal: developer-facing project, collection, and stream interface #18

Description

@syaheer

Summary

Define a simpler, developer_facing Android SDK interface around three concepts__

_ Project — identifies one app or product owned by the developer.
_ Collection — a logical searchable index inside that project.
_ Stream — a source or grouping inside the collection.

This interface should hide the backend's current two_level collection/sub_collection structure.

Motivation

Backend_oriented names such as tenant_id and end_userid are difficult to apply consistently across different mobile applications. Not every app has an end_user_specific index; some apps need one global index, while others need an isolated index per end user.

Developers should be able to organize VModal data according to their application's domain without needing to understand the backend storage layout.

Proposed public terminology

| SDK concept | Meaning | Examples |
| ___ | ___ | ___ |
| projectId | Developer_owned app or product | food_app, shopping_app |
| collectionName | Logical searchable index | global, user_123, product_catalog |
| streamName | Source/group within a collection | uploads, camera, favorites |

projectId is intentionally different from userId__ in a mobile SDK, userId normally means the app's end user. Authentication identity fields can remain internal/separate from this content_organization interface.

Proposed Kotlin interface

Illustrative developer_facing API__

VModal.configure(
    projectId = "food_app",
    apiKey = apiKey,
)

VModal.setCollection("user_123")
VModal.setStream("favorites")

The current selection is then used by upload, indexing, and search operations__

VModal.upload(video)
VModal.search("birthday dinner")

An equivalent immutable/scoped API is acceptable if it is safer for concurrent Android work, provided the developer_facing terminology and usage remain simple__

val vmodal = VModal.configure(
    projectId = "food_app",
    apiKey = apiKey,
)

val favorites = vmodal.scope(
    collectionName = "user_123",
    streamName = "favorites",
)

favorites.upload(video)
favorites.search("birthday dinner")

This avoids one process_wide mutable collection when multiple users, workers, or simultaneous operations are active.

Internal API mapping

Enforce rules inside the SDK

collection_name __   only a_z A_Z 0_9 `_` characters.          and 80 characters max
stream_name __        only a_z A_Z 0_9 `_` characters.          and 80 characters max

enforce those rules and raise error if developper input wrong ones.


The backend currently supports only two hierarchy levels. The SDK should synthesize the project level by prefixing the collection__



## SDK__
  projectId      = "food_app"
  collectionName = "user_123"
  streamName     = "favorites"

## Backend/API__
  ## aggregate by 2 `_`  __  `__`  underscore ...
  collection_name = "food_app__user_123"
  stream_name     = "favorites"

Conceptually__

projectId + "__" + collectionName _> backend collection
streamName                       _> backend sub_collection/stream

The encoding must be centralized inside the SDK so callers never construct the composite name themselves. The implementation should either reject the reserved separator in project/collection names or use an escaping scheme to prevent collisions.

Reference use cases

1. Global search index

A Google Search_style app has one central index containing all searchable videos/assets__

val search = VModal.configure(
    projectId = "video_search",
    apiKey = apiKey,
).scope(
    collectionName = "global",
    streamName = "uploads",
)

search.upload(video)
search.search("red bicycle near a bridge")

Backend collection__ video_search__global

2. Private index per end user

Each app user has an isolated searchable index__

val personal = VModal.configure(
    projectId = "food_app",
    apiKey = apiKey,
).scope(
    collectionName = "user__$endUserId",
    streamName = "personal_videos",
)

personal.upload(video)
personal.search("pasta recipe")

Backend collection example__ food_app__user_123

The end_user identifier is an application concern expressed through collectionName; it is not a mandatory SDK concept.

3. Multiple streams for one user

One user collection can separate assets by source or purpose__

val app = VModal.configure(
    projectId = "food_app",
    apiKey = apiKey,
)

val camera = app.scope("user_123", "camera")
val favorites = app.scope("user_123", "favorites")
val uploads = app.scope("user_123", "uploads")

All three use backend collection food_app__user_123, with different stream_name values.

4. Product catalog

A commerce app can organize indexes by business domain rather than by end user__

val catalog = VModal.configure(
    projectId = "shopping_app",
    apiKey = apiKey,
).scope(
    collectionName = "product_catalog",
    streamName = "merchant_uploads",
)

Backend collection__ shopping_app__product_catalog

5. Multiple developer projects

The same developer can use separate project IDs for different apps__

val foodApp = VModal.configure("food_app", foodAppApiKey)
val shoppingApp = VModal.configure("shopping_app", shoppingAppApiKey)

Their logical collection names may be identical without colliding because the project ID is included in the backend collection name.

Behavioral expectations

_ projectId is required when configuring the SDK.
_ collectionName is required for collection_bound upload, indexing, and search operations.
_ streamName has an explicitly documented default or is required; it must not silently vary between operations.
_ Values are trimmed and validated consistently.
_ The SDK constructs the backend collection name in one internal helper.
_ Upload, indexing, search, listing, and deletion use the same mapping.
_ Public errors and documentation use project/collection/stream terminology.
_ Authentication identity remains separate from project/content organization.
_ The design works safely when multiple scopes or users are active concurrently.
_ Existing APIs receive a documented migration/deprecation path.

Open implementation decisions

_ Choose the final class/facade name__ VModal versus the existing Client.
_ Choose a stateful setter interface versus an immutable scope(...) interface. The scoped form is preferable for thread safety, but both examples document the intended developer experience.
_ Define allowed characters and maximum lengths for all three names.
_ Decide whether streamName should default to a stable value such as "default".
_ Define how listing APIs remove the projectId__ prefix before returning collection names to SDK callers.

Acceptance criteria

_ [ ] Public API specification is agreed for project, collection, and stream selection.
_ [ ] projectId + collectionName maps deterministically to the backend collection_name.
_ [ ] streamName maps to the backend stream/sub_collection level.
_ [ ] Callers never need to build or parse projectId__collectionName.
_ [ ] Global_index, per_user_index, multi_stream, catalog, and multi_project use cases are covered.
_ [ ] Naming validation and separator collision behavior are documented.
_ [ ] Authentication identity and content organization are kept conceptually separate.
_ [ ] A backward_compatibility/deprecation plan is documented before implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions