oikonomiyaki
oikonomiyaki

Reputation: 7951

Firebase NodeJS SDK: Query by value on nested object

I have a collection/table that is structured like this:

{
  UsedPromos: {
    "userid_1": {
      "product_1_1": "promo_1_1",
      "product_1_2": "promo_1_2",
      ...
      "product_1_n": "promo_1_n",
    },
    "userid_2": {
      "product_2": "promo_2"
    },
    ...
    "userid_m": {
      ...
    }
  }
}

How can I query an exact match to some "promo_x_y"? I have tried this so far:

const admin = require("firebase-admin");
admin.initializeApp(...);
const ref = admin.database().ref("/UsedPromos");
ref.orderByValue()
  .equalTo("promo_x_y")
  .once("child_added")
  .then((snapshot) => {
    console.log(`{key: ${snapshot.key}, value: ${snapshot.val()}}`);
    return snapshot
  });

But it didn't return anything.

Upvotes: 0

Views: 73

Answers (1)

samthecodingman
samthecodingman

Reputation: 26171

If you are looking for all products that are part of the promotion promo_x_y, you need to adjust your query one level deeper than what you are currently doing.

Currently you are comparing the values of user_1, user_2, and so on to the value "promo_x_y". You get no results, because no entry /UsedPromos/user_1 = "promo_x_y" exists.

/UsedPromosByUser/{user}/{product} = {promo} (your current structure)

To fix this, you will need to search an individual user's list of products. Using the below snippet will log each product that has a value of "promo_x_y".

const admin = require("firebase-admin");
admin.initializeApp(/* ... */);

const ref = admin.database().ref("/UsedPromos");
const userToSearch = "user_1";

ref.child(userToSearch)
  .orderByValue()
  .equalTo("promo_x_y")
  .once("child_added")
  .then((snapshot) => {
    // snapshot.val() will always be "promo_x_y", so don't bother logging it.
    console.log(`${snapshot.key} uses "promo_x_y"`);
    return snapshot;
  });

Depending on your use case, it may be better to use a "value" event instead.

const admin = require("firebase-admin");
admin.initializeApp(/* ... */);

const ref = admin.database().ref("/UsedPromos");
const userToSearch = "user_1";

ref.child(userToSearch)
  .orderByValue()
  .equalTo("promo_x_y")
  .once("value")
  .then((querySnapshot) => {
    const productKeys = [];
    // productSnapshot.val() will always be "promo_x_y", so don't bother logging it.
    querySnapshot.forEach((productSnapshot) => productKeys.push(productSnapshot.key));
    
    console.log(`The following products have a value of "promo_x_y" for user "${userToSearch}": ${productKeys.join(", "}`);
    return productKeys;
  });

If you are looking to find all products, across all users, that use "promo_x_y", you should create an index in your database instead of using a query.

/UsedPromosByPromo/{promo}/{user}/{product} = true
OR
/UsedPromosByPromo/{promo}/{product}/{user} = true

Instead of using true in the above structure, you could store a timestamp (time of purchase, time promo expires, etc)

Upvotes: 2

Related Questions