[go: up one dir, main page]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RetailApi Search Returning Empty fields #4613

Open
brendan-Wursta opened this issue Sep 5, 2023 · 3 comments
Open

RetailApi Search Returning Empty fields #4613

brendan-Wursta opened this issue Sep 5, 2023 · 3 comments
Assignees

Comments

@brendan-Wursta
Copy link
brendan-Wursta commented Sep 5, 2023

Hi All,

Currently we are using a local backend (soon porting to CloudRun), to call the retailApi and fetch results using the google-cloud/retail node implementation. It returns the ID of the search results, but empty fields for the description, etc etc.

Why aren't 'description', 'Title' and 'Brands' populated in the JSON response below, when they are filled out and imported correctly in Merchant Center / Retail UI?

Environment:

Node version: v18.17.1 "@google-cloud/retail": "^3.0.0"

Here's An example object in the array from client.search:

{
2023-09-05 13:18:16     personalLabels: [],
2023-09-05 13:18:16     matchingVariantFields: {},
2023-09-05 13:18:16     variantRollupValues: {},
2023-09-05 13:18:16     id: '50',
2023-09-05 13:18:16     product: {
2023-09-05 13:18:16       collectionMemberIds: [],
2023-09-05 13:18:16       categories: [],
2023-09-05 13:18:16       brands: [],
2023-09-05 13:18:16       tags: [],
2023-09-05 13:18:16       fulfillmentInfo: [],
2023-09-05 13:18:16       images: [],
2023-09-05 13:18:16       sizes: [],
2023-09-05 13:18:16       materials: [],
2023-09-05 13:18:16       patterns: [],
2023-09-05 13:18:16       conditions: [],
2023-09-05 13:18:16       variants: [],
2023-09-05 13:18:16       promotions: [],
2023-09-05 13:18:16       localInventories: [],
2023-09-05 13:18:16       attributes: {},
2023-09-05 13:18:16       name: 'projects/REDACTED/locations/global/catalogs/default_catalog/branches/1/products/50',
2023-09-05 13:18:16       id: '',
2023-09-05 13:18:16       type: 'TYPE_UNSPECIFIED',
2023-09-05 13:18:16       primaryProductId: '',
2023-09-05 13:18:16       gtin: '',
2023-09-05 13:18:16       title: '',
2023-09-05 13:18:16       description: '',
2023-09-05 13:18:16       languageCode: '',
2023-09-05 13:18:16       priceInfo: null,
2023-09-05 13:18:16       rating: null,
2023-09-05 13:18:16       availableTime: null,
2023-09-05 13:18:16       availability: 'AVAILABILITY_UNSPECIFIED',
2023-09-05 13:18:16       availableQuantity: null,
2023-09-05 13:18:16       uri: '',
2023-09-05 13:18:16       audience: null,
2023-09-05 13:18:16       colorInfo: null,
2023-09-05 13:18:16       retrievableFields: null,
2023-09-05 13:18:16       publishTime: null
2023-09-05 13:18:16     },
2023-09-05 13:18:16     matchingVariantCount: 0
2023-09-05 13:18:16   }

And code for implementation:

import { SearchServiceClient } from "@google-cloud/retail";

const projectId = process.env.PROJECT_ID;
const apiLocation = "global";
const defaultCatalogPath = `projects/${projectId}/locations/${apiLocation}/catalogs/default_catalog`;
const branchNumber = "1";

// Creates a client
const client = new SearchServiceClient({
  projectId: projectId,
});

export default async function searchRetailApi(
  query: string,
  visitorId: string,
) {
  console.log(`QUERY VARIABLE TYPE: `, typeof query);
  console.log(`VISITORID VARIABLE TYPE: `, typeof visitorId);
  console.log(`Query String: `, query);
  console.log(`VisitorId String: `, visitorId);

  const requestBody = {
    placement: `${defaultCatalogPath}/placements/default_search`,
    query: JSON.stringify(query),
    visitorId: JSON.stringify(visitorId),
    branch: `${defaultCatalogPath}/branches/${branchNumber}`,
  };

  const searchResult = await client.search(requestBody);
  const singleResult = searchResult[0][0];
  console.log(`High Level Keys: `, Object.keys(searchResult));
  console.log(`Product Keys: `, searchResult[0]);
  console.log(`Single Result: `, singleResult);

  return searchResult;
}

Below is examples of the data and from searching this branch in Retail UI in GCP:

Screenshot 2023-09-05 at 2 47 52 PM

Screenshot 2023-09-05 at 2 49 25 PM

@brendan-Wursta
Copy link
Author
brendan-Wursta commented Sep 6, 2023

After further investigation, it looks like .search() method only returns name and ID on purpose. Can confirm that both the node client library and a fetch using https://retail.googleapis.com/v2/projects/myProject/locations/global/catalogs/default_catalog/servingConfigs/default_search:search endpoint returns only the ID and the name.

In the code shown below, I use name from the .search() as a parameter in the .getProduct() method to get the full product data. That means I have to call retailAPI for each result?

Question 1:

Even with pagination returning results of ~50 items per page, we are going to hit quota limits with customers very quickly since our retailAPI limits are set at 1000/min. 1000/50 = only 20 people can search a site per minute?? This seems extremely inefficient for creating display pages for product search results.

async function fetchProductInfo(objArray: any) {
  const productPromises = objArray.map(async (result: any) => {
    const productInfo = await productClient.getProduct({
      name: result.product?.name,
    });
    return productInfo;
  });

  const allProducts = await Promise.all(productPromises);

  return allProducts;
}

  const searchResult = await client.search(requestBody);
  const resultArray = searchResult[0];
  const productInfo = await fetchProductInfo(resultArray);

Question 2:

After also analyzing the search results from Retail/Evaluate/Search UI page in GCP, only one network call is made. It gives the following results:

product: 
{
  name: "projects/redacted/locations/global/catalogs/default_catalog/branches/1/products/12",…}
  description:  "Polished Pearl Button-Up Shirt"
  images: [{uri: "https://myimage.jpeg"}]
  name: "projects/myproject/locations/global/catalogs/default_catalog/branches/1/products/12"
  title: "Polished Pearl Button-Up Shirt"
  uri: "https://mywebsite.com"
}

Replicating the payload below (seen from the retail/Evaluate/Search UI GCP page) in a fetch locally to the retailAPI endpoint, still only the name and ID are returned instead of the object shown above. Why?
(I thought readMask would help return results directly but it doesn't do anything for a local fetch)

branch: "projects/myproject/locations/global/catalogs/default_catalog/branches/1"
dynamicFacetSpec: {mode: "DISABLED"}
facetSpecs: []
filter: ""
offset: 0
pageSize: 10
query: "Adidas"
readMask: "name,title,description,uri,images"
useMostRecentServingConfig:true
visitorId: "0"

@brendan-Wursta
Copy link
Author

Any update on this?

TLDR from above: why doesn't await client.search(); return the product information that can be used for UI to display search results?

Instead it seems retailAPI needs to be called for each product using productClient.getProduct, which produces ~20-50 API calls for each search... just to display results for browsing. We will hit caps very quickly, and it will likely not be cost effective to use retailAPI without fields like title, brand, price, description being returned by client.search()

@BuiltByJames
Copy link

Hey @brendan-Wursta - not sure if you got anywhere with this but might be able to help, have you set the fields to retrievable under Controls > Attribute Controls? https://console.cloud.google.com/ai/retail/catalogs/default_catalog/controls/site
(There is then a 12 hour delay or purge and re-import the catalog)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants