> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/sdgm/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/sdgm/_mcp/server.

# Try Something New

Recommend items with no recorded purchase by a user that the user may buy in the next 30 days. This is a product discovery ranking for each user. The predictive query ranks future purchases; a history filter makes the displayed candidates new within the available purchase history.

## Data and relationships

The example assumes a Kumo Engine `graph` and matching `snapshot` with these tables:

| Table          | Required columns                  | Purpose                            |
| -------------- | --------------------------------- | ---------------------------------- |
| `users`        | `user_id` as the primary key      | Defines the users to score.        |
| `items`        | `item_id` as the primary key      | Defines the product candidates.    |
| `transactions` | `user_id`, `item_id`, `timestamp` | Records purchases and their times. |

Connect `transactions.user_id` to `users.user_id` and `transactions.item_id` to `items.item_id`. Mark `transactions.timestamp` as the time column. Keep a `transactions` pandas DataFrame from the same source extract as the snapshot so the history filter uses purchases known at the prediction time.

## Rank purchases in the next 30 days

This Python query asks Kumo Engine to rank items each user may purchase during the next 30 days. `LIST_DISTINCT` makes repeat purchases of one item a single target item within a window. Its training labels can include both first and repeat purchases.

```python
from datetime import datetime
from kumo.engine import PredictiveQuery

query_text = (
    "PREDICT LIST_DISTINCT(transactions.item_id, 0, 30, days) "
    "RANK TOP 50 FOR EACH users.user_id"
)
query = PredictiveQuery(graph, query_text)
query.validate()
print(query.get_task_type())  # temporal_link_prediction

prediction_time = datetime(2024, 4, 30)  # Replace with your chosen UTC time.
prediction_table = query.generate_prediction_table(
    snapshot, prediction_time=prediction_time
)
ranked = trainer.predict(
    prediction_table, snapshot=snapshot, num_classes_to_return=50
).frame
```

Here `trainer` is a fitted model for this query. For a live run, choose a prediction time after the latest observed purchase. `ENTITY` identifies a user, `CLASS` identifies an item, and `SCORE` orders candidates for that user. A score is not a guaranteed purchase probability.

## Keep only items new to each user

Exclude user and item pairs with a purchase at or before `prediction_time`. This is Python postprocessing on `ranked`, not a constraint in the predictive query.

```python
history = transactions.loc[
    transactions["timestamp"] <= prediction_time, ["user_id", "item_id"]
].drop_duplicates()

new_candidates = (
    ranked.merge(
        history,
        left_on=["ENTITY", "CLASS"],
        right_on=["user_id", "item_id"],
        how="left",
        indicator=True,
    )
    .loc[lambda rows: rows["_merge"].eq("left_only"),
         ["ENTITY", "CLASS", "SCORE"]]
    .sort_values(["ENTITY", "SCORE"], ascending=[True, False])
)
```

Each remaining row is a product with no recorded purchase by that user as of the prediction time, ranked for a purchase in the next 30 days. Use the rows to order a discovery shelf or choose candidates for a message. Check product availability separately before showing an item. Because `RANK TOP 50` limits candidates before this filter runs, a user may have fewer than 50 new items or none. The query does not guarantee that a new item will be purchased.