findOne and find functions

When you want to penetrate only one step in the depth of relationships, the best choices are find and findOne functions.
According to Mongo's behavior, only Aggregation can be used to send a left join query to receive relationships. But considering that all relationships are automatically embedded in Lesan, you can use find and findOne along with aggregation to get one level of relationships, i.e. the father along with all the children.

findOne functions

find a user

lets add getUser functions:

const getUserValidator = () => {
  return object({
    set: object({
      userId: objectIdValidation,
    }),
    get: coreApp.schemas.selectStruct("user", 1),
  });
};
const getUser: ActFn = async (body) => {
  const {
    set: { userId },
    get,
  } = body.details;

  return await users.findOne({
    filters: { _id: new ObjectId(userId) },
    projection: get,
  });
};
coreApp.acts.setAct({
  schema: "user",
  actName: "getUser",
  validator: getUserValidator(),
  fn: getUser,
});

findOne functions accept three inputs:

  • filters which is mongodb findOne query operation
  • projection which is mongodb projection operation
  • and optional option which is mongodb findOption

You can also read mongodb findOne section for more information.

executing mainusergetUser: Screenshot 2024-01-08 at 19-21-17 Lesan Playground

Add E2E Test

Like before, for adding getUser request to E2E section you should click on the E2E button, like below picture.

e2e sequence

Then, in the E2E section and getUser sequence, you should replace the user id that you set capture in own sequence with default user id. default user id in getUser sequence is like below picture.

e2e sequence

The replaced user id is like below picture.

e2e sequence

find a city or country

Finding a city or country is exactly the same as finding a user.
Pay attention to the following code:

const getCountryValidator = () => {
  return object({
    set: object({
      countryId: objectIdValidation,
    }),
    get: coreApp.schemas.selectStruct("country", {
      citiesByPopulation: 1,
      users: 1,
      capital: 1,
    }),
  });
};
const getCountry: ActFn = async (body) => {
  const {
    set: { countryId },
    get,
  } = body.details;

  return await countries.findOne({
    filters: { _id: new ObjectId(countryId) },
    projection: get,
  });
};
coreApp.acts.setAct({
  schema: "country",
  actName: "getCountry",
  validator: getCountryValidator(),
  fn: getCountry,
});

const getCityValidator = () => {
  return object({
    set: object({
      cityId: objectIdValidation,
    }),
    get: coreApp.schemas.selectStruct("city", { country: 1, lovedByUser: 1 }),
  });
};
const getCity: ActFn = async (body) => {
  const {
    set: { cityId },
    get,
  } = body.details;

  return await cities.findOne({
    filters: { _id: new ObjectId(cityId) },
    projection: get,
  });
};
coreApp.acts.setAct({
  schema: "city",
  actName: "getCity",
  validator: getCityValidator(),
  fn: getCity,
});

The only difference here is in the coreApp.schemas.selectStruct function. The second input of this function, instead of a number, is an object of the relationship key with a value of one number, which explicitly specifies how much this act can penetrate in this particular relationship.
executing maincitygetCity: Screenshot 2024-01-09 at 14-49-33 Lesan Playground

Add E2E Test

For adding getCity request to E2E test section, you should click on the E2E button, like bottom picture.

e2e sequence

Then, in the E2E section and getCity sequence, you should replace the city id that you set capture in own sequence with default city id. default city id in getCity sequence is like below picture.

e2e sequence

The replaced city id is like below picture.

e2e sequence

executing maincountrygetCountry: Screenshot 2024-01-09 at 15-03-45 Lesan Playground

You can find full example here and test the findOne method in local computer.

Add E2E Test

Also, for adding getCountry request to E2E test section, you should click on the E2E button, like bottom picture.

e2e sequence

Then, in the E2E section and getCountry sequence, you should replace the country id that you set capture in own sequence with default country id. default counry id in getCountry sequence is like below picture.

e2e sequence

The replace country id is like below picture.

e2e sequence

find functions

find users

lets add getUsers functions:

const getUsersValidator = () => {
  return object({
    set: object({
      page: number(),
      limit: number(),
    }),
    get: coreApp.schemas.selectStruct("user", 1),
  });
};
const getUsers: ActFn = async (body) => {
  let {
    set: { page, limit },
    get,
  } = body.details;

  page = page || 1;
  limit = limit || 50;
  const skip = limit * (page - 1);
  return await users
    .find({ projection: get, filters: {} })
    .skip(skip)
    .limit(limit)
    .toArray();
};
coreApp.acts.setAct({
  schema: "user",
  actName: "getUsers",
  validator: getUsersValidator(),
  fn: getUsers,
});

find functions accept three inputs:

You can also read mongodb find section for more information.
executing mainusergetUsers: Screenshot-2024-01-11-at-13-20-17-Lesan-Playground

Add E2E Test

For adding getUsers request to E2E test section, you should click on the E2E button, like bottom picture.

e2e sequence

Well, in the E2E section you can see the getUsers sequence.

find cities or countries

Finding cities or countries is exactly the same as finding users.
Pay attention to the following code:

const getCountriesValidator = () => {
  return object({
    set: object({
      page: number(),
      limit: number(),
    }),
    get: coreApp.schemas.selectStruct("country", {
      citiesByPopulation: 1,
      users: 1,
      capital: 1,
    }),
  });
};
const getCountries: ActFn = async (body) => {
  let {
    set: { page, limit },
    get,
  } = body.details;

  page = page || 1;
  limit = limit || 50;
  const skip = limit * (page - 1);
  return await countries
    .find({ projection: get, filters: {} })
    .skip(skip)
    .limit(limit)
    .toArray();
};
coreApp.acts.setAct({
  schema: "country",
  actName: "getCountries",
  validator: getCountriesValidator(),
  fn: getCountries,
});

const getCitiesValidator = () => {
  return object({
    set: object({
      page: number(),
      limit: number(),
    }),
    get: coreApp.schemas.selectStruct("city", { country: 1, lovedByUser: 1 }),
  });
};
const getCities: ActFn = async (body) => {
  let {
    set: { page, limit },
    get,
  } = body.details;

  page = page || 1;
  limit = limit || 50;
  const skip = limit * (page - 1);
  return await cities
    .find({ projection: get, filters: {} })
    .skip(skip)
    .limit(limit)
    .toArray();
};
coreApp.acts.setAct({
  schema: "city",
  actName: "getCities",
  validator: getCitiesValidator(),
  fn: getCities,
});

The only difference here is in the coreApp.schemas.selectStruct function. The second input of this function, instead of a number, is an object of the relationship key with a value of one number, which explicitly specifies how much this act can penetrate in this particular relationship.
executing maincitygetCities: Screenshot-2024-01-11-at-13-59-22-Lesan-Playground

Add E2E Test

Like before, for adding getCities request to E2E test section, you should click on the E2E button, like bottom picture.

e2e sequence

Well, in the E2E section you can see the getCities sequence.

executing maincountrygetCountries: Screenshot-2024-01-11-at-13-58-41-Lesan-Playground

You can find full example here and test the find method in local computer.

Add E2E Test

For adding getCountries request to E2E test section, you should click on the E2E button, like bottom picture.

e2e sequence

Well, in the E2E section you can see the getCountries sequence.