Hide Table of Contents
Application Layouts
Using QueryTask, Query, and FeatureSet

QueryTask, Query, and FeatureSet are used together to query a layer in a map and display the results.

The input into the process is Query. A user provides input to the query. This input might include selecting features on a map, selecting a value from a list, or typing in a value. The input is used to create a query filter. In the next step of the process, the QueryTask is executed based on the query filter. In the final step of the process, the results are returned in a FeatureSet. These results include the geometry of each feature selected for highlighting the features on the map. It also includes the tablular data for display in an InfoWindow or an HTML table.

The following discussion assumes you are already familiar with the basic concepts of the JavaScript API and Dojo, and you understand the basic patterns of how the JavaScript API works within an HTML page. For more information, see Adding a map and Adding a task.

Create the init() function

You can use the Init function to initialize the classes you will use in later functions.
  1. Initialize a Map and add a Layer.
  2. Initialize a QueryTask layer. Note that the URL includes the layer ID. In this example, the layer ID is "0".
  3. Initialize the Query. You should specify everything except the actual filter. In this example, a list of output fields is included in outFields. When you specify the output fields, you should limit the fields only to those you expect to use in the query or the results. The fewer fields you include, the faster the response will be. You must use the actual field names rather than the alias names, but you can use the alias names later when you display the results.
  4. Initialize InfoTemplate. If you plan to use an InfoWindow to display the results, this is a good time to construct the InfoTemplate. The syntax ${fieldName} is substituted at runtime with the actual attribute value. The first value is the title. The rest of the values make up the text of the InfoWindow. You can use any valid HTML.
  5. Initialize the Symbol you want to use for displaying the highlighted features on the map.
// legacy
function init() {
  //create map and add layer
  map = new esri.Map("mapDiv");
  var layer = new esri.layers.ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer");
  map.addLayer(layer);

  //initialize query task
  queryTask = new esri.tasks.QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0");

  //initialize query
  query = new esri.tasks.Query();
  query.returnGeometry = true;
  query.outFields = ["CITY_NAME", "STATE_NAME", "POP1990"];

  //initialize InfoTemplate
  infoTemplate = new esri.InfoTemplate("${CITY_NAME}", "Name : ${CITY_NAME}<br/> State : ${STATE_NAME}<br />Population : ${POP1990}");

  //create symbol for selected features
  symbol = new esri.symbol.SimpleMarkerSymbol();
  symbol.setStyle(esri.symbol.SimpleMarkerSymbol.STYLE_SQUARE);
  symbol.setSize(10);
  symbol.setColor(new dojo.Color([255,255,0,0.5]));
}

// AMD
require([
      "esri/map",
      "esri/layers/DynamicMapServiceLayer",
      "esri/tasks/QueryTask",
      "esri/tasks/query",
      "esri/symbols/SimpleMarkerSymbol",
      "esri/InfoTemplate",
      "dojo/_base/Color",
      "dojo/domReady!"], function(Map, DynamicMapServiceLayer, QueryTask, Query, SimpleMarkerSymbol, InfoTemplate, Color) {

        //create map and add layer
        map = new Map("mapDiv");
        var layer = new ArcGISDynamicMapServiceLayer("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer");
        map.addLayer(layer);

        //initialize query task
        queryTask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0");

        //initialize query
        query = new Query();
        query.returnGeometry = true;
        query.outFields = ["CITY_NAME", "STATE_NAME", "POP1990"];

        //initialize InfoTemplate
        infoTemplate = new InfoTemplate("${CITY_NAME}", "Name : ${CITY_NAME}<br/> State : ${STATE_NAME}<br />Population : ${POP1990}");

        //create symbol for selected features
        symbol = new SimpleMarkerSymbol();
        symbol.setStyle(SimpleMarkerSymbol.STYLE_SQUARE);
        symbol.setSize(10);
        symbol.setColor(new Color([255,255,0,0.5]));

  });
    

executeQueryTask function - the callback function

Create the query filter based on user input and execute the query. User input might include selecting features on a map, selecting a value from a list, or typing in a value. In this example, the user is instructed to enter a population value.

Create the callback function executeQueryTask()

