How can I use two projections in MongoDB at the same time? - mongodb

I have documents I need to retrieve data from in this format:
{
"id": ObjectId("alskdjflqkjwr23"),
"field1": {
"morefields": values
},
"field2": {
"morefields": values
},
"field3": {
"morefields": values
},
"field14": {
"morefields": values
},
"importantField": {
"subfield1": values,
"subfield2": values,
"importantArray": [
{
"subsubfield1": values,
"importantSubArray": [
{
"subsubsubfield1": values,
"subsubsubfield2": values
},
{
"subsubsubfield1": values,
"subsubsubfield2": values
}
],
"importantValue": values
},
{
"subsubfield1": values,
"subsubfield2": values,
"subsubfield3": values
}
]
}
}
I need the importantValue and elements in the importantSubArray fields, but I can't get my query to return what I need without all of the extra data in the other fields. I just started using Mongo a week ago.
I have tried this query, but only one of the projections works, depending on which order I put them in. So I can either return only the array within importantField, but all 400 or so elements, not just the last 2, or I can return the last 2 elements of importantField.importantArray, but also return all of the other fields, subfields, subsubfields etc.
db.getCollection("my_data").find({},
{
"importantField.importantArray" : 1.0,
"importantField.importantArray": { $slice: -2 }
}
);
How can I get both of these to work at the same time? Thanks.
EDIT:
The expected output should look like this. I need the elements in importantSubArray array and importantValue field
{
"importantSubArray": [
{
"stuff": morestuff
},
],
"importantValue": value

I was able to combine the comment from #prasad_ with a slice by using an aggregation and projecting the fields I want in the first stage and then doing another projection in stage 2 and slicing for the last 2 elements.

Related

How to do fast query on String dataType in MongoDB, where values are of type double

I have a field contractValue and other fields in a collection contract which is of type String . It basically holds double value like 1200 or 1500 but at some places it may contain value like $1200 or $1500.
Sample data from collection:
{ ..
..
contractValue: "1200", //This is the one stored as String. I need
// to perform range query over it
..
..
}
{ ..
..
contractValue: "$1500",
..
..
}
I have requirement where i need to fetch contracts based on contract values. Query can be like below:
{$and: [ {'contractValue': {$gt: 100}}, {'contractValue': {$lt: 1000 }}]}
This query is giving me wrong result. It is also giving me documents having contractValue like 1238999
Also I need to create indexes on contractValue
Is it possible to create index on contract value , so that I can efficiently make range query, so that whenever making any query, it will do < or > on Index and will fetch exact set of documents, rather than making change in schema?
How to handle values like $1200 in index, so index value just contain 1200 as integer
rather than $1200
try this:
https://mongoplayground.net/p/TG3Y5tdh9aK
it assumes string data will be either a quoted number or a quoted number with "$" at the front
db.collection.aggregate([
{
$project: {
"newContractValue": {
"$convert": {
"input": "$contractValue",
"to": "double",
"onError": {
$toDouble: {
"$substr": [
"$contractValue",
1,
{
"$strLenCP": "$contractValue"
}
]
}
}
}
}
}
},
{
$match: {
$and: [
{
"newContractValue": {
$gt: 100
}
},
{
"newContractValue": {
$lt: 1000
}
}
]
}
}
])
This can be used to set a new contractValueNew field as number from the existing contractValue
db.getCollection('yourCollection').find({})
.forEach(function(record) {
if(record.contractValue.toString().substring(0, 1) == '$') {
record.contractValueNew = NumberInt(parseInt(record.contractValue.substring(1, record.contractValue.length)));
} else {
record.contractValueNew = NumberInt(parseInt(record.contractValue))
}
db.getCollection('yourCollection').save(record)
})
Try:
db.collection.find({'contractValue': {$gt: 100, $lt: 1000 }})
Create index on contractValue , but convert all values as numbers ...

mongodb query to verify embedded array sequence numbers

given a document structure as shown, where the trades array can have thousands of items... how on earth could one do a query that would verify that the sequence always has 'startTradeId' one number higher than the previous items 'endTradeId', all the way through the array? is this even possible?
{
"name": "STOCK",
"trades": [{
"endTradeId": 41306,
"startTradeId": 41302,
...
},
{
"endTradeId": 41301,
"startTradeId": 41297,
...
},
{
"endTradeId": 41296,
"startTradeId": 41240,
...
},
...
]
}
You can use $where operator like below :
db.your_collection.find( { $where : function(){ return "this.trades.startTradeId > this.trades.endTradeId" }});

MongoDB grouping&sorting

the data I'm working with looks like this:
so I have a root objects with array of elements ('data') which of each one contains field 'eprid'.
The grouping I need to achieve is based on this ('$data.eprid') field alongside with '$$ROOT.createdOn' date --and this would be the most recent date of all the equal 'eprid' values.
In other words, I need to create tuples of unique 'eprid' and 'date' where date is the most recent one of all the equal 'eprid' values from all the documents.
[
{
"eprid": <uniq.value>,
"createdOn": <mostRecentDate>
},
...
]
The closest I got is
db.domains.aggregate( [
{ $group : {
_id : { "$$ROOT.createdOn" },
eprid: { $addToSet: "$data.eprid" }
} }
] )
but that's not even close.
Thanks for suggestions.

MongoDB :: Order Search result depend on search condition

I have a data
[{ "name":"BS",
"keyword":"key1",
"city":"xyz"
},
{ "name":"AGS",
"keyword":"Key2",
"city":"xyz1"
},
{ "name":"QQQ",
"keyword":"key3",
"city":"xyz"
},
{ "name":"BS",
"keyword":"Keyword",
"city":"city"
}]
and i need to search records which have name= "BS" OR keyword="key2" with the help of query
db.collection.find({"$OR" : [{"name":"BS"}, {"keyword":"Key2"}]});
These records i need in the sequence
[{ "name":"BS",
"keyword":"key1",
"city":"xyz"
},
{ "name":"BS",
"keyword":"Keyword",
"city":"city"
},
{ "name":"AGS",
"keyword":"Key2",
"city":"xyz1"
}]
but i am getting in following sequences:
[{ "name":"BS",
"keyword":"key1",
"city":"xyz"
},
{ "name":"AGS",
"keyword":"Key2",
"city":"xyz1"
},
{ "name":"BS",
"keyword":"Keyword",
"city":"city"
}]
Please provide some suggestion i am stuck with this problem since 2 days.
Thanks
The order of results returned by MongoDB is not guaranteed unless you explicitly sort your data using the sort function. For smaller datasets you maybe "lucky" in the sense that the results are always returned in the same order, however, for bigger datasets and in particular when you have sharded Mongo clusters this is very unlikely. As proposed by Yathish you need to explicitly order your results using the sort function. Based on the suggested output, it seems you want to sort by name in descending order so I have set the sorting flag to -1 for the field name.
db.collection.find({"$or" : [{"name":"BS"}, {"keyword":"Key2"}]}).sort({"name" : -1});
If you need a more complex sorting algorithm as specified in your comment, you can convert your results to a Javascript array and create a custom sort function. This sort function will first list documents with a name equal to "BS" and then documents containing the keyword "Key2"
db.data.find({
"$or": [{
"name": "BS"
}, {
"keyword": "Key2"
}]
}).toArray().sort(function(doc1, doc2) {
if (doc1.name == "BS" && doc2.keyword == "Key2") {
return -1
} else if (doc2.name == "BS" && doc1.keyword == "Key2") {
return 1
} else {
return doc1.name < doc2.name
}
});

How can I create an index in on an array field in MongoDB?

I have a MongoDB collection with data in the format of:
[
{
"data1":1,
"data2":2,
"data3":3,
"data4":4,
"horses":[
{
"opponent":{
"jockey":"MyFirstName MyLastName",
"name":"MyHorseName",
"age":4,
"sex":"g",
"scratched":"false",
"id":"1"
},
"id":"1"
},
{
"opponent":{
"jockey":"YourFirstName YourLastName",
"name":"YourHorseName",
"age":4,
"sex":"m",
"scratched":"false",
"id":"2"
},
"id":"2"
}
]
},
...
]
Executing the following query returns exactly what I need:
db.race_results.find({ "$and": [ { "horses":
{ "$elemMatch": { "$and": [
{ "opponent.name": "MyFirstName MyLastName" },
{ "opponent.jockey": "MyHorseName"}
] } }
}
]})
However, this query takes 0.5 seconds to execute with my collection (there are a lot of records).
I am trying to find out how to create an index on the horses.opponent.name field of the data. I have read the docs about multikey indexes (here), but I'm not sure if this is exactly what I need or not. What I need (I think) is an index on the array element of horses, but only the name and jockey fields. Is this possible?
Is there a way to create an index to make my specific query (the one above) any faster?
Any pointers would be greatly appreciated. I am fairly new to MongoDB, but learning fast!
The index to create is:
db.race_results.ensureIndex({"horses.opponent.name":1, "horses.opponent.jockey":1})
After creating this index, the query in your case should return number of scanned objects that is equal to the number of matched objects:
db.race_results.find( { horses: { $elemMatch: { "opponent.name": "MyHorseName", "opponent.jockey": "MyFirstName MyLastName" } } }
).explain()