Home > Software engineering >  Find start and end in order in an array of objects - MongoDB
Find start and end in order in an array of objects - MongoDB

Time:12-29

everyone. Hope you are doing well. I currently have this problem.

This is my 'Travel' Schema.

    const TravelSchema = new mongoose.Schema({
        ...
        route: [{
            index: { type: Number},
            lat: { type: Number },
            lng: { type: Number },
        }],
        ...
    });

This is where I handled post request. (Save new Travel)

    ...
    const {route, ...rest} = req.body;
    const newTravel = new Travel({
        route: route.map((location, index) => ({index, ...location})),
        ...rest
    });
    const Travel = await newTravel.save();
    ...

Search by start and end locations in order. tart and end locations are objects with 'lat' and 'lng' properties.

    ...
    const { start, end } = req.body;
    //start -> {lat: 51.11213, lng: 19.1239219} //for example
    const travels = await Travel.find({
        route: { $all: [ {$elemMatch: {...start}}, {$elemMatch: {...end}} ] },
        $expr: {
            $lt: [
                {$arrayElemAt: ['$route.index', {$indexOfArray: ['$route', {...start}]}]},
                {$arrayElemAt: [{$reverseArray: '$route.index'}, {$indexOfArray: [{$reverseArray: '$route'}, {...end}]}]},
            ]
        }
    });
    console.log(travels); // [] is printed
    ...

Without $expr, I can get some results but not accurate ones. What I want exactly is like this:

    route: [{index: 0, lat: 54, lng: 13},  //same as end but not considered regarding order
            {index: 1, lat: 32, lng: 51},
            {index: 1, lat: 10, lng: 30},  //start
            {index: 2, lat: 54, lng: 13}]  //end
    start: {lat: 10, lng: 30}, end: {lat: 54, lng: 13}
    the route is filtered

    route: [{index: 0, lat: 54, lng: 13},  //end
            {index: 1, lat: 32, lng: 51},
            {index: 1, lat: 10, lng: 30}]  //start
    start: {lat: 10, lng: 30}, end: {lat: 54, lng: 13}
    the route is not filtered this time

Would you please help me to fix this issue? Thanks in advance.

CodePudding user response:

If I understand correctly, you can use $min and $max with $reduce. Since $indexOfArray returns the index of the first occurrence, it should not be used here, as you may have the same corrdinated multiple times. Using $reduce can create an array of all relevant indices for the start point and for the end point. Using $min and $max allows to check if there is a start point index that is smaller than an end-point index:

db.collection.find({
  route: {$all: [ {$elemMatch: {...start}}, {$elemMatch: {...end}} ] },
  $expr: {$lt: [
      {$min: {
          $reduce: {
            input: "$route",
            initialValue: [],
            in: {
              $cond: [
                {$and: [{$eq: ["$$this.lat", start.lat]}, {$eq: ["$$this.lng", start.lng]}]},
                {$concatArrays: ["$$value", ["$$this.index"]]},
                "$$value"
              ]
            }
          }
      }},
      {$max: {
          $reduce: {
            input: "$route",
            initialValue: [],
            in: {
              $cond: [
                {$and: [{$eq: ["$$this.lat", end.lat]}, {$eq: ["$$this.lng", end.lng]}]},
                {$concatArrays: ["$$value", ["$$this.index"]]},
                "$$value"
              ]
            }
          }
      }}
    ]
  }
})

See how it works on the playground example

  • Related