Sök/filtrera: Bygg mongo-queries från select-input

På en mäklarsajt är det ganska troligt att man vill kunna filtera fram sökresultat utifrån ett antal val i listor (selects), t.ex. vilken stad, minsta och högsta tänkbara pris, minsta och högsta tänkbara yta etc.

Hur översätter vi denna typ av användarinput till en mongofråga som kan ställas vi en mongresto-skapad ngResource i Angular?

För att kunna förstå exemplet behöver du känna till hur $and, $eq, $lte och $gte fungerar i mongo-queries. (Se MongoDB Manual, $and.)

I din template

I din template har du ett antal bootstrap-stylade select-listor. Detta är bara ett exempel. Troligen vill du ha fler val/vara mer detaljerad – för att skapa en bra användarupplevelse.

<fieldset class="form-group">
  <select ng-model="townSel" class="form-control">
    <option value="">Stad</option>
    <option value="Malmö">Malmö</option>
    <option value="Lund">Lund</option>
    <option value="Trelleborg">Trelleborg</option>
  </select>
</fieldset>

<fieldset class="form-group">
  <select ng-model="minPriceSel" class="form-control">
    <option value="">Lägsta pris</option>
    <option value="0">0 kr</option>
    <option value="500000">500 000 kr</option>
    <option value="10000000">1000 000 kr</option>
  </select>
</fieldset>

<fieldset class="form-group">
  <select ng-model="maxPriceSel" class="form-control">
    <option value="">Högsta pris</option>
    <option value="1000000">1 000 000 kr</option>
    <option value="2000000">2000 000 kr</option>
    <option value="5000000">5000 000 kr</option>
  </select>
</fieldset>

<fieldset class="form-group">
  <select ng-model="minAreaSel" class="form-control">
    <option value="">Minsta yta</option>
    <option value="20">20 m2</option>
    <option value="50">50 m2</option>
    <option value="100">100 m2</option>
    <option value="200">200 m2</option>
  </select>
</fieldset>

<fieldset class="form-group">
  <select ng-model="maxAreaSel" class="form-control">
    <option value="">Största yta</option>
    <option value="50">50 m2</option>
    <option value="100">100 m2</option>
    <option value="200">200 m2</option>
    <option value="500">500 m2</option>
  </select>
</fieldset>

I din controller

I din Angular-controller (stand alone eller inuti ett directive):

// The $scope variables to watch and 
// what they correspond to in your mongoose model
var options = {
  townSel: {
    modelProperty: "town",
    type: String,
    operator: "$eq"
  },
  minPriceSel: {
    modelProperty: "price",
    type: Number,
    operator: "$gte"
  },
  maxPriceSel: {
    modelProperty: "price",
    type: Number,
    operator: "$lte"
  },
  minAreaSel: {
    modelProperty: "area",
    type: Number,
    operator: "$gte"
  },
  maxAreaSel: {
    modelProperty: "area",
    type: Number,
    operator: "$lte"
  }
};

// The $scope variables to watch as an array
var toWatch = [];
for(var i in options){ toWatch.push(i); }

// Watch the variables for changes
$scope.$watchGroup(toWatch, function(){
  var query = {$and:[]}, partQuery, val, ops;
  // Build a mongo $and query by looping through the options
  for(var i in options){
    ops = options[i];
    // Get the value from $scope, convert numbers to numbers
    val = ops.type === Number ? $scope[i] / 1 : $scope[i];
    // Ignore empty and faulty values
    if(!val){ continue; }
    if(ops.type === Number && isNaN(val)){ continue; }
    // Build this part of the query
    partQuery = {};
    partQuery[ops.modelProperty] = {};
    partQuery[ops.modelProperty][ops.operator] = val;
    // Add it to the main query
    query.$and.push(partQuery);
  }
  // $and must never be an empty array
  if(query.$and.length === 0){ delete query.$and; }
  // Debug, check how the query looks
  console.log(JSON.stringify(query,'','  '));
  // Query the database through a ngResource object
  $scope.properties = Property.get(query);
});

Fråga oss gärna om ovanstående kod om den känns klurig att förstå!

Exempel på frågor som byggs

Exempel 1

Om användaren inte väljer någonting kommer frågan att se ut så här:

{}

vilket returnerar alla fastigheter.

Exempel 2

Om användaren bara väljer staden Lund kommer frågan att se ut så här:

{
  "$and": [
    {
      "town": {
        "$eq": "Lund"
      }
    }
  ]
}

Exempel 3

Om användaren väljer staden Lund, lägsta pris 500 000 kr, högsta pris 2000 000 kr, minsta yta 50 m2 och största yta 200 m2, kommer frågan att se ut så här:

{
  "$and": [
    {
      "town": {
        "$eq": "Lund"
      }
    },
    {
      "price": {
        "$gte": 500000
      }
    },
    {
      "price": {
        "$lte": 2000000
      }
    },
    {
      "area": {
        "$gte": 50
      }
    },
    {
      "area": {
        "$lte": 200
      }
    }
  ]
}
0 votes