Penetration Into Depths
The next issue is how to penetrate the depths
. In GraphQL
, you have very complex solutions to manage it, but in Lesan
, this issue does not exist by default because in projects written with Lesan
, the server-side programmer must determine the depth
of the relationships of each accessible point from the program before
writing any accessible point. Let’s take a look at implementing a small project with Lesan
to clarify the matter.
In Lesan
, starting a project will be like this:
- First of all, we create an app with the
Lesan
framework, for example (this sample is written in TypeScript):
const ecommerceApp = lesan();
- We write the
model
we want for the software using thepure
andrelations
object and add it to ourODM
application. Like this (consider the same information we mentioned in the above example forcountry
andcities
):
const cityPure = {
_id: optional(union([
instance(ObjectId),
size(string(), 24),
]));
name: string(),
geometries: optional(
object({
type: string(),
coordinates: array(array(number())),
})
),
abb: optional(string()),
description: optional(string()),
};
const cityRelations = {
country: {
optional: false,
schemaName: "country",
type: "single" as RelationDataType,
relatedRelations: {
cities: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "_id",
order: "desc" as RelationSortOrderType,
},
},
citiesByPopulation: {
type: "multiple" as RelationDataType,
limit: 50,
sort: {
field: "population",
order: "desc" as RelationSortOrderType,
},
},
capital: {
type: "single" as RelationDataType,
},
},
},
};
const cities = coreApp.odm.newModel(
"city",
cityPure,
cityRelations,
);```
- Now we create a `function` for this `model` and add it to our application `acts` (in fact, this `act` is available to the `client-side` user to call it) like this:
```ts
const getCitiesFn: ActFn = async (body) => {
const {
set: { page, take, countryId },
get,
} = body.details;
const pipeline = [];
pipeline.push({ $skip: (page - 1) * take });
pipeline.push({ $limit: take });
countryId &&
pipeline.push({ $match: { "country._id": new ObjectId(countryId) } });
return await cities
.aggregation({
pipeline,
projection: get,
})
.toArray();
};
- Now we write a
validator
for this function as follows:
const getCitiesValidator = () => {
return object({
set: object({
page: number(),
take: number(),
countryId: optional(size(string(), 24)),
}),
get: coreApp.schemas.selectStruct("city", 2),
});
};
- As you can see, the
validator
functiongetCitiesValidator
has anobject
with keysset
andget
. Theset
key is used for information that we need tofilter
andlimit
cities, and as you saw ingetCitiesFn
, this information has been used. But the value of theget
key is what we need to penetrate into its depth. This value must be anobject
of a model that accurately specifies the degree of penetration into each of therelationships
of thatmodel
(here incity
modelr. Here theget
key is generated by a function calledselectStruct
. This function has two inputs. The first input is the name of themodel
for which we want to generate thisobject
, and the second input specifies thedegree
of penetration into eachrelationship
. The second input of theselectStruct
function can be entered as anumber
or anobject
. If entered as anobject
, thekeys
of this object must be thenames
of the selected modelrelationships
, and its value can again be anumber
or anobject
of its key relationships. Such as:
{ country : { provinces: 2 }, cities: 1 }
As a result, an object will be produced as follows :
{
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
country: {
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
provinces: {
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
},
cities: {
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
},
},
province: {
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
cities: {
_id: enums([0,1]),
name: enums([0,1]),
abb: enums([0,1]),
description: enums([0,1]),
geoLocation: enums([0,1]),
}
}
}
This object
is used for validating
the data sent from the client
to the server
. With this method, we have accurately and separately determined the depth
of penetration for each function. If you notice, the key get
is exactly similar to projection
in MongoDB
and after validating this object
, we send it to the database
without any changes to receive the data. Besides, we can inform the customer side of all the details of the written requests on the server
. As a result, even before sending a request on the customer's side, we can understand what information needs to be sent and what information we can receive. Finally, we add this function and validator
to our software with the setAct
function.
ecommerceApp.acts.setAct({
schema: "city",
actName: "getCities",
validator: getCitiesValidator(),
fn: getCitiesFn,
});