This question already has answers here:
Count Elements SubDocument that match a given criterion
(2 answers)
Closed 5 years ago.
I've the problem with php and MongoDB. Here's my document:
"_id" : ObjectId("58d7815f387e76880c000000"),
"receiver" : "Katty",
"chat" : [
{
"sender" : "jhon",
"date" : ISODate("2017-03-26T08:53:55Z"),
"message" : "Who are you?"
"status" : "sent"
},
{
"sender" : "jhon",
"date" : ISODate("2017-03-26T08:53:55Z"),
"message" : "What do you want?"
"status" : "pending"
}
{
"sender" : "jhon",
"date" : ISODate("2017-03-26T08:53:55Z"),
"message" : "Hah ?"
"status" : "pending"
}
]
And here's my php program:
<?php
$conn = new Mongo();
$db = $conn->selectDB('basarnas');
$query = $db->informasi_bencana;
$nosql = array("_id"=> new MongoId($id), "chat.status"=>"pending");
$result = $query->find($nosql);
$beritasar = $result->count();
$total = $beritasar;
echo "status pending = ".$total;
?>
And the result is
status pending = 1
And I want the result is
status pending = 2
How to count of embedded document when it has status = "pending" ?
You can unwind the embedded document and then match the status then using groupby count all the documents.
db.collection.aggregate([
{ "$unwind": "$chat"},
{$match:{"chat.status": "pending"}},
{ "$group":{"_id":null, count: {$sum:1}}}
])
OR
Simplify fetch all the documents and count the length of the array
db.collection.aggregate([
{ "$unwind": "$chat"},
{$match:{"chat.status": "pending"}},
])
Related
I'm using elasticsearch in my laravel-app and I'm trying to use the range-query. I have an array of companies, which in different periods have different amounts of employees, but I'm only interested in the newest period, which in this case means the last item of the employees array.
so, basically the array looks like this:
"company" => [
"name" => "some company",
"company_number" => "1234567",
"status" => "normal",
"employees" => [
"period_1" => [
"amount" => 10
],
"period_2" => [
"amount" => 15
],
"period_3" => [
"amount" => 24
],
etc etc...
]
]
so, in the frontend, you can enter a minimum and a maximum value to search for companies with certain amounts of employees. In my Controller, I then do this:
"query":{
"bool": {
"should" : [
{ "match" : { "company.status" : "normal" },
{
"range": {
"company.employees": { // I WANT THE LAST ITEM FROM THIS ARRAY
"gte": "'. $min . '",
"lt" : "'.$max .'"
}
}
}
]
}
}
This basically works, but of course, doesn't give me the last record of the employees array.
How can I solve this? Please help...
UPDATE
ok so now I added the code which was suggested:
"query": {
"bool": {
"should" : [
{ "match" : { "company.status" : "normal" },
{
"range": {
"company.employees": { // I WANT THE LAST ITEM FROM THIS ARRAY
"gte": "'. $min . '",
"lt" : "'.$max .'"
}
}
}
]
},
"script": {
"source": """
def period_keys = new ArrayList(ctx._source.company.employees.keySet());
Collections.sort(period_keys);
Collections.reverse(period_keys);
def latest_period = period_keys[0];
def latest_amount = ctx._source.company.employees[latest_period].amount;
ctx._source.company.current_employees = ["period": latest_period, "amount": latest_amount];
"""
}
}
}
But I get the error: Unexpected character ('{' (code 123)): was expecting comma to separate Object entries...
Since I'm still learning I must say, I have no clue what is going on and error messaging from Elasticsearch is horrible.
Anyway, does anyone have a clue? Thanks in advance
Looking up something like this at runtime is quite difficult and under-optimized. Here's an alternative.
I'm assuming a given company's employee counts don't change that often -- meaning when they do change (i.e. you update that document), you can run the following _update_by_query script to get the latest period's employee info and save it on the company level while leaving the employee section untouched:
POST companies_index/_update_by_query
{
"query": {
"match_all": {}
},
"script": {
"source": """
def period_keys = new ArrayList(ctx._source.company.employees.keySet());
Collections.sort(period_keys);
Collections.reverse(period_keys);
def latest_period = period_keys[0];
def latest_amount = ctx._source.company.employees[latest_period].amount;
ctx._source.company.current_employees = ['period': latest_period, 'amount': latest_amount];
"""
}
}
One-liner:
POST companies_index/_update_by_query
{"query":{"match_all":{}},"script":{"source":" def period_keys = new ArrayList(ctx._source.company.employees.keySet());\n Collections.sort(period_keys);\n Collections.reverse(period_keys);\n \n def latest_period = period_keys[0];\n def latest_amount = ctx._source.company.employees[latest_period].amount;\n \n ctx._source.company.current_employees = ['period': latest_period, 'amount': latest_amount];"}}
Note that when the above query is empty, the script will apply to all docs in your index. But of course you could limit it to one company only.
After that call your documents will look like this:
{
"company" : {
"company_number" : "1234567",
"name" : "some company",
"current_employees" : { <---
"period" : "period_3",
"amount" : 24
},
"employees" : {
...
},
...
}
}
and the range query from above becomes a piece of cake:
...
"range": {
"company.current_employees.amount": { <--
"gte": "'. $min . '",
"lt" : "'.$max .'"
}
...
BTW I also assumed that the period keys can be sorted alphabetically but if they contain dates, the script will require an adjustment in the form of a date parsing comparator.
This question already has answers here:
How to Match on Joined Collections Using Laravel and MongoDB?
(1 answer)
How to join to two additional collections with conditions
(1 answer)
Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size
(2 answers)
Closed 4 years ago.
I have 2 collections in mongoDB namely product and author . Now the product collection has an object called the detail which has the title and other details whereas the author table has details regarding the author . Now i am trying to make a search that finds both title and author .. For eg: if the title is matched the details of that product should be listed and if the author is matched then again the details of that product should be shown. The author_id is a foreign_key in the product collection and the _id is a primary key in the author collection.
Below is my collection
The Product Collection
{
"_id" : ObjectId("5af2bd44a003533a8abf4e56"),
"product_type_id" : "5ae834807ae0d9538e45ab45",
"date_added" : "2018-03-12 12-3-54",
"status" : 1,
"detail" : {
"title" : "The random title",
"author_id" : "5af2c401a003533a8abf4e57",
"test_id" : 12345,
"description" : "I have a mongoDB collection with f rows",
"mrp" : 200,
"binding" : "sd",
"language" : "English",
"isbn" : NumberLong("9788700631625"),
"isbn_10" : 747532745,
"ean" : 897655,
"pages" : 200
}
}
This is the author collection
{
"_id" : ObjectId("5af2c401a003533a8abf4e57"),
"name" : "Dan Brown",
"test_id" : 12345
}
So far i have to search on the collections independently . But not together
For author search
db.author.aggregate
([{
"$match":
{
"name":"Dan Brown"
}
},
{
"$lookup":
{
"from": "product",
"localField": "test_id",
"foreignField": "detail.test_id",
"as": "users"
}
}
]
).pretty();
And title has been accessed using the normal find() method
EDIT 1:
So this is what i have tried so far. Is this the right approach . Btw it's still not working though
db.product.aggregate
(
[
{
"$match":
{
detail.title : 'The random title'
}
},
{
"$lookup":
{
"from": "author",
"localField": "test_id",
"foreignField": "detail.test_id",
"as": "tb2"
}
},
{
"$unwind": "$tb2"
},
{
"$match":
{
"tb2.name": "agent"
}
}
]
);
This question already has answers here:
How do I do a "NOT IN" query in Mongo?
(4 answers)
Aggregation filter after $lookup
(1 answer)
Closed 4 years ago.
I am new in MongoDB so facing issue in making join query in mongo please help
I have two collection "user" and "user_login_details"
Previously I have below query to fetch record from single collection
$data = $this->mongodb->select()->where(array('instituteId' => $instituteId))->where_not_in('type', array($instituteAdmin))->get('user');
Now I want to fetch records from two collection "user" have single row and for that row multiple entries of login detail present in "user_login_details" collection.
I try with aggregate as below
db.user.aggregate([
{$match: { instituteId: '5954ee6dcf2169d50d51b88d'} },
{
$lookup:
{
from: "user_login_details",
localField: "_id",
foreignField: "user_id",
as: "user_login_details"
}
}
]).pretty()
But need one more condition as per my first query in php
->where_not_in('type', array($instituteAdmin))
How I can add this in aggregate query.
Also above query will return data with multiple login details as below.
{
"_id" : ObjectId("5954ee6dcf2169d50d51b88e"),
"email" : "abc#gmail.com",
"instituteId": "5954ee6dcf2169d50d51b88d",
"type": "Institute Admin"
"user_name" : "SA-011",
"religion" : "",
"caste" : "",
"bloodGroup" : "",
"groups" : "",
"reset_password" : NumberLong(1),
"aws_arn" : "",
"user_login_details" : [
{
"_id" : ObjectId("5b069955df05aeda0f8b4568"),
"user_id" : ObjectId("5954ee6dcf2169d50d51b88e"),
"ip" : "::1",
"my_session_id" : "3199d6fd6ecfef1ec63bc1760115d116",
"login_time" : ISODate("2018-05-24T10:52:05Z"),
"logout_time" : ISODate("2018-05-24T11:04:47Z")
},
{
"_id" : ObjectId("5b069c52df05ae6e358b4567"),
"user_id" : ObjectId("5954ee6dcf2169d50d51b88e"),
"ip" : "::1",
"my_session_id" : "b2d33ea8bc7cc3bb8090fc1dd15f7371",
"login_time" : ISODate("2018-05-24T11:04:50Z"),
"logout_time" : ""
}
]
}
I need only latest record from "user_login_details" collection.
Please help by creating query in PHP as I used "CodeIgniter MongoDB Active Record Library".
Thanks in advance.
as mysql query: select sum(adet) from stok where isbn=$isbn. I want to make this query as a mongodb query but I couldn't make. I've read some code examples but I didn't understand how it works.
I need some help.
MongoDB's query language doesn't give us the ability to perform summations, but we can utilize its aggregation framework to do so.
Given the following sample data:
> db.stok.insert({ isbn:"foo", adet: 5 })
> db.stok.insert({ isbn:"foo", adet: 3 })
> db.stok.insert({ isbn:"foo", adet: 1 })
> db.stok.insert({ isbn:"bar", adet: 2 })
> db.stok.insert({ isbn:"bar", adet: 4 })
> db.stok.find()
{ "_id" : ObjectId("51cde94037a16345b5316987"), "isbn" : "foo", "adet" : 5 }
{ "_id" : ObjectId("51cde94437a16345b5316988"), "isbn" : "foo", "adet" : 3 }
{ "_id" : ObjectId("51cde94b37a16345b5316989"), "isbn" : "foo", "adet" : 1 }
{ "_id" : ObjectId("51cde95337a16345b531698a"), "isbn" : "bar", "adet" : 2 }
{ "_id" : ObjectId("51cde95637a16345b531698b"), "isbn" : "bar", "adet" : 4 }
We could summate all of the adet values for "foo" isbn values with the following pipeline (i.e. sequence of aggregation operators):
> db.stok.aggregate(
... { $match: { isbn: "foo"}},
... { $group: { _id: 1, sum: { $sum: "$adet" }}}
... );
{ "result" : [ { "_id" : 1, "sum" : 9 } ], "ok" : 1 }
Here, we're narrowing the considered documents by the isbn field with $match, and then using $group to collect all of those into a single bucket and aggregate the adet field with a summation.
We could also compute sums for each distinct isbn value by using a non-constant _id in our grouping:
> db.stok.aggregate({ $group: { _id: "$isbn", sum: { $sum: "$adet" }}});
{
"result" : [
{
"_id" : "bar",
"sum" : 6
},
{
"_id" : "foo",
"sum" : 9
}
],
"ok" : 1
}
The above examples use the JS shell, but you can execute aggregation queries in PHP with either the MongoCollection::aggregate() helper method, or by directly invoking the aggregate command with MongoDB::command().
db.projects.find()
{
"_id" : ObjectId("508d4028e60d8c2154a2eb36"),
"date" : NumberLong(1351432927),
"deadline" : "2012-12-31",
"payment" : "100500",
"title" : "Project",
"userid" : ObjectId("5083677d2f5c849509c3aae6")
}
All right, but in PHP print_r($find_result) doesn't show the key "date" at all!
Why? And how I can extract value from NumberLong() in PHP?