0

I have a text index on a array field, this is how the field looks like:

{"client":["Aaaa","Bbbb"]},
{"client":["Ccc","Dddd"]},
{"client":["Ccc","Dddd"]},
{"client":["Aaaa","Bbbb"]},
{"client":["Ccc","Dddd"]},
{"client":["Aaaa","Bbbb"]},
{"client":["Ccc","Dddd"]},

And I need to get all records that doesn't have Dddd only by searching the value I want to exclude and not adding what I want to include Aaaa and -Bbbb

I read here, mongoDB Docs: $text, that I can exclude some results by adding a dash before the word I want to exclude, but I don't know how to exclude only.

db.clients.find({$text:{$search: 'Aaaa -Dddd'}})

Works but I need to do something like:

db.clients.find({$text:{$search: '-Dddd'}})

I want to be able to exclude a client from search without adding every other client I want to keep :)

So the output of the query should be all the documents where the client is not {"client":["Ccc","Dddd"]} The array is the client name, location and department, so for a real example here is what a client looks like:

"client":["Microsoft","Washington","Data Analytics"],
"client":["Microsoft","Washington","Hardware Devices Research Group"],
"client":["Dell","Washington","Interactive 3D", "Technologies Research Group (I3D)"],
"client":["MSI","Washington","Interactive 3D", "Media Research Group"],

I need to be able to take an input (case insensitive) and exclude all docs that have that client, so for example i want all docs except the ones that have one of the array elements equal to Interactive 3D (case insensitive) with a query like this:

db.clients.find({$text:{$search: '-interactive 3d'}})

The result would be all documents that have this clients only

"client":["Microsoft","Washington","Data Analytics"],
"client":["Microsoft","Washington","Hardware Devices Research Group"],

And also to be able to exclude all docs that match multiple elements eq: microsoft washington data analytics and the result would be all documents that have this clients only:

"client":["Microsoft","Washington","Hardware Devices Research Group"],
"client":["Dell","Washington","Interactive 3D", "Technologies Research Group (I3D)"],
"client":["MSI","Washington","Interactive 3D", "Media Research Group"],

But I don't get any results...

I've had some success using this:

const nin = searchValue.split(' ');
db.clients.find({"client":{"$nin": nin} }) 

But it's not case insensitive

Gabriel
  • 670
  • 1
  • 8
  • 24

2 Answers2

1

As you have array of simple strings, You can simply do:

db.clients.find({client: {$ne: 'Dddd'}}).then(result => {
//do something with result
...
})
.catch(err => {
//do something with err
...
});

To find with multiple keys and making case insensitive queries, you can do something like:

db.clients.find({client: {$nin: [/^Dddd$/i, /^Eeee$/i]}}).then(result => {
//do something with result
...
})
.catch(err => {
//do something with err
...
});

Hope this helps :)

Mohammed Amir Ansari
  • 2,311
  • 2
  • 12
  • 26
  • This was my first attempt, but it have some drawbacks, it's not case insensitive and i can't search for multiple elements, If it can't be done with text index i will have to create another field that contains concatenated string of the array and work with regex... but maybe someone solved it somehow without the need of another field, also, regex is slower than text index solution.. – Gabriel Aug 17 '19 at 09:10
  • To achieve this, you can use the combination of **$nin** and **regex**. I've updated the answer, you can check there :) – Mohammed Amir Ansari Aug 17 '19 at 14:26
0

I ended up using $nin with regex, didn't know it was possible but thanks to this post Case Insensitive search with $in i managed to solve it.

const v = searchValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const nin = v.split(' ');
var searchRegexp = [];
nin.forEach(function(s) {
  if (s) searchRegexp.push(new RegExp(s, 'i'));
});
db.clients.find({"client":{"$nin": searchRegexp} }) 

This way I can partial search too. I only hope it's not too slow (didn't test yet)... If anyone have a better solution please tell me..

Thanks

Gabriel
  • 670
  • 1
  • 8
  • 24