It is basically what the title says.
Input: myArray
= an array of words
I have an model that have field
wordsCollection
, which is an array field.
How can I find all documents of that model whose wordsCollections
has at least n elements of myArray
Let say we have the following documents in our collection:
{ "_id" : ObjectId("5759658e654456bf4a014d01"), "a" : [ 1, 3, 9, 2, 9, 0 ] }
{ "_id" : ObjectId("5759658e654456bf4a014d02"), "a" : [ 0, 8, 1 ] }
{ "_id" : ObjectId("5759658e654456bf4a014d03"), "a" : [ 0, 8, 432, 9, 34, -3 ] }
{ "_id" : ObjectId("5759658e654456bf4a014d04"), "a" : [ 0, 0, 4, 3, 2, 7 ] }
and the following input array and n = 2
var inputArray = [1, 3, 0];
We can return those documents where the array field contains at least n elements of a given array using the aggregation framework.
The $match
selects only those documents with the array's length greater or equals to n
. This reduce the amount of data to be processed in down in the pipeline.
The $redact
pipeline operator use a logical condition processing using the $cond
operator and the special operations $$KEEP
to "keep" the document where the logical condition is true or $$PRUNE
to "discard" the document where the condition is false.
In our case, the condition is $gte
which returns true if the $size
of the intersection of the two arrays, which we compute using the $setIntersection
operator is greater than or equal 2
.
db.collection.aggregate(
[
{ "$match": { "a.1": { "$exists": true } } },
{ "$redact": {
"$cond": [
{ "$gte": [
{ "$size": { "$setIntersection": [ "$a", inputArray ] } },
2
]},
"$$KEEP",
"$$PRUNE"
]
}}
]
)
which produces:
{ "_id" : ObjectId("5759658e654456bf4a014d01"), "a" : [ 1, 3, 9, 2, 9, 0 ] }
{ "_id" : ObjectId("5759658e654456bf4a014d02"), "a" : [ 0, 8, 1 ] }
{ "_id" : ObjectId("5759658e654456bf4a014d04"), "a" : [ 0, 0, 4, 3, 2, 7 ] }
Use aggregation.
In $match aggregation pipeline, you can use $size and $gte
You don't need no framework here it's quite simple with JS. I had to create a test case so please forgive the crowd. We invent a new Array method. Array.prototype.intersect()
. It will give us the common items of two arrays. So if we have two text paragraphs we can insert one of each word (no duplicates) into two arrays and then take an intersection of them. Let's see...
Array.prototype.intersect = function(a) {
return this.filter(e => a.includes(e));
};
var text = "Last week we installed a kitty door so that our cat could come and go as she pleases. Unfortunately, we ran into a problem. Our cat was afraid to use the kitty door. We tried pushing her through, and that caused her to be even more afraid. The kitty door was dark, and she couldn’t see what was on the other side. The first step we took in solving this problem was taping the kitty door open. After a couple of days, she was confidently coming and going through the open door. However, when we removed the tape and closed the door, once again, she would not go through. They say you catch more bees with honey, so we decided to use food as bait. We would sit next to the kitty door with a can of wet food and click the top of the can. When kitty came through the closed door, we would open the can and feed her. It took five days of doing this to make her unafraid of using the kitty door. Now we have just one last problem; our kitty controls our lives!",
given = "People often install a kitty door, only to discover that they have a problem. The problem is their cat will not use the kitty door. There are several common reasons why cats won’t use kitty doors. First, they may not understand how a kitty door works. They may not understand that it is a little doorway just for them. Second, many kitty doors are dark and cats cannot see to the other side. As such, they can’t be sure of what is on the other side of the door, so they won’t take the risk. One last reason cats won’t use kitty doors is because some cats don’t like the feeling of pushing through and then having the door drag across their back. But don’t worry—there are solutions to this problem.\nThe first step in solving the problem is to prop the door open with tape. This means your cat will now be able to see through to the other side; your cat will likely begin using the kitty door immediately. Once your cat has gotten used to using the kitty door, remove the tape. Sometimes cats will continue to use the kitty door without any more prompting. If this does not happen, you will want to use food to bribe your cat. When it’s feeding time, sit on the opposite side of the door from your cat and either click the top of the can or crinkle the cat food bag. Open the door to show your cat that it is both you and the food waiting on the other side of the door. Repeat this a couple times, and then feed your cat. After a couple days of this, your kitty door problem will be gone.",
// get one of each word of "text" lowercased and insert into textar
textar = Array.from(new Set(text.match(/\b\w+\b/g).map(e => e.toLowerCase()))),
// get one of each word of "given" lowercased and insert into textar
givenar = Array.from(new Set(given.match(/\b\w+\b/g).map(e => e.toLowerCase()))),
shared = givenar.intersect(textar);
console.log(JSON.stringify(shared));
console.log(shared.length) // this is your result to decide upon.