The callback function is the function referenced from the input in the HTML page. In this example, a function named "executeQueryTask" is called after a user types in a population value and clicks the "Get Details" button.

The "population" value is plugged into Query.where. The where attribute is one of three filters you can use with Query:

  • Query.where: any valid SQL where statement. You need to make sure you have the correct sequence of single and double quotes when writing the where clause in JavaScript.

    String query:
      query.where = "NAME = '" + stateName + "'";
        
    Number query:
      query.where = "POP04 > " + population;
        
  • Query.text: shorthand for a where clause using "like". The field used is the display field defined in the map document. You can determine what the display field is for a layer in Services Directory.
      query.text = stateName;
        
  • Query.geometry: when a user selects features on a map.
      query.geometry = evt.mapPoint;
        
After you have created the query filter, you execute the query. You do this through the QueryTask.execute() method. The inputs to the method are the Query and a reference to a function named "showResults".
  function executeQueryTask(population) {
    //set query based on what user typed in for population;
    query.where = "POP04 > " + population;

    //execute query
    queryTask.execute(query,showResults);
  }
    

Create a function to show the results

The showResults() function has a FeatureSet as input. The FeatureSet is populated when the query is executed. This function parses the results in the FeatureSet and adds them to the GraphicsLayer. The geometry is used to draw graphic features on the map. The attribute values are added to graphic using the InfoTemplate. When a user clicks on a highlighted feature, an InfoWindow pops up with the results.
  function showResults(featureSet) {
    //remove all graphics on the maps graphics layer
    map.graphics.clear();

    //Performance enhancer - assign featureSet array to a single variable.
    var resultFeatures = featureSet.features;

    //Loop through each feature returned
    for (var i=0, il=resultFeatures.length; i<il; i++) {
      //Get the current feature from the featureSet.
      //Feature is a graphic
      var graphic = resultFeatures[i];
      graphic.setSymbol(symbol);

      //Set the infoTemplate.
      graphic.setInfoTemplate(infoTemplate);

      //Add graphic to the map graphics layer.
      map.graphics.add(graphic);
    }
  }
    

HTML BODY

The BODY section contains what the user sees on the HTML page.

  • The user has a place to type in the population. When the user clicks the "Get Details" button, the onclick event is invoked and calls the executeQueryTask() function.
  • The map displays in the mapDiv. Once the query is executed, the selected features are highlighted. When a user clicks on a feature, an InfoWindow appears.
  <body>
    <br/>
    US city population greater than : <input type="text" id="population" value="500000" />
    <input type="button" value="Get Details" onclick="executeQueryTask(dojo.byId('population').value);" />

    <div id="mapDiv" style="width:600px; height:600px; border:1px solid #000;"></div>
    Click on a city once it's highlighted to get an InfoWindow.

  </body>
     

Performance Considerations

You need to be careful that the query you are constructing is reasonable. Your audience will not understand the data as well as you. As a result, users may make queries that are unrealistic or request too much data. There is a direct correlation between performance and the number of features selected.

  • If a large number of features are selected, it can take some time to draw the graphics on the map. The type of feature also makes a difference. Points draw much faster than lines and polygons. Therefore, whenever you can use point graphics, you should.
  • If you are retrieving tabular data and do not intend to add graphics to the map, you should not request geometry.
  • If queries are too large on a regular basis, you might want to limit the query to a smaller area or refine the query so fewer features are returned at one time.

Queries on cached layers do not have scale dependencies. This means that features for a layer can be queried even if the layer is not displayed on the map for a given scale. Thus, your query should not be accessible for a layer if displaying the results does not make sense to the end user.

You should also consider that users can inadvertently or maliciously request unlimited numbers of features. The maximum number of features returned can be specified in the properties for each map service. By default, this maximum is set to 1000 features. Some common ways a user can request the maximum number of records for a layer are:

  • Request all cities with a population greater than 0.
  • Send an empty where statement (where="")
  • Send a statement that is always true (where="1=1").
  • Selecting all features in a layer.

You can trap for these types of queries in your application. If you are hosting services that other users may include in their application, you may want to protect your site from multiple large requests by limiting the number of features that can be returned in a query response by setting a reasonable value in MaxRecordCount.