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
Lesanframework, for example (this sample is written in TypeScript):
const ecommerceApp = lesan();
- We write the
modelwe want for the software using thepureandrelationsobject and add it to ourODMapplication. Like this (consider the same information we mentioned in the above example forcountryandcities):
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
validatorfor 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
validatorfunctiongetCitiesValidatorhas anobjectwith keyssetandget. Thesetkey is used for information that we need tofilterandlimitcities, and as you saw ingetCitiesFn, this information has been used. But the value of thegetkey is what we need to penetrate into its depth. This value must be anobjectof a model that accurately specifies the degree of penetration into each of therelationshipsof thatmodel(here incitymodelr. Here thegetkey is generated by a function calledselectStruct. This function has two inputs. The first input is the name of themodelfor which we want to generate thisobject, and the second input specifies thedegreeof penetration into eachrelationship. The second input of theselectStructfunction can be entered as anumberor anobject. If entered as anobject, thekeysof this object must be thenamesof the selected modelrelationships, and its value can again be anumberor anobjectof 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,
});