Pull an item from all arrays MongoDB - javascript

idk if this is possible but need some help with mongo, I have the following document, and I want to make it so I can use $addToSet to add a value to one of the items in votes, but remove that item from all the other items in votes but have no idea how
{
_id: '872952643117518909',
questions: [
{ question: 'a', number: 1, dropDownInfo: [Object] },
{ question: 'b', number: 2, dropDownInfo: [Object] },
{ question: 'c', number: 3, dropDownInfo: [Object] }
],
votes: {
'1': [ '619284841187246090', '662697094104219678' ],
'2': [ '619284841187246090', '662697094104219678' ],
'3': [ '662697094104219678', '619284841187246090' ]
},
question: 'abc',
timestamp: 1628198528903,
finished: false,
channel: '812038854302892064'
}

The bellow pipeline adds a vote('619284841187246090') to a specific field,here randomly "2" was chosen,and removes that vote from "1" and "3" array.
Solution is general,can work with any vote fields not just "1" "2" "3".
You can use this pipeline in aggregation or update with pipeline (Mongodb>=4.2)
$addToSet doesn't work in arrays, it works when grouping and
in some other places in MongoDB 5.
I think your schema has a problem, because you are saving data in the schema, and that makes querying harder and creating indexing harder etc.
But we can still do it converting the object to array and back to object.
I think its best to keep data in arrays,and fields to be the known schema.
You can run the bellow code here
Query
db.collection.aggregate( [ {
"$addFields" : {
"votes" : {
"$arrayToObject" : {
"$map" : {
"input" : {
"$map" : {
"input" : {
"$objectToArray" : "$votes"
},
"as" : "m",
"in" : [ "$$m.k", "$$m.v" ]
}
},
"as" : "vote",
"in" : {
"$cond" : [ {
"$eq" : [ {
"$arrayElemAt" : [ "$$vote", 0 ]
}, "2" ]
}, [ {
"$arrayElemAt" : [ "$$vote", 0 ]
}, {
"$cond" : [ {
"$in" : [ "619284841187246090", {
"$arrayElemAt" : [ "$$vote", 1 ]
} ]
}, {
"$arrayElemAt" : [ "$$vote", 1 ]
}, {
"$concatArrays" : [ {
"$arrayElemAt" : [ "$$vote", 1 ]
}, [ "619284841187246090" ] ]
} ]
} ], [ {
"$arrayElemAt" : [ "$$vote", 0 ]
}, {
"$filter" : {
"input" : {
"$arrayElemAt" : [ "$$vote", 1 ]
},
"as" : "v",
"cond" : {
"$not" : [ {
"$eq" : [ "$$v", "619284841187246090" ]
} ]
}
}
} ] ]
}
}
}
}
}
} ])
Results
[
{
"_id": "872952643117518909",
"channel": "812038854302892064",
"finished": false,
"question": "abc",
"questions": [
{
"dropDownInfo": "",
"number": 1,
"question": "a"
},
{
"dropDownInfo": "",
"number": 2,
"question": "b"
},
{
"dropDownInfo": "",
"number": 3,
"question": "c"
}
],
"timestamp": 1.628198528903e+12,
"votes": {
"1": [
"662697094104219678"
],
"2": [
"619284841187246090",
"662697094104219678"
],
"3": [
"662697094104219678"
]
}
}
]

Related

Modify the query to get the expected result

I am trying to modify query to get expected output.I am able to write the query but not getting the output as expected so that I may bind in the front end.
Actual output:-
{
"_id" : null,
"first" : 3571.0,
"second" : 24.0
}
Expected output:-
{ "_id" : null,
"opertion":edit,
"count" : 3571.0,
}
{ "_id" : null,
"opertion":read,
"count" : 24,
}
{ "_id" : null,
"opertion":update,
"count" : 9000,
}
Myquery:-
db.getCollection('blog').aggregate([
{ "$group": {
"_id": null,
"first": {
"$sum": {
"$cond": [{ "$in": ["$Operation", ["edit1", "edit2"]] }, 1, 0]
}
},
"second": {
"$sum": {
"$cond": [{ "$in": ["$Operation", ["read1", "read2"]] }, 1, 0]
}
}
},
},
])
if you have collection which is like as below:
[
{
"_id" : 1,
"operation" : "edit1" # some extra fields
},
{
"_id" : 2,
"operation" : "read1"
},
{
"_id" : 3,
"operation" : "update1"
}
]
by using $project and $cond you can rename the "read1", "read2" to read or updates to update, or edits to edit then by grouping on the new operation field you can get the count of each operation.
you can use this query:
db.aggregate([
{
"$project": {
"new_operation":
{
"$cond": [
{"$in":
["$Operation", ["edit1", "edit2"]]
}, "edit", {
"$cond": [
{"$in":
["$operation", ["read1", "read2"]]
}, "read", "update"]
}
]
}
}
},
{
"$group": {
"_id": "$new_operation",
"count": {"$sum": 1}
}
}
])

