Legacy API
This page documents the legacy @logic-bee/flow-query package, which has been superseded by @logic-bee/data-flow. See Reading documents and the migration guide for equivalents. New code should use the new API.
Search
Search for one or more documents by ID, query, perform aggregations with integrated user access and document-level permissions.
getOne()
Get one document using a docId variable sent in the request payload.
// -->Get: document
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.docId(bob.dataPayload.docId)
.getOne();
getMany()
Get multiple documents using a docIds array variable sent in the request payload.
// -->Get: documents
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.docIds(bob.dataPayload.docIds)
.getMany();
query()
Search queries can be performed using flowQuery in any operation, including bulk.
Supported operators:
// -->Search: documents by the id of another document
const libraryDocuments = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
'data.libraryId': bob.dataPayload.libraryId
})
.getMany();
// -->Search: documents by an array of ids
const libraryDocuments = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
'data.libraryId': { $in: bob.dataPayload.docIds }
})
.getMany();
// -->Search: documents by type and a value higher than n
const libraryDocuments = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
'data.type': 'salary',
'data.salary': { $gt: 500 }
})
.getMany();
// -->Search: with skip and limit
const libraryDocuments = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.limit(5000)
.skip(100)
.query({
'data.salary': { $lt: 100 }
})
.getMany();
Using Dynamic Queries
// -->Get: documents that match a specific field
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
'data.name': 'Dante'
})
.getMany();
// -->Get: documents with dynamic user-provided query
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
...bob.dataPayload.query
})
.getMany();
// -->Get: limited results from dynamic query
const limitedDocData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.query({
...bob.dataPayload.query
}).limit(100).getMany();
getDocumentStatuses()
// -->Query: get documents that match the statuses
const status$ = await flowCollection.docs.flowQuery()
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.getDocumentStatuses(['processing', 'awaiting-approval'], 'data')
.getMany();
count()
// -->Query: count the number of documents that match the query
const count$ = await flowCollection.docs.flowQuery()
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.count();
sortByFieldValue()
// -->Query: use the field value to sort the documents
const docs$ = await flowCollection.docs.flowQuery()
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.sortByFieldValue({ 'docId': 'hvastvavsdgas' })
.getMany();
Search Options
limit()
// -->Get: documents with limit and skip
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.limit(100)
.skip(20)
.flowOptions(bob.naoQueryOptions)
.query({
'data.name': 'Dante'
})
.getMany();
skip()
// -->Get: documents with skip and limit
const docData = await flowCollection.docs.flowQuery()
.addPolicy('canUpdate')
.user(bob.flowUser)
.limit(100)
.skip(20)
.flowOptions(bob.naoQueryOptions)
.query({
'data.name': 'Dante'
})
.getMany();
Full Example: Frontend to Backend
- Frontend
- Request Payload
- Request Configuration
- Action
- Response
// -->Execute: request for info
this.naoFlowService.reqData({
cfpSlug: 'project',
action: 'data/get-by-id',
data: {docId: this.projectId},
naoQueryOptions: {docName: 'project', cfpPath: 'projects/projects'}
})
.execute()
.subscribe({
next: (res) => {
console.warn(` res > `, res)
},
error: (err) => {
this.status.error();
},
complete: () => {
//
},
});
{
"data": {
"naoQueryOptions": {
"docName": "project",
"cfpPath": "project/project"
},
"data": {
"docId": "jnbaduygay7fduyvadf"
}
}
}
{
cfpPath: 'projects/projects',
slug: 'projects',
requestPresets: [
{
action: 'search',
dataType: 'data',
requestTypes: ['POST'],
compatibleFlowDocs: [
{ docType: 'project' }
],
allowPublicRequests: false,
jobs: [
{ eventName: 'nao-app-data/nao-app-data.data.validateCrudRequest', setInitialPayload: true, options: { requestType: 'list' } },
{ eventName: 'nao-app-data/nao-app-data.crudHooks.search', returnPayload: true }
]
}
]
}
/**
* Get one document by docId
*/
public static getDocument() {
return async (bob: BobRequest<{
data: {
data: { docId: string },
naoQueryOptions: NaoQueryOptions
}
}>) => {
let ok = true, error: any = null, data;
try {
/**
* Validate
*/
bob.validator.joi().validateData(bob.dataPayload, SuperJoi.object({
docId: SuperJoi.string().documentId(),
}).required(), bob.flowUser.getUserInfo());
// -->Get: the flow collection
const flowCollection = bob.flowGlobal.getFlowCollection(bob.cfpPath);
// -->Get: the document
data = await flowCollection.docs.flowQuery()
.addPolicy('canRead')
.user(bob.flowUser)
.flowOptions(bob.naoQueryOptions)
.docId(bob.dataPayload.docId)
.getOne();
// -->Set: the status
ok = true;
} catch (err) {
error = err;
ok = false;
}
// -->Return: result
return returnEventResult(bob, {ok, error, data});
};
}
{
"ok": true,
"meta": {
"workerId": "worker-100",
"requestId": "CbK4V0VP6hL",
"events": [
{
"eventName": "nao-app-data/nao-app-data.data.validateCrudRequest",
"ms": 15.302342
},
{
"eventName": "nao-app-data/nao-app-data.crudHooks.search",
"ms": 1571.456272
}
],
"request": {
"eventName": "request",
"ms": 1590.085056
},
"mode": "active",
"total": 1,
"totalDocuments": 1,
"jobsLog": []
},
"returnType": "data",
"eventType": "data",
"data": [
{
"_id": "619a5045e12ae3f8ec661dab",
"docId": "LKAet6ret4fWcGg2vq554RYG",
"data": {
"name": "CRM",
"slug": "crm",
"categoryId": "sales",
"industryId": "b2b-ecommerce"
},
"status": "active",
"docType": "project"
}
]
}
Best Practices
✅ DOs:
- Always use
flowQueryto run search queries - Always include
.user(bob.flowUser)on every query - Always add the appropriate access policy:
.addPolicy('canRead')
❌ DON'Ts:
- Do not run a query without
.user(bob.flowUser) - Do not run a query without adding access policies
info
Default policies are canRead, canUpdate, canDelete, canCreate. The Builder has the ability to create custom policies that can be used with different roles.