-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Object model surface area and nomenclature changes (v2) #5
Comments
Suggested change:I think it makes sense to allow objects to be able to modify themselves and to create objects that can be self-referencing. The objects would still be stateless (save for the coordinates from the factory i.e. ids and partition keys) Examples:Container#.database(): Database // point to parent database
#.read(options?: RequestOptions) => Promise<Response<ContainerDefinition>>
#.replace(body: T, options?: RequestOptions) => Promise<Response<ContainerDefinition>>
#.delete(options?: RequestOptions) => Promise<Response<ContainerDefinition>> Example: // check if this container has a sibling(s) with a suffix
async function findSiblings(c: Container): Container[] {
const {result: siblings} = await c.database.query(`SELECT * FROM d WHERE STARTSWITH(${c.id})-`).toArray();
return siblings;
} Item#.container(): Container // points to parent container
#.read<T>(options?: RequestOptions) => Promise<Response<T>>
#.replace<T>(body: T, options?: RequestOptions) => Promise<Response<T>>
#.delete<T>(options?: RequestOptions) => Promise<Response<T>> somewhat contrived example: function getTodoForFutureUpdates(c: Container, todoId: string, userId: string): Item {
return c.getItem(userId, todoId);
} Stored Procedure#.container(): Container // points to parent container
#.execute<T>(params: any[], options?: RequestOptions) => Promise<Response<T>>
#.read<T>(options?: RequestOptions) => Promise<Response<T>>
#.replace<T>(body: T, options?: RequestOptions) => Promise<Response<T>>
#.delete<T>(options?: RequestOptions) => Promise<Response<T>> Current: c.executeStoredProcedure("fizzbuzz");
c.executeStoredProcedure("fizzbuzz"); Suggested: (Current continues to work) const sp = c.getStoredProcedure("fizzbuzz");
sp.execute();
sp.execute();
sp.execute(); |
Container vs CollectionAs a developer, "container" feels awkward to me. I don't know if that's because I'm used to seeing "collection", but I am wracking my brain and I cannot think of any other instances where I have used "container" in a NoSQL context. |
re: Container vs Collection - @kirankumarkolli & @kirillg, could you comment? |
Assuming that there is not another contending name, I prefer "collection" over "container". Container I have mostly seen in a GUI context. It does feel awkward. |
@burkeholland RE: Container vs Collection Because we're multi-model, we're starting to rename the base concepts to more generic names, so we're not overloading traditional NoSQL names (Collection/Document/etc.) when we're doing Graph and Cassandra operations on the same data set, in the future. We could, however, offer a shim or typedef for Collection & Document for folks looking for traditional NoSQL APIs, that are just direct representations of our Container/Item concepts. Do you think that might address your concern? |
How would our docs look? Would they reference the shims or the new naming?
…On Thu, Jun 14, 2018 at 1:10 PM Christopher Anderson < ***@***.***> wrote:
@burkeholland <https://github.com/burkeholland> RE: Container vs
Collection
Because we're multi-model, we're starting to rename the base concepts to
more generic names, so we're not overloading traditional NoSQL names
(Collection/Document/etc.) when we're doing Graph and Cassandra operations
on the same data set, in the future.
We could, however, offer a shim or typedef for Collection & Document for
folks looking for traditional NoSQL APIs, that are just direct
representations of our Container/Item concepts. Do you think that might
address your concern?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAp7c04hoa3QddQ4oA_VkDr_0VYBAvH4ks5t8rUcgaJpZM4UZrPl>
.
|
It's a good question. We'd have to document them both, is my assumption. Same doc or different doc. I'm not sure how/when we're changing our top level messaging/portal UI/etc. If it's an interesting idea to you, I can work it out with @kirillg in more detail. |
I'm thinking it might be more confusing to have two sets of docs. And more
work. If that's the case we should probably stick to one set of naming.
It's not the end of the world by any stretch. We may find that people
aren't at all caught off guard by the naming. I'm in support of whatever
the group decides is the best naming.
…On Thu, Jun 14, 2018 at 5:30 PM Christopher Anderson < ***@***.***> wrote:
It's a good question. We'd have to document them both, is my assumption.
Same doc or different doc. I'm not sure how/when we're changing our top
level messaging/portal UI/etc.
If it's an interesting idea to you, I can work it out with @kirillg
<https://github.com/kirillg> in more detail.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAp7czokjQ43ptOzzEt3O66nA1I4Wd3cks5t8uP5gaJpZM4UZrPl>
.
|
It's fair feedback on the naming, though. We've had a few folks mention it. We have good reasons to change the name for other consumers (like Mongo/Gremlin/etc.) to avoid overloading the Document NoSQL stuff for each of them, but that plan does mean impacting the folks using Cosmos for the Document style interaction. I definitely want to see what can be done to prevent this causing confusion. We're doing user testing with the new names & API style. That will hopefully give us some more feedback in a controlled setting. |
Suggestion: All the I think it's self explanatory that |
Suggestion: (Needs more investigation)Could support a Proxy on the plural objects that have So you can do: instead of
It really pays off once you add two layers:
Strawman implementation: type ProxiedDatabases = Databases | [database: string]: Database;
const databases = new Databases(this);
this.databases = new Proxy(databases, {
get: (obj, prop) => obj.get(prop); Pros:
Cons:
|
Suggestion:
|
Suggestion: Things like permissions require a link to a resource. It takes a resource url string, but it should also be able to take an object that has a |
👍 to both of @christopheranderson suggestion. I particularly found that overloading |
This feedback has been addressed or moved to independent issues, so I'm closing. |
Goals
For each of the objects we operate on (database, collection, document, etc.), we want to move their operations off of
DocumentClient
and onto objects which represent those operations. We also want to rename those objects to better capture their functionality in today's Cosmos terms (as opposed to legacy Document DB terms). We want the SDK to be easy to use & grok (understand).Nomenclature updates
Surface Area updates
A few notable changes:
container
will have operations forreadItem
on it).await client.getDatabase("foo").getContainer("bar").readItem("baz")
)get
operations) will be sync and not talk to serverWe take some inspiration from REST models, but have a goal of being easy to understand/code versus being technically pure.
Examples
Query Items
Note: Might want to support queryItems (for interfaces) to allow users to get help on the interface of
items
.Pass a container object around
Register collection with DI framework
Consume container in route logic
Objects
Heirarchy
A given object can have many children
A given object only has 1 parent
client
-container
- items
- item
- attachments
- attachment
- triggers
- trigger
- user defined functions
- user defined function
- stored procedures
- stored procedure
Overall pattern will be:
CosmosClient
#.getDatabaseAccount(options?: RequestOptions) => Promise<DatabaseAccount>
#.databases: Databases
#.offers: Offers
DatabaseAccount
???
Databases
#.getDatabase(id: string) => Database
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<DatabaseDefinition>
#.create(body: object, options?: RequestOptions) => Promise<Response<DatabaseDefinition>>
#.read(options?: FeedOptions) => QueryIteratory<DatabaseDefinition>
getDatabase
Database
#.id
#.containers: Containers
#.read(options?: RequestOptions) => Promise<Response<DatabaseDefinition>>
#.replace(options?: RequestOptions) => Promise<Response<DatabaseDefinition>>
#.delete(options?: RequestOptions) => Promise<Response<DatabaseDefinition>>
Containers
#.getContainer(id: string) => Container
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<ContainerDefinition>
#.create(body: object, options?: RequestOptions) => Promise<Response<ContainerDefinition>>
#.read(options?: FeedOptions) => QueryIterator<ContainerDefinition>
Container
#.id
#.database: Database
#.items: Items
#.read(name: string, options?: RequestOptions) => Promise<Response<ContainerDefinition>>
#.replace(name: string, body: ContainerDefinition, options?: RequestOptions) => Promise<Response<ContainerDefinition>>
#.delete(name: string, options?: RequestOptions) => Promise<Response<ContainerDefinition>>
Items
#.getItem(id: string, pk?: string) => Item
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<?>
#.read(options?: FeedOptions) => QueryIterator<?>
#.create<T>(body: T, options?: RequestOptions) => Promise<Response<T>>
#.upsert<T>(body: T, options?: RequestOptions) => Promise<Response<T>>
Item
#.id
#.primaryKey
#.container: Container
#.readItem<T>(id: string, options?: RequestOptions) => Promise<Response<T>>
#.replaceItem<T>(id: string, body: T, options?: RequestOptions) => Promise<Response<T>>
- Do we need this id? How do we sniff out if we're using a partition resolver?
#.deleteItem<T>(id: string, options?: ResquestOptions) => Promise<Response<T>>
StoredProcedures
#.getStoredProcedure(id: string) => StoredProcedure
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<StoredProcedureDefinition>
#.read(options?: FeedOptions) => QueryIterator<StoredProcedureDefinition>
#.create(body: StoredProcedureDefinition, options?: RequestOptions) => Promise<Response<StoredProcedureDefinition>>
#.upsert(body: StoredProcedureDefinition, options?: RequestOptions) => Promise<Response<StoredProcedureDefinition>>
StoredProcedure
#.id
#.container: Container
#.read(options?: RequestOptions) => Promise<Response<StoredProcedureDefinition>>
#.replace(body: StoredProcedureDefinition, options?: RequestOptions) => Promise<Response<StoredProcedureDefinition>>
#.delete(options?: RequestOptions) => Promise<Response<StoredProcedureDefinition>>
#.execute<T>(params?: any[], options?: RequestOptions) => Promise<Response<T>>
Triggers
#.getTrigger(id: string) => Trigger
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<TriggerDefinition>
#.read(options?: FeedOptions) => QueryIterator<TriggerDefinition>
#.create(body: TriggerDefinition, options?: RequestOptions) => Promise<Response<TriggerDefinition>>
#.upsert(body: TriggerDefinition, options?: RequestOptions) => Promise<Response<TriggerDefinition>>
Trigger
#.id
#.container: Container
#.read(options?: RequestOptions) => Promise<Response<TriggerDefinition>>
#.replace(body: TriggerDefinition, options?: RequestOptions) => Promise<Response<TriggerDefinition>>
#.delete(options?: RequestOptions) => Promise<Response<TriggerDefinition>>
UserDefinedFunctions
#.getUserDefinedFunction(id: string) => UserDefinedFunction
#.query(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<UserDefinedFunctionDefinition>
#.read(options?: FeedOptions) => QueryIterator<UserDefinedFunctionDefinition>
#.create(body: UserDefinedFunctionDefinition, options?: RequestOptions) => Promise<Response<UserDefinedFunctionDefinition>>
#.upsert(body: UserDefinedFunctionDefinition, options?: RequestOptions) => Promise<Response<UserDefinedFunctionDefinition>>
UserDefinedFunction
#.id
#.container: Container
#.read(id: string, options?: RequestOptions) => Promise<Response<UserDefinedFunctionDefinition>>
#.replace(id: string, body: UserDefinedFunctionDefinition, options?: RequestOptions) => Promise<Response<UserDefinedFunctionDefinition>>
#.delete(id: string, options?: RequestOptions) => Promise<Response<UserDefinedFunctionDefinition>>
Offers
#.getOffer(id: string) => Offer
#.queryOffers(query: string | SqlQuerySpec, options?: FeedOptions) => QueryIterator<OfferDefinition>
#.readOffers(options?: FeedOptions) => QueryIterator<OfferDefinition>
Offer
#.id
#.readOffer(id: string, options?: RequestOptions) => Promise<Response<OfferDefinition>>
#.replaceOffer(body: OfferDefinition, options?: RequestOptions) => Promise<Response<OfferDefinition>>
User
#.id
Open Questions
Most of the API is top down, but can we put bottom up calls where it makes sense?
For instance,
client.readDatabase("foo")
instead beingclient.database("foo").read()
What are we doing with Users?
Are we going to continue to support partition resolver?
Change Log
The text was updated successfully, but these errors were encountered: