xerosa
xerosa

Reputation: 31

Finding results with mongodb

I have a db full of skills and I'm trying to make an info cmd where if you type the skill name you will get some info on it. Some skills are 2-3 arguments. For example "Animal Call". I want to be able to find my skill in mongodb by typing both !info animal call and for example something short like !info ani call

The problem about this is that if i try to find it with !info ani call it wont show up as a result.

Here's my code:

let skill = args.slice(0).join(" ");

const skillDB = await Skill.find({})

for (let i = 0; i < skillDB.length; i++) {
 if (skillDB[i].skillName.toLowerCase().includes(`${skill.toLowerCase()}`)) {
  const embed = new MessageEmbed()
  .setDescription(`\`${skillDB[i].skillIcon}\` ${skillDB[i].skillName}`)
  .setImage(skillDB[i].skillImg)
  return message.channel.send(embed)
 }
}

Upvotes: 0

Views: 35

Answers (1)

Zsolt Meszaros
Zsolt Meszaros

Reputation: 23141

I think you will need to use some kind of regex here. Instead of joining the arguments by a space, you could join them by .* and use that as a regex.

.* matches any characters, so when you join ['ani', 'call'], it will become a string like 'ani.*call'. As a regex pattern, /ani.*call/ matches any string that contains both ani and call (in this order).

const args = ['ani', 'call']
const regex = new RegExp(args.join('.*'), 'i')

console.log(`Words matching ${regex}:`)
console.log('animal', regex.test('animal'))
console.log('animal call', regex.test('animal call'))
console.log('anime caller', regex.test('anime caller'))
console.log('anicall', regex.test('anicall'))
console.log('animal cruelty', regex.test('animal cruelty'))
console.log('anemia caribou', regex.test('anemia caribou'))

If you don't want to match words without any additional characters between them, you can join the words with .+. Using the + sign requires at least one extra character between the two strings; this way it won't match anicall:

const args = ['ani', 'call']
const regex = new RegExp(args.join('.+'), 'i')

console.log(`Words matching ${regex}:`)
console.log('animal', regex.test('animal'))
console.log('animal call', regex.test('animal call'))
console.log('anime caller', regex.test('anime caller'))
console.log('anicall', regex.test('anicall'))
console.log('animal cruelty', regex.test('animal cruelty'))
console.log('anemia caribou', regex.test('anemia caribou'))

You could also use [a-z\s]+ to only accept characters in the range a-z and whitespace:

const args = ['ani', 'call']
const regex = new RegExp(args.join('[a-z\\s]+'), 'i')

console.log(`Words matching ${regex}:`)
console.log('animal', regex.test('animal'))
console.log('animal call', regex.test('animal call'))
console.log('anime caller', regex.test('anime caller'))
console.log('anicall', regex.test('anicall'))
console.log('animal cruelty', regex.test('animal cruelty'))
console.log('anemia caribou', regex.test('anemia caribou'))

With your current code, you could use the .test() method instead of .includes(). It searches for a match between a regex and a specified string.

You could also use a for ... of loop instead of the for loop to make it more readable:

const regex = new RegExp(args.join('[a-z\\s]+'), 'i');
const skills = await Skill.find({});
const embed = new MessageEmbed();

for (let skill of skills) {
  if (regex.test(skill.skillName)) {
    embed
      .setDescription(`\`${skill.skillIcon}\` ${skill.skillName}`)
      .setImage(skill.skillImg);

    return message.channel.send(embed);
  }
}

I'm not sure why you're fetching every skill from the database though. You could also use find() or findOne() with the regex above to only fetch the data you need:

const regex = new RegExp(args.join('[a-z\\s]+'), 'i');
// findOne returns a single object or null if not found
const skill = await Skill.findOne({
  skillName: { $regex: regex },
});

if (!skill) {
  return message.channel.send(
    `Oops, I couldn't find any skill in the database for \`${args.join(' ')}\``,
  );
}

const embed = new MessageEmbed()
  .setDescription(`\`${skill.skillIcon}\` ${skill.skillName}`)
  .setImage(skill.skillImg);

return message.channel.send(embed);

enter image description here

Upvotes: 1

Related Questions