MongoDB associative array - pull possible? - mongodb

Having the following Array:
array(
'id' => 12,
'keys' => array('x1' => array('idx' => 12, 'text'=> '1123145'),
'x2' => array('idx' => 14, 'text'=> '1123142'),
'x3' => array('idx' => 12, 'text'=> '1123145'),
'x4' => array('idx' => 14, 'text'=> '1123145')
)
)
I want to pull all keys with idx 12. So i do the following:
$mdb->db->collection->update(array('id' => 12), array('$pull' => array('keys' => array('idx' => 12))));
But it don't works, whats the problem?

This is impossible to do with this schema.
you are trying to pull the id = 12 from the array key, but the problem, is that each element of key is an object by itself.
the only way to do what you want with minimum modification is change schema in this way:
{
"_id" : 12,
"keys" : [
{
"type" : 'x1',
"idx" : 12,
"text" : "1111"
},
{
"type" : 'x2',
"idx" : 14,
"text" : "1111"
},
{
"type" : 'x3',
"idx" : 12,
"text" : "1111"
},
{
"type" : 'x4',
"idx" : 14,
"text" : "1111"
}
}]
}
than you can run your query as follows:
db.XXX.update(
{ "_id" : 12},
{
'$pull' : {
'keys' : {
'idx' : 12
}
}
}
);
I hope you will be able to transfer this into php with an arrays, because it looks for me that you are using php for querying

Related

MongoDB + Laravel + jenssegers/laravel-mongodb + update nested child elements

Hellow folks, I am new to MongoDB and looking for some answer
Is there any way to update nested array without looping it.
foreach ($post->comments as $key => $comment) {
if ($comment['posted_by'] == $authUser['id']) {
$data = $post->update([
"comments.$key.description" => $dataArray['description'],
"comments.$key.updated_at" => $dataArray['updated_at'],
]);
}}
I want to to do something like below.
$post = Post::where('_id', $id)->where('comments.*.id', $commentId)->update(array('description' => $desc));
Or I have to write raw MongoDB query for that.
I have 1 level nested comment also under main comments so if I want to update nested comment than I have to loop comment array than the nested comment array.
if ($subCommentId) {
foreach ($comment as $nestedkey => $nestedComments) {
if ($nestedComments['id'] === $subCommentId && $nestedComments['posted_by'] == $authUser['id']) {
$data = $post->update([
"comments.$key.$nestedkey.description" => $dataArray['description'],
"comments.$key.$nestedkey.updated_at" => $dataArray['updated_at'],
]);
}
}
}
Something like this :
$post = Post::where('_id', $id)->where('comments.*.id', $commentId)->where('comments.*.*.id', $subCommentId)->update(array('description' => $desc));
Is it good to store comment in the same collection as an array or should I create a new collection for that as maximum BSON document size is 16 megabytes and how much comments it can store like 10K or more?
Below is my sample comment array format under one Collection.
"comments" : [
{
"description" : "description some",
"channel" : "swachhata-citizen-android",
"user_role" : "Citizen",
"id" : "5b4dc367d282f",
"user_role_id" : ObjectId("5accd7f8309a203be03b6441"),
"created_at" : "2018-07-17 15:52:31",
"updated_at" : "2018-07-17 15:52:31",
"ip_address" : "127.0.0.1",
"user_agent" : "PostmanRuntime/6.4.1",
"deleted" : false,
"channel_id" : "5acccfe4309a2038347a5c47",
"posted_by" : NumberInt(1),
"comments" : [
{
"description" : "some description nested",
"channel" : "swachhata-citizen-android",
"user_role" : "Citizen",
"id" : "5b4dcfc7022db",
"user_role_id" : ObjectId("5accd7f8309a203be03b6441"),
"created_at" : "2018-07-17 16:45:19",
"updated_at" : "2018-07-17 16:45:19",
"ip_address" : "127.0.0.1",
"user_agent" : "PostmanRuntime/6.4.1",
"deleted" : false,
"channel_id" : "5acccfe4309a2038347a5c47",
"posted_by" : NumberInt(1)
}
]
}
]
Thanks. :)
To update nested document, you should use arrayFilters:
Post::raw()->updateMany(
[],
[ '$set' => ["comments.$[i].comments.$[j].description" => $desc] ],
[ '$arrayFilters' => [
[
[ "i.id" => "5b4dc367d282f" ],
[ "j.id" => "5b4dcfc7022db" ]
]
]
]
)
Hope it helps :)

