How do I find "$oid" in MongoDB instead of _id? - mongodb

I'm trying to do a find search in MongoDB and I just want to return the objectId as a string, but instead I get:
[
{
"_id": {
"$oid": "6114ffebae76283ccfb92c44"
}
}
[
I want:
[
{
"$oid": "6114ffebae76283ccfb92c44"
}
]
Here is my query:
db.person.find(
{ $and: [{"age":20},{"name":"Frank"}] }, {"_id":1}
)

You could apply a $toString to get the id in your desired format.
db.collection.find({},
{
"_id": {
$toString: "$_id"
}
})
However you can't use the $oid as field name because is a reserved keyword.
Example: https://mongoplayground.net/p/y1DsrQ-k4Hj

You can use aggregate:
db.collection.aggregate([
{
$project: {
_id: {
$toString: "$_id"
}
}
}
])

Here you can test it (I set the name of _id to id)
https://mongoplayground.net/p/xdw3PpiwYId
db.person.find({
$and: [
{
"age": 20
},
{
"name": "Frank"
}
]
},
{
"id": {
$toString: "$_id"
},
"_id": 0
})

Related

How to compare a field to a field of a array in mongodb

I have a documents like below. I want to retrieve all documents whose address.city == "newyork" and address.id == active.
[
{
"name": "star1",
"active": 1,
"address": [
{
"id": 1,
"city": "newyork"
},
{
"id": 2,
"city": "sydney"
}
]
},
{
"name": "star2",
"active": 2,
"address": [
{
"id": 1,
"city": "newyork"
},
{
"id": 2,
"city": "london"
}
]
}
]
I have written below query and it Partially works, But It is not returning complete document. I can't use unwind. Do we have any solution without using "unwind". Is it possible to solve a problem only with $match
db.collection.aggregate([
{
$unwind: "$address"
},
{
$match: {
$expr: {
$eq: [
"$active",
"$address.id"
]
},
"address.city": "newyork"
}
}
])
Maybe something like this:
db.collection.aggregate([
{
"$addFields": {
"address": {
"$filter": {
"input": "$address",
"as": "a",
"cond": {
$and: [
{
$eq: [
"$$a.id",
"$active"
]
},
{
$eq: [
"$$a.city",
"newyork"
]
}
]
}
}
}
}
},
{
$match: {
address: {
$ne: []
}
}
}
])
Explained:
Use addFields/filter to match only matching documents in the array.
Remove the documents with empty address from the array for the cases where no subdocuments is found.
Playground
In case you need to match the whole document containing at least one entry having {address.id==active and address.city==newyork } here is an option:
db.collection.aggregate([
{
$match: {
$expr: {
"$in": [
{
id: "$active",
city: "newyork"
},
"$address"
]
}
}
}
])
Explained:
Match only documents having at least one object in address array with id==$active and city=="newyork"
Playground
In case we expect different order inside the address objects , the more correct option is as follow:
db.collection.aggregate([
{
$match: {
$expr: {
$or: [
{
"$in": [
{
id: "$active",
city: "newyork"
},
"$address"
]
},
{
"$in": [
{
city: "newyork",
id: "$active"
},
"$address"
]
}
]
}
}
}
])
Explained:
Match only documents having at least one object in array with { id==$active and city=="newyork" } or { city=="newyork" and id==$active }
Playground 2

MongoDB Aggregate Query to find the documents with missing values

I am having a huge collection of objects where the data is stored for different employees.
{
"employee": "Joe",
"areAllAttributesMatched": false,
"characteristics": [
{
"step": "A",
"name": "house",
"score": "1"
},
{
"step": "B",
"name": "car"
},
{
"step": "C",
"name": "job",
"score": "3"
}
]
}
There are cases where the score for an object is completely missing and I want to find out all these details from the database.
In order to do this, I have written the following query, but seems I am going wrong somewhere due to which it is not displaying the output.
I want the data in the following format for this query, so that it is easy to find out which employee is missing the score for which step and which name.
db.collection.aggregate([
{
"$unwind": "$characteristics"
},
{
"$match": {
"characteristics.score": {
"$exists": false
}
}
},
{
"$project": {
"employee": 1,
"name": "$characteristics.name",
"step": "$characteristics.step",
_id: 0
}
}
])
You need to use $exists to check the existence
playground
You can use $ifNull to handle both cases of 1. the score field is missing 2. score is null.
db.collection.aggregate([
{
"$unwind": "$characteristics"
},
{
"$match": {
$expr: {
$eq: [
{
"$ifNull": [
"$characteristics.score",
null
]
},
null
]
}
}
},
{
"$group": {
_id: null,
documents: {
$push: {
"employee": "$employee",
"name": "$characteristics.name",
"step": "$characteristics.step",
}
}
}
},
{
$project: {
_id: false
}
}
])
Here is the Mongo playground for your reference.

How do I use $unwind and then $group in the same mongodb query

I have the following mongodb structure...
[
{
track: 'Newcastle',
time: '17:30',
date: '22/04/2022',
bookmakers: [
{
bookmaker: 'Coral',
runners: [
{
runner: 'John',
running: true,
odds: 3.2
},
...
]
},
...
]
},
...
]
I'm trying to find filter the bookmakers array for each document to only include the objects that match the specified bookmaker values, for example:
{ 'bookmakers.bookmaker': { $in: ['Coral', 'Bet365'] } }
At the moment, I'm using the following mongodb query to only select the bookmakers that are specified, however I need to put the documents back together after they've been seperated by the '$unwind', is there a way I can do this using $group?
await HorseRacingOdds.aggregate([
{ $unwind: "$bookmakers" },
{
$group: {
_id: "$_id",
bookmakers: "$bookmakers"
}
},
{
$project: {
"_id": 0,
"__v": 0,
"lastUpdate": 0
}
}
])
How about a plain $addFields with $filter?
db.collection.aggregate([
{
"$addFields": {
"bookmakers": {
"$filter": {
"input": "$bookmakers",
"as": "b",
"cond": {
"$in": [
"$$b.bookmaker",
[
"Coral",
"Bet365"
]
]
}
}
}
}
},
{
$project: {
"_id": 0,
"__v": 0,
"lastUpdate": 0
}
}
])
Here is the Mongo playground for your reference.

Mongo DB Join on Primary/Foreign Key

I have two collections, viz: clib and mp.
The schema for clib is : {name: String, type: Number} and that for mp is: {clibId: String}.
Sample Document for clib:
{_id: ObjectId("6178008397be0747443a2a92"), name: "c1", type: 1}
{_id: ObjectId("6178008397be0747443a2a91"), name: "c2", type: 0}
Sample Document for mp:
{clibId: "6178008397be0747443a2a92"}
{clibId:"6178008397be0747443a2a91"}
While Querying mp, I want those clibId's that have type = 0 in clib collection.
Any ideas how this can be achieved?
One approach that I can think of was to use $lookUp, but that doesnt seem to be working. Also, I m not sure if this is anti-pattern for mongodb, another approach is to copy the type from clib to mp while saving mp document.
If I've understood correctly you can use a pipeline like this:
This query get the values from clib where its _id is the same as clibId and also has type = 0. Also I've added a $match stage to not output values where there is not any coincidence.
db.mp.aggregate([
{
"$lookup": {
"from": "clib",
"let": {
"id": "$clibId"
},
"pipeline": [
{
"$match": {
"$expr": {
"$and": [
{
"$eq": [
{
"$toObjectId": "$$id"
},
"$_id"
]
},
{
"$eq": [
"$type",
0
]
}
]
}
}
}
],
"as": "result"
}
},
{
"$match": {
"result": {
"$ne": []
}
}
}
])
Example here
db.mp.aggregate([
{
$lookup: {
from: "clib",
let: {
clibId: "$clibId"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [ "$_id", "$$clibId" ],
}
]
}
}
},
{
$project: { type: 1, _id: 0 }
}
],
as: "clib"
}
},
{
"$unwind": "$clib"
},
{
"$match": {
"clib.type": 0
}
}
])
Test Here

