Queries
How to conduct a query
With the ADS architecture in place, we can now conduct queries on the zkDB. Letβs explore how this is done.
Note that all queries return the count for the number of satisfying users (or number of rows) in the database. This is for privacy reasons, we will not return actual user data.
Anatomy of a Query
A query is a request for information from the database. More importantly, we can construct virtually any query we would like from a few basic operations.
- Set membership:
- Set non-membership:
- Intersection:
- Union/Join:
- Sum:
- Count:
- Min, Max: )
- Mean, Median:
Using these we can conduct any user-data related query we would like. Moreover, we can represent this query in a specific way, namely as where each is of the form and each is set (non-)membership.
Query Engine
We have to do things in a certain order to ensure it is efficient and flexible.
Grab accumulators from ADS
Since our ADS has leaves with accumulators representing single values, we have to take them from the data structure one by one along with their Merkle proofs.
Conduct inner unions
In order to conduct ranges or set membership, we need to create a new accumulator that contains all values in the range or set. So grab each accumulator for each value in the range, and conduct a union on them (for the polynomials this is equivalent to the lcm operation).
Conduct intersections
After all the unions, we have union proofs, as well as the union accumulators. Now we can conduct the intersection proofs of each
Conduct outer union
Now that we have some intersection accumulators and their proofs, we can conduct the outer union proof.
Property
Finally, we can conduct a property assessment of the accumulator, such as a count (the number of users in the accumulator), min, max, mean or median. We have subprotocols we can apply to the resulting accumulator from the previous step to get these values.
Verifiers can check the query by checking the proofs in iteration:
- Check the ADS proof to ensure the database is valid.
- Check the Merkle proofs to ensure that the accumulators were grabbed correctly.
- Check the inner union proofs to ensure that the unions were conducted correctly.
- Check the intersection proofs to ensure that the intersections were conducted correctly.
- Check the outer union proof to ensure that it was conducted correctly.
- Finally, check the property proof to ensure that the result is correct.
In the image below is a birdseye view of the process, assuming count as the property check.
Query Example
Letβs say we want to find all users with a Masters or PhD who live in London or Istanbul. Our query would be.
(education {Masters, PhD}) (location {London, Istanbul})
To conduct this we would:
- Grab the accumulators for Masters and PhD, and conduct a union on them.
- Grab the accumulators for London and Istanbul, and conduct a union on them.
- Conduct an intersection on the two unions.
- Conduct the outer union on the intersection (in this case there is only one accumulator, so this step is redundant).
- Count the number of users that satisfy the query (this is the degree of the accumulator polynomial we just made).
If we wanted to include the outer union necessity, we could instead query for all users with a Masters or PhD who live in London or Istanbul, or users with a Masters in Hong Kong. This would be.
((education {Masters, PhD}) (location {London, Istanbul})) ((education {Masters}) (location {Hong Kong}))