I\'m currently creating an e-commerce app for iOS and I\'m having trouble deciding on how to structure my database for the scenario of a user searching for an item by \"keywords
How about:
items
item0
color: black
type: t-shirt
option: design
keywords: black_t-shirt_design
item1
color: black
type: hat
option: design
keywords: black_hat_design
then a simple query using startingAt and endingAt.
So for example, say the user is using a filter to narrow the results and wants to know about all of the black hats and doesn't care what the option parameter is:
itemsRef.queryOrdered(byChild: "keywords")
.queryStarting(atValue: "black_hat")
.queryEnding(atValue: "black_hat")
likewise if the user wanted to know about all of the clothing that's just black, you could query on the 'color' child for black.
But if they wanted all of the black hats with the design option
itemsRef.queryOrdered(byChild: "keywords")
.queryStarting(atValue: "black_hat_design")
.queryEnding(atValue: "black_hat_design")
In Firebase, you can query using a single value or a value range, so constructing a single query to obtain items matching several keywords is not going to be possible. However, you could arrange your database so that you could easily query or a single keyword. You'd then need to combine several queries, etc.
You could arrange your data like this:
{
"items": {
"item1_id": {
"brand_id": ...,
"itemName": ...,
"price": ...,
"keywords": {
"black": true,
"t-shirt": true
}
}
}
}
However, as explained in this answer, you would need to define an index for each keyword. To allow for efficient keyword-based queries, you could create your own mapping of keywords to items:
{
"items": {
"item1_id": {
"brand_id": ...,
"itemName": ...,
"price": ...,
"keywords": {
"black": true,
"t-shirt": true
}
}
},
"keywords": {
"black": {
"item1_id": true
},
"t-shirt": {
"item1_id": true
}
}
}
The query for a keyword would be something like this:
let keyword = "black";
database.ref(`keywords/${keyword}`).once("value", (snapshot) => {
snapshot.forEach((idSnapshot) => {
database.ref(`items/${idSnapshot.key}`).once("value", (itemSnapshot) => {
console.log(JSON.stringify(itemSnapshot.val()));
});
});
});
Having to maintain your own mapping of keywords to items is a little painful, but the queries will be fast. Also, Firebase's multi-location updates can make maintaining the keywords mapping a little easier. To create an item:
database.ref().update({
"items/item1_id": {
"brand_id": ...,
"itemName": ...,
"price": ...,
"keywords": {
"black": true,
"t-shirt": true
}
},
"keywords/black/item1_id": true,
"keywords/t-shirt/item1_id": true
});
To change an item's keywords (remove black
, add blue
and leave t-shirt
untouched):
database.ref().update({
"items/item1_id/keywords": {
"black": null,
"blue": true
},
"keywords/black/item1_id": null,
"keywords/blue/item1_id": true
});
And to delete an item:
database.ref().update({
"items/item1_id": null,
"keywords/blue/item1_id": null,
"keywords/t-shirt/item1_id": null
});