This is the code that reproduces the bug (as custom apps for node/typescript).
import "@shopify/shopify-api/adapters/node";
import { shopifyApi, ApiVersion } from "@shopify/shopify-api";
import dotenv from "dotenv";
const METAOBJECT_DEFINITION_TYPE = "metaobject_for_test_query_by_display_name";
const METAOBJECT_FIELD_KEY = "the_name";
const METAOBJECT_TEST_NAMES = {
"kanji-a": "漢字えー",
"kanji-b": "漢字びい",
};
dotenv.config();
const SHOPIFY_STORE: string = process.env.SHOPIFY_STORE ?? "";
const SHOPIFY_SECRET_KEY: string = process.env.SHOPIFY_SECRET_KEY ?? "";
const SHOPIFY_ACCESS_TOKEN: string = process.env.SHOPIFY_ACCESS_TOKEN ?? "";
const shopify = shopifyApi({
apiSecretKey: SHOPIFY_SECRET_KEY,
apiVersion: ApiVersion.January25,
isCustomStoreApp: true,
adminApiAccessToken: SHOPIFY_ACCESS_TOKEN,
isEmbeddedApp: false,
hostName: SHOPIFY_STORE,
});
const customAppSession = shopify.session.customAppSession(SHOPIFY_STORE);
const graphQLClient = new shopify.clients.Graphql({ session: customAppSession });
/** Check if the metaobject definition exists */
async function checkMetaobjectDefinitionExists(): Promise<string | undefined> {
const response = await graphQLClient.request(
`query {
metaobjectDefinitionByType(type: "${METAOBJECT_DEFINITION_TYPE}") { id }
}`,
);
console.log(`Checking if metaobject definition ${METAOBJECT_DEFINITION_TYPE} exists: ` + JSON.stringify(response?.data?.metaobjectDefinitionByType, null, 2));
return response?.data?.metaobjectDefinitionByType?.id;
}
/** Create the metaobject definition */
async function createMetaobjectDefinition(): Promise<string | undefined> {
const response = await graphQLClient.request(
`mutation ($definition: MetaobjectDefinitionCreateInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition { id }
userErrors { field message code }
}
}`, {
variables: {
"definition": {
"type": METAOBJECT_DEFINITION_TYPE,
"displayNameKey": METAOBJECT_FIELD_KEY,
"fieldDefinitions": [
{
"key": METAOBJECT_FIELD_KEY,
"type": "single_line_text_field",
}
]
}
}
}
);
console.log(`Creating metaobject definition ${METAOBJECT_DEFINITION_TYPE}: ` + JSON.stringify(response?.data?.metaobjectDefinitionCreate, null, 2));
return response?.data?.metaobjectDefinitionCreate?.metaobjectDefinition?.id;
}
/** Check if a metaobject entry exists */
async function checkMetaobjectExists(handle: string, name: string): Promise<string | undefined> {
const response = await graphQLClient.request(
`query ($handle: MetaobjectHandleInput!) {
metaobjectByHandle(handle: $handle) { id }
}`, {
variables: {
"handle": {
"handle": handle,
"type": METAOBJECT_DEFINITION_TYPE,
}
}
}
);
console.log(`Checking if metaobject ${handle}, ${name} exists: ` + JSON.stringify(response?.data?.metaobjectByHandle, null, 2));
return response?.data?.metaobjectByHandle?.id;
}
/** Create a metaobject entry */
async function createMetaobject(handle: string, name: string): Promise<string | undefined> {
const response = await graphQLClient.request(
`mutation ($metaobject: MetaobjectCreateInput!) {
metaobjectCreate(metaobject: $metaobject) {
metaobject { id }
userErrors { field message code }
}
}`, {
variables: {
"metaobject": {
"handle": handle,
"type": METAOBJECT_DEFINITION_TYPE,
"fields": [{
key: METAOBJECT_FIELD_KEY, value: name,
}]
}
}
}
);
console.log(`Creating metaobject ${handle}, ${name}: ` + JSON.stringify(response?.data?.metaobjectCreate, null, 2));
return response?.data?.metaobjectCreate?.metaobject?.id;
}
/** Fetch metaobject entries by display_name */
async function searchMetaobjectsByDisplayName(displayName: string): Promise<string[]> {
const response = await graphQLClient.request(
`query {
metaobjects(type:"${METAOBJECT_DEFINITION_TYPE}", first:5, query:"display_name:'${displayName}'") {
nodes {
id
theField: field(key:"${METAOBJECT_FIELD_KEY}"){ value }
}
}
}`
);
console.log(`Searching metaobjects by display_name ${displayName}: ` + JSON.stringify(response?.data?.metaobjects, null, 2));
return response?.data?.metaobjects?.nodes.map((node: any) => {
return node.theField.value;
});
}
async function test() {
// Create metaobject definition if it does not exist.
let metaobjectDefinitionId = await checkMetaobjectDefinitionExists();
if (!metaobjectDefinitionId) {
metaobjectDefinitionId = await createMetaobjectDefinition();
if (!metaobjectDefinitionId) {
console.log("Failed to create metaobject definition.");
return;
}
}
// Create metaobject entries.
let objectCreated = false;
for (const [handle, name] of Object.entries(METAOBJECT_TEST_NAMES)) {
let metaobjectId = await checkMetaobjectExists(handle, name);
if (!metaobjectId) {
metaobjectId = await createMetaobject(handle, name);
if (!metaobjectId) {
console.log("Failed to create metaobject.");
return;
}
objectCreated = true;
}
}
// Sleep for 3 seconds to wait for the metaobject index to be updated.
if (objectCreated) {
console.log("Sleep 3 seconds to wait for the metaobject index to be updated.");
await new Promise((resolve) => setTimeout(resolve, 3000));
}
// Test if each search result by display_name is identical.
console.log("==== Tests ====");
for (const [handle, name] of Object.entries(METAOBJECT_TEST_NAMES)) {
const result = await searchMetaobjectsByDisplayName(name);
if (result.length !== 1) {
console.log(`[NG] Searching display_name "${name}" results in ${result.length} entries: ${result.join(", ")}`);
continue;
}
if (result[0] !== name) {
console.log(`[NG] Searching display_name "${name}" results in a wrong entry: ${result.join(", ")}`);
continue;
}
console.log(`[OK] Searching display_name "${name}" returns the correct result.`);
}
}
test();
Here is the result (of the problematic part).
Searching metaobjects by display_name 漢字えー: {
"nodes": [
{
"id": "gid://shopify/Metaobject/95075893544",
"theField": {
"value": "漢字えー"
}
},
{
"id": "gid://shopify/Metaobject/95075926312",
"theField": {
"value": "漢字びい"
}
}
]
}
[NG] Searching display_name "漢字えー" results in 2 entries: 漢字えー, 漢字びい