I'm trying to create a simple search algorithm that will try to match against a first name, last name, and/or set of tags, as an example:
[
{
"key": 1,
"fname": "Bob",
"lname": "Smith",
"tags": [
"a",
"b",
"c"
]
},
{
"key": 2,
"fname": "John",
"lname": "Jacob",
"tags": [
"c",
"d",
"e"
]
},
{
"key": 3,
"fname": "Will",
"lname": "Smith",
"tags": [
"a",
"b",
"c"
]
}
]
This works with the following, but I can only get the tags count. Basically what I'm going for here is to match first-name, last-name, or tags and for each match store a "point":
db.collection.aggregate([
{
$match: {
$or: [
{
"fname": "Will"
},
{
"lname": "Smith"
},
{
tags: {
$in: [
"b",
"c"
]
}
}
]
}
},
{
$project: {
tagsMatchCount: {
$size: {
"$setIntersection": [
[
"b",
"c"
],
"$tags"
]
}
}
}
},
{
"$sort": {
tagsMatchCount: -1
}
}
])
Here's the sandbox I'm playing with: https://mongoplayground.net/p/DFJQZY-dfb5
CodePudding user response:
Query
- create a document to hold the matches each in separate field
- add one extra field total
- keep only those with at least 1 match
- you can sort also after by any of the 3 types of matches or by total, like
{"$sort":{"points.total":-1}} - if you have index that can be used, remove my
$matchand add your match as first stage like in your example
aggregate(
[{"$set":
{"points":
{"fname":{"$cond":[{"$eq":["$fname", "Will"]}, 1, 0]},
"lname":{"$cond":[{"$eq":["$lname", "Smith"]}, 1, 0]},
"tags":{"$size":{"$setIntersection":["$tags", ["b", "c"]]}}}}},
{"$set":
{"points.total":
{"$add":["$points.fname", "$points.lname", "$points.tags"]}}},
{"$match":{"$expr":{"$gt":["$points.total", 0]}}}])
