Query APIs
WeaveDB queries cover most of the things Firestore can do with syntactic sugar inspired by Firestore Sweet.
getInfo
Get configurations
await db.getInfo()
getHash
Get current hash of chained txs.
WeaveDB contracts keep track of valid transactions by hashing chained txIds like blockchain.
latest_hash
= hash( [ last_hash
, current_txId
] )
await db.getHash()
getNonce
To get the next nonce for an address. Nonces are internally used for signature verification to write data.
await db.getNonce("address")
get / cget
get
only returns data, whereas cget
returns metadata of the docs too.
{ id, setter, data, block: { height, timestamp } }
The metadata returned with cget
functions as a cursor for pagination.
Get a doc
await db.get(collection_name, doc_id)
await db.cget(collection_name, doc_id)
Get a collection
await db.get(collection_name)
await db.cget(collection_name)
Get a sub collection
Arbitrary length of document nesting is possible.
await db.get(collection_name, doc_id, sub_collection_name_1, sub_doc_id_1, sub_collection_name_2)
Limit
await db.get(collection_name, 5)
Sort
await db.get(collection_name, [ "age" ])
await db.get(collection_name, [ "age", "desc" ])
await db.get(collection_name, [ "age", "desc" ], [ "name", "asc" ])
Where
await db.get(collection_name, ["age"], [ "age", ">", 20 ])
==
>
>=
<
<=
!=
in
not-in
array-contains
array-contains-any
are supported.
Dot notation can be used to specify nested object fields. (e.g. [ "favorites.food", "==", "apple" ]
)
Note that dot notation only works with where
for now.
=
is deprecated and replaced by ==
at v0.23.0
. You can still use it for backward compatibility.
Skip
await db.get(collection_name, [ "age" ], [ "startAfter", 20 ], [ "endAt", 60 ])
await db.get(collection_name, [ "age" ], [ "name", "desc" ], [ "startAfter", 20, "Bob" ])
startAt
startAfter
endAt
endAfter
are supported.
Pagination
const docs_page1 = db.cget(collection_name, [ "age" ])
const docs_page2 = db.cget(
collection_name,
["age"],
["startAfter", docs_page1[docs_page1.length - 1]]
)
on / con
You can subscribe to state changes with on
and con
. They are the counterparts of get
and cget
respectively.
These only work with weavedb-sdk-node
for now.
const unsubscribe = await on.(collection_name, doc_id, (data) => {
console.log(data)
unsubscribe()
})
getCache / cgetCache
They are the same as get
/ cget
, but get values from the cached state, which is faster but may not be the most up-to-date values.
These only work with weavedb-sdk-node
for now.
await db.getCache(collection_name, doc_id)
await db.cgetCache(collection_name, doc_id)
listCollections
List collection names
await db.listCollections() // list root collections
await db.listCollections(collection_name, doc_id) // list sub collections
nocache
With weavedb-client
and weavedb-node-client
, if the last argument is boolean, it's recognized as nocache
option.
gRPC nodes use getCache
/ cgetCache
to get data by default, but the up-to-date values can be obtained with nocache
option set true
.
You would use nocache
after updating data to get the latest values.
await db.set({ field : "value"}, collection_name, doc_id)
await db.get(collection_name, doc_id, true) // without true, the data might be old
add
Add a doc
await db.add({ age: 20, name: "Bob" }, collection_name)
The doc id will be randomly yet deterministically assigned.
getIds
To get the last added doc id, use getIds
.
const tx = await db.add({ age: 20, name: "Bob" }, collection_name)
const doc_id = (await db.getIds(tx))[0]
set
Set a doc
await db.set({ age: 20, name: "Bob" }, collection_name, doc_id)
upsert
Upsert a doc
await db.upsert({ age: 20, name: "Bob" }, collection_name, doc_id)
update
Update a doc
await db.update({ age: 25 }, collection_name, doc_id)
del()
Delete a field
await db.update({ age: db.del() }, collection_name, doc_id)
inc()
Increase/Decrease a field
await db.update({ age: db.inc(5) }, collection_name, doc_id)
await db.update({ age: db.inc(-5) }, collection_name, doc_id)
union()
Array union
await db.update({ chars: db.union([ "a", "b", "c", "d" ]) }, collection_name, doc_id)
remove()
Array remove
await db.update({ chars: db.remove([ "b", "c" ]) }, collection_name, doc_id)
ts()
Set block timestamp
await db.update({ date: db.ts() }, collection_name, doc_id)
signer()
Set signer Ethereum address
await db.update({ address: db.signer() }, collection_name, doc_id)
data()
Replace a field with relayer's extra data
// icon field will be automatically replaced with `image.png` from the relayer
const query = await db.sign(
"update",
{ icon: db.data("extra_data_key") },
collection_name,
doc_id,
{ jobID: "jobID", evm: signer_wallet },
)
await db.relay("jobID", query, { extra_data_key: "image.png" }, { evm: relayer_wallet })
zkp()
Verify Zero Knowledge Proof with PolygonID
await db.update({ userID: db.zkp(proof, pub_signals) }, collection_name, doc_id)
/*
userID will be replaced with the following
you should modify it further in access control rules
{
valid,
pub_signals: {
value,
merklizedn,
userID,
issuerAuthState,
requestID,
issuerID,
isRevocationChecked,
issuerClaimNonRevState,
timestamp,
claimSchema,
claimPathNotExists,
claimPathKey,
slotIndex,
operator,
},
}
*/
delete
Delete a doc
await db.delete(collection_name, doc_id)
batch
Atomic batch write from a single signer
await db.batch([
["set", { name: "Bob" }, "people", "Bob"],
["upsert", { name: "Alice" }, "people", "Alice"],
["delete", "John"]
])
Admin queries can be batch-executed as well
await db.batch([
["setSchema", schema, "people"],
["setRules", rules, "people"],
["addOwner", "0xABC"]
], { ar : admin_arweave_wallet })
sign
Sign a query without sending a transaction
await db.sign("set", {name: "Bob", age: 20}, collection_name, doc_id)
relay
Relay a query
const param = await db.sign("set", {name: "Bob"}, collection_name, doc_id)
const extra = { age: 20 }
await db.relay("jobID", param, extra, {evm: relayer_wallet})
bundle
Bundle multiple queries from multiple signers
const query1 = await db.sign("set", {name: "Bob"}, "people", "Bob", {evm: wallet1})
const query2 = await db.sign("set", {name: "Alice"}, "people", "Alice", {ii: wallet2})
const query3 = await db.sign("set", {name: "Beth"}, "people", "Beth", {ar: wallet3})
// bundle query needs no signer wallet
await db.bundle([query1, query2, query3])
setBundlers / getBundlers
Restrict bundlers, if bundlers are set, no one else but bundlers can write to the DB
await db.setBundlers([ BUNDLER_ADDRESS_1, BUNDLER_ADDRESS_2 ], {evm : admin_wallet})
// now only bundle queries can be accepted
await db.getBundlers()
// return bundlers, if empty, anyone can execute bundle queries
Restricting bundlers is useful to make queries consistent with low latencies via a rollup node.