mongodb sorting group result

I am trying to get the first of each group, then I need to sort the resultset. I have achieved the first part but unable to sort the result. Here is what I have tried
Sample Data
{
"_id" : ObjectId("57f549e1831529409b000001"),"name" : "book1","author" : "abc","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:43:45.902Z"),"publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54a4483152940ad000001"),"name" : "book1","author" : "a1","revision" : 1.1,
"published_on" : ISODate("2016-10-05T18:45:24.436Z"), "publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54baa83152940c3000001"), "name" : "oldbook", "author" : "alice","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:51:22.484Z"),"publisher" : "newpublisher"
},
{
"_id" : ObjectId("57f54c4983152940c3000002"),"name" : "artoflearning","author" : "mike","revision" : 1.0,
"published_on" : ISODate("2016-10-05T18:54:01.585Z"),"publisher" : "oldpublisher"
},
{
"_id" : ObjectId("57f54c5883152940c3000003"),"name" : "artoflearning","author" : "mike","revision" : 1.1,
"published_on" : ISODate("2016-10-05T18:54:16.568Z"),"publisher" : "oldpublisher"
},
{ "_id" : ObjectId("57f54c6583152940c3000004"),"name" : "artoflearning","author" : "mike","revision" : 1.2,
"published_on" : ISODate("2016-10-05T18:54:29.848Z"),"publisher" : "oldpublisher"
},
{ "_id" : ObjectId("57f5513f8315294116000000"),"name" : "learning","author" : "mike","revision" : 2.0,
"published_on" : ISODate("2016-10-05T19:15:11.342Z"),"publisher" : "newpublisher"
}
I am getting latest revision (sort by revision in desc) group by name for a given publisher. I have used this query to achieve
Book.collection.aggregate([
{'$match' => {"publisher"=>"newpublisher"}},
{'$sort' => {'revision' => -1}},
{'$group' => {'_id' => '$name',
'revision' => {'$first' => '$revision'},
'id' => {'$first' => '$_id'},
'name' => {'$first' => '$name'},
'published_on' => {'$first' => '$published_on'},
'publisher' => {'$first' => '$publisher'}
}
},
{'$project' => {'_id' => '$id',
'revision' => '$revision',
'name' => '$name',
'published_on' => '$published_on',
'publisher' => '$publisher'
}
},
{'$skip' => 1},
{ '$limit' => 10 }
])
Since I need paginated result, I have applied skip and limit.
I have got this result
{"_id"=>{"$oid"=>"57f54a4483152940ad000001"}, "revision"=>1.1, "name"=>"book1", "published_on"=>"2016-10-05T18:45:24.436Z", "publisher"=>"newpublisher"},
{"_id"=>{"$oid"=>"57f5513f8315294116000000"}, "revision"=>2.0, "name"=>"learning", "published_on"=>"2016-10-05T19:15:11.342Z", "publisher"=>"newpublisher"}
Now I would like to sort by 'name' or 'published_on' columns. When I try to apply that on the result from above, I am getting error
Please advise
You didn't said what error you're getting, that would help. But you said
Now I would like to sort by 'name' or 'published_on' columns. When I try to apply that on the result from above, I am getting error
and by that I understand you added the sort after the limit. If you did that, it only sorts the skipped and limited results, in order to sort all results you must call the sort before skipping and limiting them, like:
Book.collection.aggregate([
{'$match' => {"publisher"=>"newpublisher"}},
{'$sort' => {'revision' => -1}},
{'$group' => {'_id' => '$name',
'revision' => {'$first' => '$revision'},
'id' => {'$first' => '$_id'},
'name' => {'$first' => '$name'},
'published_on' => {'$first' => '$published_on'},
'publisher' => {'$first' => '$publisher'}
}
},
{'$project' => {'_id' => '$id',
'revision' => '$revision',
'name' => '$name',
'published_on' => '$published_on',
'publisher' => '$publisher'
}
},
{'$sort' => {'name' => 1}},
{'$skip' => 1},
{'$limit' => 10 }
])
I added the documents you posted in a collection and ran this query and this is the result
[ { _id: 57f5513f8315294116000000,
revision: 2,
name: 'learning',
published_on: Wed Oct 05 2016 16:15:11 GMT-0300 (BRT),
publisher: 'newpublisher' },
{ _id: 57f54baa83152940c3000001,
revision: 1,
name: 'oldbook',
published_on: Wed Oct 05 2016 15:51:22 GMT-0300 (BRT),
publisher: 'newpublisher' } ]
And sorted by published_on:
{'$sort': {'published_on': 1}}
[ { _id: 57f54baa83152940c3000001,
revision: 1,
name: 'oldbook',
published_on: Wed Oct 05 2016 15:51:22 GMT-0300 (BRT),
publisher: 'newpublisher' },
{ _id: 57f5513f8315294116000000,
revision: 2,
name: 'learning',
published_on: Wed Oct 05 2016 16:15:11 GMT-0300 (BRT),
publisher: 'newpublisher' } ]