MongoDB aggregate: count of elements in an object key

I have a MongoDB document like this:
[
{
"_id": {
"$oid": "5ff09030cd55d6d9f378d460"
},
"username": "a value",
"userid": "123456",
"last_access_ts": 1612426253,
"last_access": "2021-2-4 9:10:53",
"anotherid": 12345678910,
"verified_date": "2021-1-2 16:24:32",
"verified_ts": 1609601072,
"group_users": {
"-1001159747589": [
{
"anotherid": 12345678910,
"userid": "123456"
}
],
"-1001143137644": [
{
"anotherid": 12345678910,
"userid": "123456"
}
],
"-1001368608972": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"registered_access": "2021-1-2 16:24:42",
}
]
I've two questions.
First one: I need to count the elements inside each group_users[key] object, and I'm stuck with this aggregate:
db.collection.aggregate([
{
$match: {
username: "a value"
}
},
{
$project: {
_id: 1,
userid: 1,
"groups": {
"$objectToArray": "$group_users"
}
}
},
{
$unwind: "$groups",
},
])
This aggregate gives me this result:
[
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001449720492",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
},
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001159747589",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
},
{
"_id": ObjectId("5ff09030cd55d6d9f378d460"),
"groups": {
"k": "-1001143137644",
"v": [
{
"anotherid": 12345678910,
"userid": "123456"
}
]
},
"userid": "123456"
}
]
How can I count each single groups[v] and then re-group the data? I would like to have a result like:
{
... some user data
"groups": {
"group_key": "count",
"second_group_key": "count",
"third_group_key": "count"
}
}
Is it possible with aggregate or I need to loop in the code?
My second question is always about the group_users. Is possible to have, recursively, the user data inside a group_users object?
I mean, every object inside group_users is an array of users; from this array can I have the user data (maybe with $graphLookup?) using the userid field or the anotherid field?
As a result from this second aggregate I would like to have something like this:
{
... some user data
"groups": {
"group_key": [{"userid": userid, "username": username}],
"second_group_key": [{"userid": userid, "username": username}],
"third_group_key": [{"userid": userid, "username": username}]
}
}
Obviously I can limit this "recursion" to 10 elements per time.
Thanks for any advice.
$objectToArray convert group_users object to array
$let to Bind variable group_arr for use in the specified expression, and returns the result of the expression.
$map to iterate loop of bind variable group_arr, get size of total element of v and return k and v,
$arrayToObject convert returned array from $map to object
db.collection.aggregate([
{ $match: { username: "a value" } },
{
$project: {
_id: 1,
userid: 1,
groups: {
$let: {
vars: { group_arr: { $objectToArray: "$group_users" } },
in: {
$arrayToObject: {
$map: {
input: "$$group_arr",
in: {
k: "$$this.k",
v: { $size: "$$this.v" }
}
}
}
}
}
}
}
}
])
Playground
Second question,
$unwind deconstruct groups array
$lookup with pipeline, match anotherid $in condition and return required fields
$group by _id and reconstruct groups array
$arrayToObject convert groups array to object
db.collection.aggregate([
{ $match: { username: "a value" } },
{
$project: {
_id: 1,
userid: 1,
groups: { $objectToArray: "$group_users" }
}
},
{ $unwind: "$groups" },
{
$lookup: {
from: "collection",
let: { anotherid: "$groups.v.anotherid" },
pipeline: [
{ $match: { $expr: { $in: ["$anotherid", "$$anotherid"] } } },
{
$project: {
_id: 0,
userid: 1,
username: 1
}
}
],
as: "groups.v"
}
},
{
$group: {
_id: "$_id",
groups: { $push: "$groups" },
userid: { $first: "$userid" }
}
},
{ $addFields: { groups: { $arrayToObject: "$groups" } } }
])
Playground