Skip to main content

Lab - Application Package

In this lab you will structure your e-commerce application as a proper application package with query profiles. You will also add a rank profile placeholder and redeploy to see live configuration changes.

Prerequisites​

Your Vespa container should be running with the 20-product dataset from Lab 2.

Review Your Current Structure​

Your ecommerce-app/ directory should look like this:

ecommerce-app/
├── schemas/
│ └── product.sd
└── services.xml

This is already a valid application package. Now we will add query profiles and prepare the structure for future labs.

Add Query Profiles​

Query profiles let you define named bundles of default parameters. Create the query profiles directory:

mkdir -p ecommerce-app/search/query-profiles

Create ecommerce-app/search/query-profiles/default.xml:

<query-profile id="default">
<field name="hits">10</field>
<field name="ranking">default</field>
</query-profile>

Create ecommerce-app/search/query-profiles/mobile.xml:

<query-profile id="mobile">
<field name="hits">5</field>
<field name="timeout">2s</field>
</query-profile>

The default profile applies to all queries unless overridden. The mobile profile returns fewer results and has a shorter timeout, suitable for mobile clients. Note that hits sets the default result count for the profile - if you wanted to also enforce an upper bound that callers cannot exceed, you would add maxHits as well.

Add a Rank Profile​

Open ecommerce-app/schemas/product.sd and add a simple rank profile after the document-summary short block, before the closing } of the schema:

  rank-profile default {
first-phase {
expression: nativeRank(title, description)
}
}

This makes the default ranking explicit rather than relying on the implicit default. In Lab 4 you will add more rank profiles.

Review the Full Package​

Your application package should now look like this:

ecommerce-app/
├── schemas/
│ └── product.sd
├── search/
│ └── query-profiles/
│ ├── default.xml
│ └── mobile.xml
└── services.xml

Redeploy​

vespa deploy --wait 300 ecommerce-app

Vespa applies changes live. Your 20 products are still there, now served with the new query profiles and explicit rank profile.

Check Application Status​

After redeploying, it is a good idea to verify that everything came up healthy. Run the built-in status command:

vespa status

This checks that the container serving queries and feeds is up. To check the config server that receives your deployments instead, run vespa status deploy.

You can also call the health endpoints directly. The config server listens on port 19071 and the container on port 8080:

curl -s http://localhost:19071/state/v1/health
curl -s http://localhost:8080/state/v1/health

Both commands should report "code" : "up".

On Vespa Cloud, the config server isn't exposed, so skip the first command and check deployment status with vespa status deployment or in the console. The container health check can still be performed by using the application's endpoint with your data-plane certificates, as shown in the YQL chapter.

To see how many documents are in the system, you can query the metrics endpoint:

curl -s 'http://localhost:8080/metrics/v2/values' | python3 -m json.tool | grep -A2 'documents.active'

Or, if you prefer a simpler approach, just query for all documents with zero hits:

vespa query "select * from product where true" "hits=0"

The totalCount in the response tells you how many documents match. With hits=0 you skip the actual results and just get the count:

{
"root": {
"id": "toplevel",
"fields": { "totalCount": 20 },
"coverage": { "coverage": 100, "documents": 20, "full": true }
}
}

You should see 20, confirming that your products survived the redeployment.

Test the Query Profiles​

Run a query that matches all 20 products, without specifying a profile (uses the default profile):

vespa query "select * from product where true"

You get 10 hits, the hits value from the default profile. Now use the mobile profile:

vespa query "select * from product where true" "queryProfile=mobile"

This time you get only 5 hits. totalCount is still 20 in both responses: the profile changes how many hits are returned, not how many documents match.

You can still override any profile value in the request. Parameters you pass explicitly take precedence over the profile:

vespa query "select * from product where true" "queryProfile=mobile" "hits=8"

This returns 8 hits.

Test the Rank Profile​

vespa query "select * from product where default contains 'jacket'"

Three products should appear: the hiking jacket, the denim jacket, and the packable rain jacket. The nativeRank function scores them based on how well "jacket" matches the title and description fields.

Verify the Full Package​

Run this query as a checkpoint:

vespa query "select * from product where true" "queryProfile=mobile"

You should see exactly 5 hits returned (the mobile profile's hits setting), even though totalCount reports all 20 matches:

{
"root": {
"id": "toplevel",
"fields": { "totalCount": 20 },
"coverage": { "coverage": 100, "documents": 20, "full": true },
"children": [ ... 5 hits ... ]
}
}

Which 5 products you get may differ from run to run. With where true, every document gets the same relevance, so there is no meaningful order to pick the top 5 by.

What You Built​

Your application now has:

  • A properly structured application package with schemas, services, and query profiles
  • A default query profile with standard settings
  • A mobile query profile for resource-constrained clients
  • An explicit default rank profile

The package structure is ready for the rank profiles, embeddings, and models you will add in later labs.

Lab 3 application package with services.xml, schemas, and search/query-profiles directories

In the next lab, you will add multi-signal ranking with BM25, freshness, and business metrics, plus grouping queries for faceted navigation.