How to apply correctly $limit and $skip in subfields?

I'm starting with mongodb and I'm finding many difficulties with the following scheme.
{
"_id" : "AAA",
"events" : [
{
"event" : "001",
"time" : 1456823333
},
{
"event" : "002",
"time" : 1456828888
},
{
"event" : "003",
"time" : 1456825555
},...
]
}
I want to get the events sorted by date and apply limit and skip.
I'm using the following query:
$op = array(
array('$match' => array('_id' => $userId)),
array('$unwind' => '$events'),
array('$sort' => array('events.time' => -1)),
array('$group' => array('_id' => '$_id',
'events' => array('$push' => '$events')))
//,array('$project' => array('_id' => 1, 'events' => array('$events', 0, 3)))
//,array('$limit' => 4)
//,array('$skip' => 3)
);
$result= Mongo->aggregate('mycollection', $op);
I have tried everything to filter $project or $limit and $skip but none of it works.
How should I apply the limit and skyp conditions in events?
If I do not apply the conditions of "limit" above the result is ordered correctly.
Result:
{ "waitedMS":0,
"result":[
{
"_id":"AAA",
"events":[
{
"event":"002",
"time":1456828888,
},
{
"event":"003",
"time":1456825555,
},
{
"event":"001",
"time":1456823333,
},...
}
],
"ok":1
}
Order correctly but I can not limit the number of results for paging.

MongoDB Aggregation Framework

I have a document that's structured as follows:
{
'_id' => 'Star Wars',
'count' => 1234,
'spelling' => [ ( 'Star wars' => 10, 'Star Wars' => 15, 'sTaR WaRs' => 5) ]
}
I would like to get the top N documents (by descending count), but with only one one spelling per document (the one with the highest value). It there a way to do this with the aggregation framework?
I can easily get the top 10 results (using $sort and $limit). But how do I get only one spelling per each?
So for example, if I have the following three records:
{
'_id' => 'star_wars',
'count' => 1234,
'spelling' => [ ( 'Star wars' => 10, 'Star Wars' => 15, 'sTaR WaRs' => 5) ]
}
{
'_id' => 'willow',
'count' => 2211,
'spelling' => [ ( 'willow' => 300, 'Willow' => 550) ]
}
{
'_id' => 'indiana_jones',
'count' => 12,
'spelling' => [ ( 'indiana Jones' => 10, 'Indiana Jones' => 25, 'indiana jones' => 5) ]
}
And I ask for the top 2 results, I'll get:
{
'_id' => 'willow',
'count' => 2211,
'spelling' => 'Willow'
}
{
'_id' => 'star_wars',
'count' => 1234,
'spelling' => 'Star Wars'
}
(or something to this effect)
Thanks!
Your schema as designed would make using anything but a MapReduce difficult as you've used the keys of the object as values. So, I adjusted your schema to better match with MongoDB's capabilities (in JSON format as well for this example):
{
'_id' : 'star_wars',
'count' : 1234,
'spellings' : [
{ spelling: 'Star wars', total: 10},
{ spelling: 'Star Wars', total : 15},
{ spelling: 'sTaR WaRs', total : 5} ]
}
Note that it's now an array of objects with a specific key name, spelling, and a value for the total (I didn't know what that number actually represented, so I've called it total in my examples).
On to the aggregation:
db.so.aggregate([
{ $unwind: '$spellings' },
{ $project: {
'spelling' : '$spellings.spelling',
'total': '$spellings.total',
'count': '$count'
}
},
{ $sort : { total : -1 } },
{ $group : { _id : '$_id',
count: { $first: '$count' },
largest : { $first : '$total' },
spelling : { $first: '$spelling' }
}
}
])
Unwind all of the data so the aggregation pipeline can access the various values of the array
Flatten the data to include the key aspects needed by the pipeline. In this case, the specific spelling, the total, and the count.
Sort on the total, so that the last grouping can use $first
Then, group so that only the $first value for each _id is returned, and then also return the count which because of the way it was flattened for the pipeline, each temporary document will contain the count field.
Results:
[
{
"_id" : "star_wars",
"count" : 1234,
"largest" : 15,
"spelling" : "Star Wars"
},
{
"_id" : "indiana_jones",
"count" : 12,
"largest" : 25,
"spelling" : "Indiana Jones"
},
{
"_id" : "willow",
"count" : 2211,
"largest" : 550,
"spelling" : "Willow"
}
]

Perl and MongoDB: Inserting Array of Objects into Key-Value Pairs

I'd like to insert into my MongoDB using perl the following BSON structure:
{"name" : "BOB", "stuff" : [{"area1": [1,2,3,4,5]}, {"area2": [5,6,7,8,9]}]}
But have had a hard time finding a good example of this. I tried the following:
#!/usr/bin/perl
use MongoDB;
use MongoDB::Database;
use MongoDB::OID;
my $conn = MongoDB::Connection->new;
my $db = $conn->test;
my $users = $db->real_time10;
$users->insert
({
"name" => "BOB",
"stuff" =>
"area1" => [1,2,3,4,5],
"area2" => [5,6,7,8,9]
});
Which grossly outputs upon query in the mongo shell:
db.real_time10.find()
{ "_id" : ObjectId("4fc912fa000207ec08000000"), "ARRAY(0xa5bdd4)" : "area2", "A
RAY(0x2f2e844)" : null, "name" : "BOB", "stuff" : "area1" }
What is going on? Is there a simple way to do this?
My dream/desired output would be:
> db.real_time10.find()
{ "_id" : ObjectId("4fc912fa000207ec08000000"), "stuff" : {"area1" : [1,2,3,4,5],
"area2": [5,6,7,8,9]}, "name" : "BOB" }
Your missing your anonymous-array-constructor (square-brackets) in your example code - but including them in your BSON example. To get your desired output try:
$users->insert({
"name" => "BOB",
"stuff" => {
"area1" => [1,2,3,4,5],
"area2" => [5,6,7,8,9]
}
});
By excluding the array constructor it builds up a hash with the supplied array key, value pairs so it would be parsed as the following (which matches your data-dump):
{
"name" => "BOB",
"stuff" => "area1",
[1,2,3,4,5] => "area2",
[5,6,7,8,9] => undef
}
Note: an array-ref in scalar context will be seen as a string like "ARRAY(0x6052b8)"
Ah, it's this:
#!/usr/bin/perl
use MongoDB;
use MongoDB::Database;
use MongoDB::OID;
my $conn = MongoDB::Connection->new;
my $db = $conn->test;
my $users = $db->real_time10;
$users->insert({
"name" => "BOB",
"stuff" =>
{"area1" => [1,2,3,4,5],
"area2" => [5,6,7,8,9]}
});
This outputs:
{ "_id" : ObjectId("4fc91f110064e9d40b000000"), "name" : "BOB", "stuff" : { "are
a2" : [ 5, 6, 7, 8, 9 ], "area1" : [ 1, 2, 3, 4, 5 ] } }