Search code examples
javascriptmongodbmongoosediscord.js

Mongo DB remove 1 item from an array


I have a buy system in place for my discord bot where it adds something to the inventory array in mongoDB:

data.Inventory[itemTobuy] ++;

It gets stored like this:

pizza: 1

And i have a use system where you can use items in your inventory. I would like to remove the whole thing if theres only 1 instance of this item but if there is more i would like to decrement the value by one. Say from pizza: 5 to pizza: 4. Althought im only 25% sure on how i would do such things

The full code for the command that adds the item if needed:

const { Client, Message, MessageEmbed } = require('discord.js');
const { User } = require("../../databasing/schemas/User")
const inventory = require('../../databasing/schemas/inventory')
const items = require('../../items/shopItems')

module.exports = {
    name: 'buy',
    /** 
     * @param {Client} client 
     * @param {Message} message 
     * @param {String[]} args 
     */
    run: async(client, message, args) => {
        userData = await User.findOne({ id: message.author.id }) || new User({ id: message.author.id })
        const itemTobuy = args[0].toLowerCase()
        embed = new MessageEmbed({ color: "#2F3136" })
        if(!args[0]) return message.channel.send({
            embeds: [ embed.setTitle('<:redCross:1004290018724020224> | Please mention something to buy!')]
        });
        const itemid = !!items.find((val) => val.item.toLowerCase() === itemTobuy);
        if(!itemid) return message.channel.send({
            embeds: [ embed.setTitle(`<:redCross:1004290018724020224> | ${itemTobuy} is not a valid item!`)]
        });
        itemPrice = items.find((val) => val.item.toLowerCase() === itemTobuy).price;
        userBalance = userData.wallet;
        if(userBalance < itemPrice) return message.channel.send({
            embeds: [embed.setTitle(`<:redCross:1004290018724020224> | You dont have enough money to buy this item(LOL)`)]
        }); 
        const param ={
            User: message.author.id
        }
        inventory.findOne(param, async(err, data) => {
            if(data){
                const hasItem = Object.keys(data.Inventory).includes(itemTobuy);
                if(!hasItem){
                    data.Inventory[itemTobuy] = 1;
                } else {
                    data.Inventory[itemTobuy] ++;
                }
                await inventory.findOneAndUpdate(param, data);
                } else {
                new inventory({
                    User: message.author.id,
                    Inventory: {
                        [itemTobuy]: 1,
                    }
                }).save()
                }
                message.channel.send({
                    embeds: [embed.setTitle(`<:greenTick:1004290019927785472> | Successfully bought ${itemTobuy}!`)]
                });
                userData.wallet -= itemPrice;
                userData.save()
        })
    }
}

Solution

  • You can use $inc method to decrease the value of your item. For example:

    const param = {
      User: message.author.id
    }
    
    const item_name = args.splice(0).join(" ").toLowerString();
    //Take note that .toLowerString() will transform any string to lowercase.
    //If your data is case sensitive, remove the .toLowerString().
    
    inventory.findOne({
       param, //User.id
       "Inventory.name": item_name //Change the "name" to something on how you call the name of your items.
    }, async(err, data) => {
       //Take note that we only identify if the item existing or not.
      if(data) {
        //Here's where you decrease the amount of the item.
        await inventory.findOneAndUpdate({
          param,
          "Inventory.name": item_name
        }, {
           $inc: {
             "Inventory.$.value": -1 //Change the value on how you call the value of your item
           }
        })
      } else {
        //Here is where you send a message that the item doesn't exist.
      }
    })
    

    EDIT:

    To get the name and the value of your item, you need to find the curtain name.

    First you need to create your inventory as array.

    const inv = await inventory.findOne({
      param
    })
    
    const arr = inv.Inventory;
    

    Your const arr = inv.Inventory; will make it to be an array. Then next step. Find the name

    const item_name = args.splice(0).join(" ").toLowerString();
    const inv = await inventory.findOne({
      param
    })
    
    const arr = inv.Inventory;
    const item = arr.find(x => x.name == item_name);
    

    After you get the item_name. You can now get the value and the name aswell and post it in somewhere you wanted. Can be embed or normal message.

    const item_name = args.splice(0).join(" ").toLowerString();
    const inv = await inventory.findOne({
      param
    })
    
    const arr = inv.Inventory;
    const item = arr.find(x => x.name == item_name);
    
    const items_value = item.value;
    const items_name = item.name;
    
    const embed = new MessageEmbed()
    .addFields(
      {name: `Name:`, value: `${items_name}`},
      {name: `Value:`, value: `${items_value}`}
    )
    
    message.channel.send({embeds: [embed]})