Nested queries don't work with cosmosdb-mongodb

My document in cosmosdb looks like this
{
"todayDate": "2017-12-08",
"data": [
{
"group": {"priority": 1, "total": 10},
"severity": 1
},
{
"group": {"priority": 2, "total": 13},
"priority": 2
}
]
}
The following query when issued from either mongoShell for cosmosdb in azure portal or using my spring data mongodb project works fine and returns results in no time:
db.myCollection.find({ "$or" : [ { "data" : { "$elemMatch" : { "priority" : 1}} , "$or" : [ { "data" : { "$elemMatch" : { "group.priority" : 1}}}] }]})
However, the following query on the same lines with more OR conditions which basically is two of the above queries with OR operator, hangs indefinitely:
db.myCollection.find({ "$or": [ { "data" : { "$elemMatch" : { "priority" : 1}} , "$or" : [ { "data" : { "$elemMatch" : { "group.priority" : 1}}}] }, { "data" : { "$elemMatch" : { "severity" : 2}} , "$or" : [ { "data" : { "$elemMatch" : { "group.severity" : 2}}}] } ] })
Is there anything wrong with the last query that makes it hang indefinitely? Even if I replace initial OR with AND, still the same result i.e. hangs indefinitely.
I created 3 documents in my cosmos db according to the document template you provided.
[
{
"id": "1",
"todayDate": "2017-12-08",
"data": [
{
"group": {
"severity": 1,
"total": 10
},
"severity": 1
},
{
"group": {
"priority": 1,
"total": 13
},
"priority": 1
}
]
},
{
"id": "2",
"todayDate": "2017-12-09",
"data": [
{
"group": {
"priority": 3,
"total": 10
},
"severity": 1
},
{
"group": {
"priority": 3,
"total": 13
},
"priority": 1
}
]
},
{
"id": "3",
"todayDate": "2017-12-10",
"data": [
{
"group": {
"priority": 1,
"total": 10
},
"severity": 1
},
{
"group": {
"priority": 2,
"total": 13
},
"priority": 2
}
]
}
]
Then I use Robo 3T tool to execute your sql.
db.coll.find({
"$or": [
{ "data" : { "$elemMatch" : { "priority" : 1}} ,
"$or" : [
{ "data" : { "$elemMatch" : { "group.priority" : 1}}}
] },
{ "data" : { "$elemMatch" : { "severity" : 2}} ,
"$or" : [
{ "data" : { "$elemMatch" : { "group.severity" : 2}}}
] }
]
})
result:
The syntax of the $or that I found on the official document is:
{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
It seems that your SQL can be executed normally though it is different from the above syntax. Per my experience, $or is generally used to be nested with $and (MongoDB Nested OR/AND Where?) ,so I do not quite understand what is the purpose of your $or nested here.
Surely, an indefinite hang is probably because the data is too large so that SQL runs too long and you need to optimize your SQL.
Hope it helps you.Any concern ,please let me know.
Update Answer:
I have properly modified my 3 sample documents then query 2 eligible documents via the SQL you provided.
SQL:
db.coll.find(
{
"$and": [
{
"$or": [
{
"data": {
"$elemMatch": {
"priority": 2
}
}
},
{
"data": {
"$elemMatch": {
"group.priority": 2
}
}
}
]
},
{
"$or": [
{
"data": {
"$elemMatch": {
"severity": 1
}
}
},
{
"data": {
"$elemMatch": {
"group.severity": 1
}
}
}
]
}
]
}
)
Results:
So , I think your SQL is correct. Is the data in the database very large? If you've been hanging for a long time, did you have seen timeout error messages? Or you could check RUs setting's issue.

Trouble creating query in MongoDB with subquery

I have a dataset that looks something like this:
{
"id": "02741544",
"items": [{
"item": "A"
}]
}, {
"id": "02472691",
"items": [{
"item": "A"
}, {
"item": "B"
}, {
"item": "C"
}]
}, {
"id": "01316523",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316526",
"items": [{
"item": "A"
}, {
"item": "B"
}]
}, {
"id": "01316529",
"items": [{
"item": "A"
}, {
"item": "D"
}]
},
I'm trying to craft a query that will give me an output that looks like this:
{
"item": "A",
"ids": [{
"id": "02741544"
}, {
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}, {
"id": "01316529"
}]
}, {
"item": "B",
"ids": [{
"id": "02472691"
}, {
"id": "01316523"
}, {
"id": "01316526"
}]
}, {
"item": "C",
"ids": [{
"id": "02472691"
}]
}, {
"item": "D",
"ids": [{
"id": "02472691"
}]
},
Basically, I'm trying to get the distinct items from the item array in the object, and then returning an array of ids for each obj that has that item in it's item array.
Better use the aggregation framework in which you need to run an operation that consists of the following pipeline steps (in the given order):
$unwind - This initial step will flatten the items array i.e. it produces a copy of each document per array entry. This is necessary for processing the documents further down the pipeline as "denormalised" documents which you can aggregate as groups.
$group - This will group the flattened documents by the item subdocument key and create the ids list by using the $push accumulator operator.
-- UPDATE --
As #AminJ pointed out in the comments, if items can have duplicate item values and you don't want duplicate ids in the result you can use $addToSet instead of $push
The following example demonstrates this:
db.collection.aggregate([
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
])
Sample Output
{
"_id" : "A",
"ids" : [
{ "id" : "02741544" },
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" },
{ "id" : "01316529" }
]
}
/* 2 */
{
"_id" : "B",
"ids" : [
{ "id" : "02472691" },
{ "id" : "01316523" },
{ "id" : "01316526" }
]
}
/* 3 */
{
"_id" : "C",
"ids" : [
{ "id" : "02472691" }
]
}
/* 4 */
{
"_id" : "D",
"ids" : [
{ "id" : "01316529" }
]
}
The result from an aggregate() function is a cursor to the documents produced by the final stage of the aggregation pipeline operation. So if you want the results in an array you can use the cursor's toArray() method which returns an array that contains all the documents from it.
For example:
var pipeline = [
{ "$unwind": "$items" },
{
"$group": {
"_id": "$items.item",
"ids": {
"$push": { "id": "$id" } /* or use
"$addToSet": { "id": "$id" } if you don't want duplicate ids */
}
}
}
],
results = db.collection.aggregate(pipeline).toArray();
printjson(results);
Here's a solution using an aggregation pipeline:
db.col.aggregate([
{
$unwind: "$items"
},
{
$project: {
id: 1,
item: "$items.item"
}
},
{
$group: {
_id: "$item",
ids: {
$push: "$id"
}
}
}
])

How to convert string to number in a nested array?

I have the following array of objects which has an array inside an
array. the values are in the string, I want it to be a number. What is
the best possible solution to do that in JavaScript.
[
{
"key": "America",
"values": [
[
"1469664000000",
"1"
],
[
"1469750400000",
"13"
],
[
"1469836800000",
"2"
],
[
"1469923200000",
"1"
],
[
"1470009600000",
"1"
],
[
"1470096000000",
"1"
]
]
},
{
"key": "India",
"values": [
[
"1469750400000",
"1"
]
]
},
{
"key": "China",
"values": [
[
"1469664000000",
"2"
],
[
"1469836800000",
"1"
],
[
"1470096000000",
"12"
]
]
},
{
"key": "Africa",
"values": [
[
"1470096000000",
"1"
]
]
},
{
"key": "UK",
"values": [
[
"1469664000000",
"3"
],
[
"1469750400000",
"3"
],
[
"1469836800000",
"1"
],
[
"1469923200000",
"2"
],
[
"1470009600000",
"4"
],
[
"1470096000000",
"2"
]
]
}
]
var data = [{"key":"America","values":[["1469664000000","1"],["1469750400000","13"],["1469836800000","2"],["1469923200000","1"],["1470009600000","1"],["1470096000000","1"]]},{"key":"India","values":[["1469750400000","1"]]},{"key":"China","values":[["1469664000000","2"],["1469836800000","1"],["1470096000000","12"]]},{"key":"Africa","values":[["1470096000000","1"]]},{"key":"UK","values":[["1469664000000","3"],["1469750400000","3"],["1469836800000","1"],["1469923200000","2"],["1470009600000","4"],["1470096000000","2"]]}];
data.forEach(function (o) {
o.values.forEach(function (values) {
values.forEach(function (value, i) {
values[i] = parseInt(value, 10);
});
});
});
console.log(JSON.stringify(data, null, 4));
var data = [{"key":"America","values":[["1469664000000","1"],["1469750400000","13"],["1469836800000","2"],["1469923200000","1"],["1470009600000","1"],["1470096000000","1"]]},{"key":"India","values":[["1469750400000","1"]]},{"key":"China","values":[["1469664000000","2"],["1469836800000","1"],["1470096000000","12"]]},{"key":"Africa","values":[["1470096000000","1"]]},{"key":"UK","values":[["1469664000000","3"],["1469750400000","3"],["1469836800000","1"],["1469923200000","2"],["1470009600000","4"],["1470096000000","2"]]}];
data = data.map(function(t){
var newVals = t.values.map(function(vs){
return vs.map(function(v){
return parseInt(v, 10);
});
});
return {'key': t.key, values: newVals};
});
console.log(JSON.stringify(data));
You can use JSON.parse(your json object
or maybe use two forEachs

MongoDB nested query - BadValue error

Here's the query I'm trying to run:
{ ownerId: 14,
'$or':
[ { '$or':
[ { '$and':
[ { provider: 'INSTAGRAM' },
{ tags:
{ '$or':
[ { '$in': [ 'skyhotel' ] },
{ '$or': [ { '$all': ["skyhotel","excellent"] } ] } ] } } ] },
{ '$and':
[ { provider: 'VKONTAKTE' },
{ tags:
{ '$or':
[ { '$in': [ 'skyhotel' ] },
{ '$or': [ { '$all': ["skyhotel","excellent"] } ] } ] } } ] } ] },
{ '$or':
[ { '$and':
[ { provider: 'INSTAGRAM' },
{ authorLogin: { '$in': [ 'valera92', 'petyan' ] } } ] },
{ '$and':
[ { provider: 'VKONTAKTE' },
{ authorLogin: { '$in': [ 'valera92' ] } } ] } ] },
{ '$or':
[ { '$and':
[ { provider: 'INSTAGRAM' },
{ locationId: { '$in': [ '32454234' ] } } ] } ] },
{ '$or':
[ { '$and':
[ { provider: 'INSTAGRAM' },
{ location: { '$or': [ { '$geoWithin': { '$centerSphere': [ [ '56.829782', '60.593162' ], 0.000012629451881788331 ] } } ] } } ] } ] } ] }
It seems to be malformed according to mongo standards. The error I'm getting is:
Can't canonicalize query: BadValue unknown operator: $or
What would be the proper way in which I could reformat this query?
EDIT: Document example
{
"_id" : ObjectId("570362332ee1a7ab1ecb9899"),
"proextid" : "INSTAGRAM_fdfsfsdfsdfwefwef2r3232",
"updatedAt" : ISODate("2016-04-05T06:58:59.683Z"),
"createdAt" : ISODate("2016-04-05T06:58:59.683Z"),
"ownerId" : 7,
"authorId" : "390599885",
"authorName" : "name",
"authorLogin" : "login",
"authorDetail" : {
"authorPicture" : "url",
"authorLink" : "url"
},
"externalCreatedAt" : ISODate("2015-08-29T22:42:04.000Z"),
"externalId" : "fdsfsdfsdfsdfwer2342342423423r23r",
"detailType" : "PHOTO",
"location" : [
0,
0
],
"locationId" : "",
"locationTitle" : "",
"provider" : "INSTAGRAM",
"detail" : {},
"description" : "hello",
"tags" : [
"ленинградскийпроспект",
"москва",
"архитектура",
"историческоенаследие",
"петровскийдворец"
],
"commentsCount" : 1,
"likesCount" : 40,
"groupName" : "",
"__v" : 0
}
Your query could be simplified as something like:
{
ownerId:14,
provider: { $in: [ 'INSTAGRAM', 'VKONTAKTE' ] },
'$or': [
{
tags: { $in: [ 'skyhotel', 'excellent' ] }
},
{
authorLogin: { $in: [ 'valera92', 'petyan' ] },
},
{
locationId:{ '$in':[ '32454234' ] }
},
{
location:{
{
'$geoWithin':{
'$centerSphere':[ [ '56.829782', '60.593162' ], 0.000012629451881788331 ]
}
}
}
}
]
}
Now this may not be exactly what you're looking for. But from what you asked, and the information you provided, this is the best suggestion I can give.

Categories

Resources