I have collection of user, and this is the following of documents :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 }
I want to show the list of users with some of criterias and ordering with some conditons, online users and most liked in the top of the list,after online user list is show offline users with most scored & most liked. The following of rules :
If online is 1 must be sort by descending of like.
If online is 0 and score is 1 must be sort by descending of score.
If online is 0 and score is 0 must be sort by descending of like.
So, the result can be like :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 }
I have finished until point 2, my query following :
db.users.aggregate([
{
$project :
{
"id" : 1,
"name" : 1,
"online: 1,
"like" : 1,
"score" : 1,
"sort" : {
$cond:
{
"if" :
{
$eq : ["$online", true]
},
"then" : "$like",
"else" : "$score"
}
}
}
},
{
$sort :
{
"online" : -1,
"sort" : -1,
"id" : 1
}
},
{
$skip : 0
},
{
$limit : 9
}
])
But I have the current result following :
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 1 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 7, "name": "G", "online": 0, "like": 5, "score": 0 },
{ "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 }
{ "_id": 9, "name": "I", "online": 0, "like": 6, "score": 0 },
You can see, based on point 3, instance { "_id": 8, "name": "H", "online": 0, "like": 13, "score": 0 } should be on top with score is 0
First create additional column call point with value (1 - online)*score
After this sort data by:
online desc
point desc (online = 1 pointbe always 0, online is 0 point is score)
like desc
You can use this query
db.yourtable.aggregate(
[
{ $project:{
"id" : 1,
"name" : 1,
"online": 1,
"like" : 1,
"score" : 1,
point: { $multiply: [
{$subtract: [1,"$online"]}
, "$score"
]}
}
}
,{ $sort : { online: -1, point : -1, like : -1 } }
]
);
Please check below query :
db.getCollection('yourTable').aggregate([
{
$project :
{
"id" : 1,
"name" : 1,
"online": 1,
"like" : 1,
"score" : 1,
onlineSortLike: {
$cond: {
if: { $and: [{ $eq: ['$online',1 ] }] },
then: '$like',
else: 0,
},
},
sortOfflineScore: {
$cond: {
if: { $and: [{ $eq: ['$online',0] }] },
then: '$score',
else: 0,
},
},
sortOfflineScoreLike: {
$cond: {
if: { $and: [{ $eq: ['$online', 0] }] },
then: '$like',
else: 0,
},
},
}
},
{
$sort :
{
"online" : -1,
"onlineSortLike" : -1,
"sortOfflineScore" : -1,
"sortOfflineScoreLike" : -1
}
},
{
$skip : 0
},
{
$limit : 9
}
])
Related
I need the find matching pairs in a single collection based on the values of fields and update them.
Let's say I have the following collection:
[{ "_id": "a", "client": 1, "tag": "", "debit": 12, "credit": -20 },
{ "_id": "b", "client": 1, "tag": "G", "debit": -12, "credit": 20 } ,
{ "_id": "c", "client": 1, "tag": "G", "debit": 12, "credit": 20 },
{ "_id": "d", "client": 2, "tag": "", "debit": 13, "credit": 0 },
{ "_id": "e", "client": 2, "tag": "G", "debit": -13, "credit": 0 },
{ "_id": "f", "client": 3, "tag": "", "debit": 13, "credit": 0 },
{ "_id": "g", "client": 3, "tag": "G", "debit": 14, "credit": 0 },
{ "_id": "h", "client": 4, "tag": "", "debit": 0, "credit": 0 }]
Now for two documents to build a pair they have to:
have the same client
one has the "" tag, one has the "G" tag
one has to have the inverse value of debit OR credit
bonus: ignore 0 if possible (since -0 == 0, I could also just remove the field)
bonus: if multiple pairs with "G" are found only take one of the two
So the example collection would result in the following pairs:
("a", "b") or ("a", "c"):
a.client == b.client && a.tag == "" && b.tag == "G" && a.debit == -b.debit
for "c": a.credit == -c.credit
("d", "e"):
normal pair: d.client == e.client && d.tag == "" && e.tag == "G" && d.debit == -e.debit
("f", "g") DON'T match, because 0 doesn't count
"h" has no match
The result could look something like this:
[{ "pair": ["a", "b"] }, { "pair": ["d", "e"] }]
The goal is to update all documents who appear in any pair, so this could also just be a list of ids.
Final goal:
[{ "_id": "a", "client": 1, "tag": "", "debit": 12, "credit": -20, "s": true },
{ "_id": "b", "client": 1, "tag": "G", "debit": -12, "credit": 20, "s": true } ,
{ "_id": "c", "client": 1, "tag": "G", "debit": 12, "credit": 20 },
{ "_id": "d", "client": 2, "tag": "", "debit": 13, "credit": 0, "s": true },
{ "_id": "e", "client": 2, "tag": "G", "debit": -13, "credit": 0, "s": true },
{ "_id": "f", "client": 3, "tag": "", "debit": 13, "credit": 0 },
{ "_id": "g", "client": 3, "tag": "G", "debit": 14, "credit": 0 },
{ "_id": "h", "client": 4, "tag": "", "debit": 0, "credit": 0 }]
I know I can group by client but how do I group by non matching fields?
And how can I compare the values of two documents, since I have to compare each documents with all others..
In SQL I solved it with this statement, maybe this helps or makes it clearer (I know it doesn't filter duplicates, the one with duplicates is too long to get the point across):
SELECT d1.id, d2.id
FROM documents d1, documents d2
WHERE d1.client = d2.client
AND d1.tag = "" AND d2.tag = "G"
AND (d1.debit = -d2.debit OR d1.credit = -d2.credit)
Thanks :)
I hope you get a more succinct answer, but here's one way to update (using "$merge") the documents in the collection using your logic to identify them as pairs.
db.documents.aggregate([
{
"$match": {
"tag": ""
}
},
{
"$set": {
"docStore": "$$ROOT"
}
},
{
"$lookup": {
"from": "documents",
"localField": "client",
"foreignField": "client",
"let": {
"credit": "$credit",
"debit": "$debit",
"tag": "$tag"
},
"pipeline": [
{
"$match": {
"$expr": {
"$and": [
{"$eq": ["$tag", "G"]},
{
"$or": [
{
"$and": [
{"$ne": ["$$credit", 0]},
{"$eq": [{"$add": ["$$credit", "$credit"]}, 0]}
]
},
{
"$and": [
{"$ne": ["$$debit", 0]},
{"$eq": [{"$add": ["$$debit", "$debit"]}, 0]}
]
}
]
}
]
}
}
}
],
"as": "pairs"
}
},
{
"$match": {
"$expr": {
"$gt": [{"$size": "$pairs"}, 0]
}
}
},
{
"$set": {
"pair": [
"$docStore",
{"$first": "$pairs"}
]
}
},
{
"$project": {
"_id": 0,
"pair": 1
}
},
{"$unwind": "$pair"},
{
"$replaceWith": {
"$mergeObjects": [
"$pair",
{"s": true}
]
}
},
{"$merge": "documents"}
])
Try it on mongoplayground.net.
I am trying to insert data in the last element of an array in MongoDB. insert in the first element works using the 0 operator, but I'm confused about how to insert in the last element. To explain it, consider the following data:
[
{
"_id": "1",
"company": "turivius",
"forms": [
{
"current_version": 1,
"history": [
{
"content": [],
"layout": [],
"responses": [
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 1",
},
{
"field_id": 1,
"value": "aaa",
},
],
},
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 2",
},
{
"field_id": 1,
"value": "bbb",
},
],
},
],
"version": 0,
},
{
"content": [],
"layout": [],
"responses": [
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"primeiro user": "primeiro form, resposta 1 v1",
},
{
"field_id": 1,
"value": "aaa",
},
],
}
],
"version": 1,
},
],
"id": "33b66684-24a9-4a45-a12f-27a330152ac8",
"is_active": True,
},
{
"current_version": 0,
"history": [],
"id": "fa2eb9a1-c7c4-4b64-b682-7f51658bc4ab",
"is_active": False,
},
],
"user_id": "1",
},
{
"_id": "2",
"company": "turivius",
"forms": [
{
"current_version": 0,
"history": [],
"id": "565656",
"is_active": True,
},
],
"user_id": "9999",
},
]
I want to insert data in the document with user_id = 1, form.id = 33b66684-24a9-4a45-a12f-27a330152ac8 and in the history array where the version = 1 I want to insert a new response:
{
"client_id": 100,
"response_date": datetime(2022, 5, 17, 3, 0),
"values": [
{
"field_id": 0,
"test": "test",
},
{
"field_id": 1,
"test2": "test3",
},
],
}
The history with version 1 is the last element, if I wanted insert in the first element the following code would works:
find_one_and_update(
{"$and": [{"user_id": "1"}, {"forms.id": '33b66684-24a9-4a45-a12f-27a330152ac8'}]},
{
"$push": {"forms.$.history.0.responses": data_here},
},
return_document=ReturnDocument.AFTER,
)
Is there anyway to do it that way or I need a more complex query to do it?
You want to add a new document to the responses array when those conditions are true:
{
user_id: '1',
'forms.id': '33b66684-24a9-4a45-a12f-27a330152ac8',
'forms.history.version': 1
}
forms is an array, so you want to update the array element that matches the condition, so you can use the $[identifier] operator. Inside the array of forms, you only want to update the history with a specific version, so you need to use the $[identifier] operator multiple times, the field you want to update would be like this:
forms.$[f].history.$[h].responses
f is the identifier of each elements in forms that matches the condition and h the elements of the history array that match the other condition. An arrayFilters object is used to define the conditions.
The complete query could be like this:
db.collection.update({
user_id: "1",
'forms.id': '33b66684-24a9-4a45-a12f-27a330152ac8',
'forms.history.version': 1
},
{
$push: {
'forms.$[f].history.$[h].responses': {
"client_id": 100,
"response_date": "datetime(2022, 5, 17, 3, 0)",
"values": [
{
"field_id": 0,
"test": "test",
},
{
"field_id": 1,
"test2": "test3",
},
],
}
}
},
{
arrayFilters: [
{
'h.version': 1
},
{
'f.id': '33b66684-24a9-4a45-a12f-27a330152ac8'
}
]
})
Working example at MongoDB playground
I am having issues to create a Jolt specification for the following input, I want to convert the Score to an array called Scores, what is happening is when I have only one score it is not send as array, so I want to convert to an array of scores.
[
{
"Page": 320301,
"ScoreInfo": {
"StadiumID": 126,
"Stadium": "abcded",
"Inn": 3,
"TB": 2,
"Team": [
{
"HV": 1,
"ID": 1,
"NameS": "abdcd",
"NameES": "abdcd",
"R": 1,
"PA": 10,
"H": 2,
"BB": 3,
"SB": 0,
"E": 0,
"Score": {
"Inn": 1,
"TB": 2,
"R": 0,
"H": 0,
"BB": 1
}
},
{
"HV": 2,
"ID": 3,
"NameS": "afewqe",
"NameES": "dadfa",
"R": 1,
"PA": 12,
"H": 3,
"BB": 0,
"SB": 0,
"E": 0,
"Score": [
{
"Inn": 1,
"TB": 1,
"R": 1,
"H": 2,
"BB": 0
},
{
"Inn": 2,
"TB": 1,
"R": 0,
"H": 0,
"BB": 0
}
]
}
],
"HV": null
}
}
]
The output expected is:
{
"ScoreInfo" : {
"Team" : [ {
"HV" : 1,
"ID" : 1,
"NameS" : "abdcd",
"NameES" : "abdcd",
"R" : 1,
"PA" : 10,
"H" : 2,
"BB" : 3,
"SB" : 0,
"E" : 0,
"Scores" : [ {
"Inn" : 1,
"TB" : 2,
"R" : 0,
"H" : 0,
"BB" : 1
} ]
}, {
"HV" : 2,
"ID" : 3,
"NameS" : "afewqe",
"NameES" : "dadfa",
"R" : 1,
"PA" : 12,
"H" : 3,
"BB" : 0,
"SB" : 0,
"E" : 0,
"Scores" : [ {
"Inn" : 1,
"TB" : 1,
"R" : 1,
"H" : 2,
"BB" : 0
}, {
"Inn" : 2,
"TB" : 1,
"R" : 0,
"H" : 0,
"BB" : 0
}]
} ]
},
"Type" : "ScoreInfo",
"Datasource" : "Vendor"
}
I have tried it so far, but it is not going anywhere:
[
{
"operation": "shift",
"spec": {
"*": {
"ScoreInfo": {
"Team": {
"*": {
"#": "ScoreInfo.&2",
"Score": {
"*": {
"#": "ScoreInfo.Team.Scores.[]"
}
}
}
}
}
}
}
},
{
"operation": "default",
"spec": {
"Datasource": "Vendor",
"Type": "ScoreInfo"
}
}
]
Thanks in advance,
The common key name(Score) leads to a confusion, so need to seperate the first(0) index of the Team array and the other members while evaluation, and use [] extension only for the first index at the end of the "Score": "&3.&2.[#2].&s[]" key-value pair
[
{
"operation": "shift",
"spec": {
"*": {
"ScoreInfo": {
"Team": {
"0": {
"*": "&3.&2.[#2].&",
"Score": "&3.&2.[#2].&s[]"
},
"*": {
"*": "&3.&2.[#2].&",
"Score": "&3.&2.[#2].&s"
}
}
}
}
}
},
{
"operation": "default",
"spec": {
"Datasource": "Vendor",
"Type": "ScoreInfo"
}
}
]
where no need to explicitly write the name of the keys, you can rather use ampersand placeholders along with integer suffixes(such as &2,&3..) in order to grab the related value from the outer levels, or just a single ampersand(&) to grab the current value of the key.
I have a rating model for my posts and the rating value for them. I would like to receive all ratings (ratings vary from 1 to 5) for each post in the database. I would also like to receive a percentage
https://mongoplayground.net/p/tDvZ8qLQ1m8
My scheme just looks like:
{
"_id": 1,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"author": ObjectId("5eefd305d9020c52fd81f80d"),
"rating": 1,
},
{
"_id": 2,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 1,
},
{
"_id": 3,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 1,
},
{
"_id": 4,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 5,
},
{
"_id": 5,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 3,
},
{
"_id": 6,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 2,
},
{
"_id": 7,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 2,
},
{
"_id": 8,
"post": ObjectId("5ee40fbc1617af52edf81682"),
"rating": 2,
}
]
My Code:
{
$match: {
post: ObjectId("5ee40fbc1617af52edf81682")
}
},
{
"$group": {
"_id": {
"post": "$name",
"rating": "$rating"
},
"count": {
"$sum": 1
},
}
},
{
"$group": {
"_id": "$_id.post",
"counts": {
"$push": {
"rating": "$_id.rating",
"result": "$count",
"percent": "percent",
}
}
}
}
])
My Result:
{
"_id": null,
"counts": [
{
"percent": "percent",
"rating": 2,
"result": 3
},
{
"percent": "percent",
"rating": 3,
"result": 1
},
{
"percent": "percent",
"rating": 5,
"result": 1
},
{
"percent": "percent",
"rating": 1,
"result": 3
}
]
}
]
My preferred result
{
"_id": null,
"ratingAll": 8,
"ratingAvg": [[rating-average]],
"counts": [
{
"rating": 1,
"result": 3,
"percent": [[percentValue]],
},
{
"rating": 2,
"result": 3,
"percent": [[percentValue]],
},
{
"rating": 3,
"result": 1,
"percent": [[percentValue]],
},
{
"rating": 4,
"result": 0,
"percent": [[percentValue]],
},
{
"rating": 5,
"result": 1,
"percent": [[percentValue]],
}
]
}
]
I need make a query with indice at the mongodb, I will show below a minimal case example of my real case.
I have following collection with flowing data:
devsrv(mongod-3.0.4) test> db.teste.find()
{
"_id": ObjectId("57b324c341aaa4b930ef3b92"),
"a": 1,
"b": 1
}
{
"_id": ObjectId("57b324c941aaa4b930ef3b93"),
"a": 1,
"b": 2
}
{
"_id": ObjectId("57b324cd41aaa4b930ef3b94"),
"a": 1,
"b": 3
}
{
"_id": ObjectId("57b324d141aaa4b930ef3b95"),
"a": 1,
"b": 4
}
{
"_id": ObjectId("57b324d541aaa4b930ef3b96"),
"a": 1,
"b": 5
}
{
"_id": ObjectId("57b324da41aaa4b930ef3b97"),
"a": 1,
"b": 6
}
{
"_id": ObjectId("57b324df41aaa4b930ef3b98"),
"a": 1,
"b": 7
}
{
"_id": ObjectId("57b324e441aaa4b930ef3b99"),
"a": 1,
"b": 8
}
{
"_id": ObjectId("57b324f341aaa4b930ef3b9a"),
"a": 1,
"b": ""
}
{
"_id": ObjectId("57b324f641aaa4b930ef3b9b"),
"a": 1,
"b": " "
}
{
"_id": ObjectId("57b324fc41aaa4b930ef3b9c"),
"a": 1,
"b": null
}
{
"_id": ObjectId("57b3250341aaa4b930ef3b9d"),
"a": 1
}
{
"_id": ObjectId("57b46ace41aaa4b930ef3b9e"),
"a": 2
}
And I have the following indexes:
devsrv(mongod-3.0.4) test> db.teste.getIndexes()
[
{
"v": 1,
"key": {
"_id": 1
},
"name": "_id_",
"ns": "test.teste"
},
{
"v": 1,
"key": {
"a": 1,
"b": 1
},
"name": "a_1_b_1",
"ns": "test.teste"
},
{
"v": 1,
"key": {
"b": 1
},
"name": "b_1",
"ns": "test.teste"
}
]
And I need make a query equal this:
devsrv(mongod-3.0.4) test> db.teste.find({$or:[{"b":null},{"b":""},{"b":" "},{"b":{$lt:3}}],"a":1}).explain("executionStats")
{
"queryPlanner": {
"plannerVersion": 1,
"namespace": "test.teste",
"indexFilterSet": false,
"parsedQuery": {
"$and": [
{
"$or": [
{
"b": {
"$eq": null
}
},
{
"b": {
"$eq": ""
}
},
{
"b": {
"$eq": " "
}
},
{
"b": {
"$lt": 3
}
}
]
},
{
"a": {
"$eq": 1
}
}
]
},
"winningPlan": {
"stage": "FETCH",
"filter": {
"a": {
"$eq": 1
}
},
"inputStage": {
"stage": "FETCH",
"filter": {
"$or": [
{
"b": {
"$eq": null
}
},
{
"b": {
"$eq": ""
}
},
{
"b": {
"$eq": " "
}
},
{
"b": {
"$lt": 3
}
}
]
},
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"b": 1
},
"indexName": "b_1",
"isMultiKey": false,
"direction": "forward",
"indexBounds": {
"b": [
"[null, null]",
"[-inf.0, 3.0)",
"[\"\", \"\"]",
"[\" \", \" \"]"
]
}
}
}
},
"rejectedPlans": [
{
"stage": "FETCH",
"filter": {
"$or": [
{
"b": {
"$eq": null
}
},
{
"b": {
"$eq": ""
}
},
{
"b": {
"$eq": " "
}
},
{
"b": {
"$lt": 3
}
}
]
},
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"a": 1,
"b": 1
},
"indexName": "a_1_b_1",
"isMultiKey": false,
"direction": "forward",
"indexBounds": {
"a": [
"[1.0, 1.0]"
],
"b": [
"[MinKey, MaxKey]"
]
}
}
}
]
},
"executionStats": {
"executionSuccess": true,
"nReturned": 6,
"executionTimeMillis": 0,
"totalKeysExamined": 8,
"totalDocsExamined": 14,
"executionStages": {
"stage": "FETCH",
"filter": {
"a": {
"$eq": 1
}
},
"nReturned": 6,
"executionTimeMillisEstimate": 0,
"works": 10,
"advanced": 6,
"needTime": 2,
"needFetch": 0,
"saveState": 0,
"restoreState": 0,
"isEOF": 1,
"invalidates": 0,
"docsExamined": 7,
"alreadyHasObj": 7,
"inputStage": {
"stage": "FETCH",
"filter": {
"$or": [
{
"b": {
"$eq": null
}
},
{
"b": {
"$eq": ""
}
},
{
"b": {
"$eq": " "
}
},
{
"b": {
"$lt": 3
}
}
]
},
"nReturned": 7,
"executionTimeMillisEstimate": 0,
"works": 8,
"advanced": 7,
"needTime": 1,
"needFetch": 0,
"saveState": 0,
"restoreState": 0,
"isEOF": 1,
"invalidates": 0,
"docsExamined": 7,
"alreadyHasObj": 0,
"inputStage": {
"stage": "IXSCAN",
"nReturned": 7,
"executionTimeMillisEstimate": 0,
"works": 8,
"advanced": 7,
"needTime": 1,
"needFetch": 0,
"saveState": 0,
"restoreState": 0,
"isEOF": 1,
"invalidates": 0,
"keyPattern": {
"b": 1
},
"indexName": "b_1",
"isMultiKey": false,
"direction": "forward",
"indexBounds": {
"b": [
"[null, null]",
"[-inf.0, 3.0)",
"[\"\", \"\"]",
"[\" \", \" \"]"
]
},
"keysExamined": 8,
"dupsTested": 0,
"dupsDropped": 0,
"seenInvalidated": 0,
"matchTested": 0
}
}
}
},
"serverInfo": {
"host": "devsrv",
"port": 27017,
"version": "3.0.4",
"gitVersion": "0481c958daeb2969800511e7475dc66986fa9ed5"
},
"ok": 1
}
But MongoDB isn't using the two indexes together.
Each $or term is effectively a separate query, so it helps to structure your query so that each term aligns with the index you're hoping to use. In this case that means moving the a: 1 part inside of each $or term:
db.teste.find({
$or:[
{a: 1, b: null},
{a: 1, b: ""},
{a: 1, b: " "},
{a: 1, b: {$lt: 3}}
]}).explain('executionStats')
The explain output shows that the a_1_b_1 is used for this query.
But you can simplify this a bit more by using $in to combine the first three terms into one:
db.teste.find({
$or:[
{a: 1, b: {$in: [null, "", " "]}},
{a: 1, b: {$lt: 3}}
]}).explain('executionStats')
This is also able to use the a_1_b_1 index.
The code is
db.teste.explain("executionStats").find({a: 1,
$or:[{b: null},
{b: ""},
{b: " "},
{b: {$lt:3}}]
}).hint({a: 1, b: 1})
Be careful with the hint command as the query optimizer chooses the most efficient query by measuring actual performance of the query with every suitable index.