Skip to content
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

Querying over composite key #664

Closed
Gdowski opened this issue Feb 18, 2022 · 7 comments
Closed

Querying over composite key #664

Gdowski opened this issue Feb 18, 2022 · 7 comments

Comments

@Gdowski
Copy link

Gdowski commented Feb 18, 2022

Hi, I was wondering if my thinking is wrong or that's how it's supposed to work, but
While querying the composite key:

pub const STATE: Map<(Addr,Addr),State>

While using

.prefix(addr)

I'm able to query only by the first element of the composite key.
I have two addresses as composite key, let's say (owner, spender)
I'd like to write a query "give me all records for the owner with such address" (that's easy).
But also "give me all records for spender with such address" (this is where I'm lost).
Also "give all records where such address is either owner or spender" would also be great.
Is there a way to query both/second-part only?
Digging through issues on git and documentation didn't find an answer

I'm using the latest cosmwasm storage plus

@maurolacy
Copy link
Contributor

maurolacy commented Feb 19, 2022

...
But also "give me all records for spender with such address" (this is where I'm lost).

There are various ways to solve this. The easiest, but not very space efficient, would be to create a "reverse" Map of (spender, owner) keys.

A better / proper way would be to create an index of State(s) per spender over the original map. See examples of MultiIndex. There's also UniqueIndex, but here I think you'll need the former, as there can be many owners (that means, multiple entries over the spender index) for a given spender.

Also "give all records where such address is either owner or spender" would also be great. Is there a way to query both/second-part only? Digging through issues on git and documentation didn't find an answer

That can be done using the first range query you mention, extended or composed by a range query over the spender index (or the other map).

I'm using the latest cosmwasm storage plus

Cool. There are some examples of using indexes in the storage-plus README.md. That should be up-to-date. Please shoot us a message if you find some inconsistent or outdated info there or elsewhere.

@Gdowski
Copy link
Author

Gdowski commented Feb 19, 2022

Thx for the replay!
I was thinking that it will come down to multi-index but thought that this feature is more 'built-in' somewhere,
Especially when I looked into keys.rs where the PrimaryKey trait is defined. It has this example:

/// By example, for a 2-tuple (T, U):
///
/// T: Prefix.
/// U: Suffix.
/// (): Sub-prefix.
/// (T, U): Super-suffix.

And the same way you can do
.prefix(Addr)
doing
.suffix(Addr)
would be a cool feature. But maybe I'm resolving some edge case that there's no need for in the broader scheme.
Anyways, thanks for pointing me in the right direction!

@maurolacy
Copy link
Contributor

Those "suffix" thingies are for annotating the deserialisation type of the keys, not ranging. Perhaps we should clarify that in the docs.

What you propose is cool, but tantamount to using indexes. Need to think more about it, but I think it would require to create and maintain those indexes, just for providing the suffix queries; which is not very convenient.

@Gdowski
Copy link
Author

Gdowski commented Feb 19, 2022

I will give a shot on the multi-index then, thanks again!

@Gdowski Gdowski closed this as completed Feb 19, 2022
@Gdowski
Copy link
Author

Gdowski commented Feb 20, 2022

Hi,
I've managed to get my project working with multi-index, thanks again for setting me up on the right path.
However, during development, I've come upon some shortcomings (?)

There is no logical OR statement possible while querying storage, is this by design or is it a possible feature
2)
Creating Indexes and IndexedMap by yourself is quite 'wordy' it all will be abstracted away in future releases I guess?
3)
&Addr vs Addr as Key in Map? Is there a reasonable difference between these two?
4)
While defining MultiIndex
pub struct MultiIndex<'a, IK, T, PK = ()>
What is the value-added in the benefit of setting up PK to anything different than the default (or type of IK)?
Can't wrap my head around it.

Sorry if these questions are uber simple and I'm taking off your development time guys 😄
I'm super fresh here

@maurolacy
Copy link
Contributor

maurolacy commented Feb 21, 2022

Hi, I've managed to get my project working with multi-index

Glad to hear that.

, thanks again for setting me up on the right path. However, during development, I've come upon some shortcomings (?)

There is no logical OR statement possible while querying storage, is this by design or is it a possible feature

This can be done manually, by combining two queries. I don't think we'll add that, as the storage-plus API is already crowded. But open an issue if you want, detailing your use case.

  1. Creating Indexes and IndexedMap by yourself is quite 'wordy' it all will be abstracted away in future releases I guess?

Yes, that is one of the outstanding things we want to improve. There's an open issue for it: #531.

  1. &Addr vs Addr as Key in Map? Is there a reasonable difference between these two?

With &Addr, you can avoid extra cloning when building / accessing the keys. It can be a little more difficult to handle, but if possible, it's recommended to use references / borrowing for the keys.

  1. While defining MultiIndex pub struct MultiIndex<'a, IK, T, PK = ()> What is the value-added in the benefit of setting up PK to anything different than the default (or type of IK)? Can't wrap my head around it.

The value is that if you want to use the primary key in some way, it's already deserialised for you. That is particularly useful with composite keys. The default of () is there for backwards compatibility with existing code. Also, if you don't need the PK, having that set to () saves some CPU / gas.

Sorry if these questions are uber simple and I'm taking off your development time guys 😄 I'm super fresh here

Good questions. Glad that it worked for you.

@Gdowski
Copy link
Author

Gdowski commented Feb 21, 2022

Thanks a lot for the answers, hope to contribute in some way in the near future. 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants