mongoDB concatenate results - mongodb

Whats the best way to concatenate results in MongoDB? In particular the PHP driver? Do I need to use mapReduce?
In mySQL I would do something like this: SELECT CONCAT(fname,' ',lname) as name FROM users but I can't seem to find a simple way to do this in mongo.

In the PHP Driver
I recommend doing this in your application. Use PHP's concatenation features to add a "fullname" attribute/key to the user object/array. I'd stay away from map/reduce/finalize unless you need to do some database-side processing or selecting before returning the results. (And, before that, maybe look into where queries as well - http://www.mongodb.org/display/DOCS/Advanced+Queries.)
In the Shell
If this is a one-off query and you're doing it in the shell, there are two different (but related) ways to go about this.
Which one you use depends widely on if only want the concat-ed name or if you want the rest of the document to go with it. For instance, if you only want the name, you can do something like this:
> db.show_concat.find().forEach( function (o) { print(o.fname + ' ' + o.lname); } )
john smith
jane doe
Otherwise, if you want the other fields, you can do:
> db.show_concat.find().forEach( function (o) { o.full_name = o.fname + ' ' + o.lname; printjson(o); } )
{
"_id" : ObjectId("4cd6dabb5391d08d405bb0bb"),
"fname" : "john",
"lname" : "smith",
"full_name" : "john smith"
}
{
"_id" : ObjectId("4cd6daee5391d08d405bb0bc"),
"fname" : "jane",
"lname" : "doe",
"full_name" : "jane doe"
}

You can use aggregate, $project and $concat :
https://docs.mongodb.org/v3.0/reference/operator/aggregation/project/
https://docs.mongodb.org/manual/reference/operator/aggregation/concat/
It would be something like this :
db.show_concat.aggregate(
[
{ $project: { full_name: { $concat: [ "$fname", " - ", "$lname" ] } } }
]
)

Related

MongoDB query returning no results

I'm new to mongo and am trying to do a very simple query in this collection:
{
"_id" : ObjectId("gdrgrdgrdgdr"),
"administrators" : {
"-HGFsfes" : {
"name" : "Jose",
"phone" : NumberLong(124324)
},
"-HGFsfqs" : {
"name" : "Peter",
"phone" : "+43242342"
}
},
"countries" : {
"-dgfgrdg : {
"lang" : "en",
"name" : "Canada"
},
"-grdgrdg" : {
"lang" : "en",
"name" : "USA"
}
}
}
How do I make a query that returns the results of administrators with name like "%Jos%" for example.
What I did until now is this: db.getCollection('coll').find({ "administrators.name": /Jos/});
And variations of this. But every thing I tried returns zero results.
What am I doing wrong?
Thanks in advance!
Your mistake is that administrators is not an array, but an object with fields that are themselves objects with name field. Right query will be
{ "administrators.-HGFsfes.name": /Jos/}
Unfortunatelly this way you're only querying -HGFsfes name field, not other administrator name field.
To achieve what you want, the only thing to do is to replace administrators object by an array, so your document will look like this :
{
"administrators" : [
{
"id" : "-HGFsfes",
"name" : "Jose",
"phone" : 124324
},
{
"id" : "-HGFsfqs",
"name" : "Peter",
"phone" : "+43242342"
}
],
countries : ...
}
This way your query will work.
BUT it will return documents where at least one entry in administrators array has the matching name field. To return only administrator matching element, and not whole document, check this question and my answer for unwind/match/group aggregation pipeline.
You need to use query like this:
db.collection_name.find({})
So if your collection name is coll, then it would be:
db.coll.find({"administrators.-HGFsfes.name": /Jos/});
Look this for like query in mongo.
Also, try with regex pattern like this:
db.coll.find({"administrators..-HGFsfes.name": {"$regex":"Jos", "$options":"i"}}});
It will give you only one result because your data is not an array as below in screenshot:
If you want multiple results, then you need to restructure your data.
Ok, think i've found a better solution for you, with aggregation framework.
Run the following query on your current collection, will return you all administrators with name "LIKE" jos (case insensitive with i option) :
db.test1.aggregate(
[
{
$project: {
administrators:{ $objectToArray: "$administrators"}
}
},
{
$unwind: {
path : "$administrators"
}
},
{
$replaceRoot: {
newRoot:"$administrators"
}
},
{
$match: {
"v.name":/jos/i
}
},
]
);
Output
{
"k" : "-HGFsfes",
"v" : {
"name" : "Jose",
"phone" : NumberLong(124324)
}
}
"k" and "v" are coming from "$objectToArray" operator, you can add a $project stage to rename them (or discard if k value doesn't matter)
Not sure for Robomongo testing but in Studio 3T, formerly Robomongo, you can either copy/paste this query in Intellishell console, or copy/import in aggregation tab, (small icon 'paste from the clipboard').
Hope it helps.

Find by sub-documents field value with case insensitive

I know this is a bit of newb question but I'm having a hard time figuring out how to write a query to find some information. I have several documents (or orders) much like the one below and I am trying to see if there is any athlete with the name I place in my query.
How do I write a query to find all records where the athleteLastName = Doe (without case sensitivity)?
{
"_id" : ObjectId("57c9c885950f57b535892433"),
"userId" : "57c9c74a0b61b62f7e071e42",
"orderId" : "1000DX",
"updateAt" : ISODate("2016-09-02T18:44:21.656Z"),
"createAt" : ISODate("2016-09-02T18:44:21.656Z"),
"paymentsPlan" :
[
{
"_id" : ObjectId("57c9c885950f57b535892432"),
"customInfo" :
{
"formData" :
{
"athleteLastName" : "Doe",
"athleteFirstName" : "John",
"selectAttribute" : ""
}
}
}
]
}
You need to use dot notation to access the embedded documents and regex because you want case insensitive.
db.collection.find({'paymentsPlan.customInfo.formData.athleteLastName': /Doe/i}

Using $last on Mongo Aggregation Pipeline

I searched for similar questions but couldn't find any. Feel free to point me in their direction.
Say I have this data:
{ "_id" : ObjectId("5694c9eed4c65e923780f28e"), "name" : "foo1", "attr" : "foo" }
{ "_id" : ObjectId("5694ca3ad4c65e923780f290"), "name" : "foo2", "attr" : "foo" }
{ "_id" : ObjectId("5694ca47d4c65e923780f294"), "name" : "bar1", "attr" : "bar" }
{ "_id" : ObjectId("5694ca53d4c65e923780f296"), "name" : "bar2", "attr" : "bar" }
If I want to get the latest record for each attribute group, I can do this:
> db.content.aggregate({$group: {_id: '$attr', name: {$last: '$name'}}})
{ "_id" : "bar", "name" : "bar2" }
{ "_id" : "foo", "name" : "foo2" }
I would like to have my data grouped by attr and then sorted by _id so that only the latest record remains in each group, and that's how I can achieve this. BUT I need a way to avoid naming all the fields that I want in the result (in this example "name") because in my real use case they are not known ahead.
So, is there a way to achieve this, but without having to explicitly name each field using $last and just taking all fields instead? Of course, I would sort my data prior to grouping and I just need to somehow tell Mongo "take all values from the latest one".
See some possible options here:
Do multiple find().sort() queries for each of the attr values you
want to search.
Grab the original _id of the $last doc, then do a findOne() for each of those values (this is the more extensible option).
Use the $$ROOT system variable as shown here.
This wouldn't be the quickest operation, but I assume you're using this more for analytics, not in response to a user behavior.
Edited to add slouc's example posted in comments:
db.content.aggregate({$group: {_id: '$attr', lastItem: { $last: "$$ROOT" }}}).

merge (combine) fields from a query

This is the result of my query :
{
id : 1 ,
name : "Richard" ,
last_name : "stallman",
sex : "Mr"
}
But I need something like this :
{
id : 1 ,
details : "Mr Richard - stallman"
}
How can I fix it?
You can use aggregation for this:
db.COLLECTION.aggregate([{$project: {id: "$id", details: {$concat: ["$sex", " ", "$name", " ", "$last_name"]} }}])
See the documentation at http://docs.mongodb.org/manual/reference/operator/aggregation/ and http://docs.mongodb.org/manual/reference/operator/aggregation/concat/ .
I am assuming you are querying a database?
You would have to query for the data, store it in variables, and then generate your new data.
i.e.
$details=row['sex']." ".row['firstname']." - ".rpw['lastname'];

query the value of a sub document in mongodb

I'm using the Java driver withe document that looks like this (a real test example):
{
"_id" : ObjectId("5207fe359b88bfa6f90a82b0"),
"meta_id" : "d6eb1b13-50c7-473f-8348-b5a638a542a0",
"name" : "Fake Name Inc.",
"created" : ISODate("2013-08-11T21:12:21.533Z"),
"members" : {
"5207fe359b88bfa6f90a82af" : [
"Admin",
"User"
]
}
}
I want to select the string array at the path "members.5207fe359b88bfa6f90a82af" (which is a list of roles).
I'm at a loss as to how to do that. It looks like a projection would work here, but I'm new enough to Mongo that the way the projection is written is not obvious.
I can of course load the whole object or maybe even just the "members" field, but I think I should be able to select just exactly the data I'm after.
So, does anyone have an idea of how such a query would be written?
Note: This question suggests that maybe I need to change the structure of the document to make things easier: MongoDB - Query by sub-tree
You can use dot notation in the projection parameter of find to do this. In the shell:
db.test.find(
{_id : ObjectId("5207fe359b88bfa6f90a82b0")},
{'members.5207fe359b88bfa6f90a82af': 1, _id: 0})
Returns:
{
"members": {
"5207fe359b88bfa6f90a82af": [
"Admin",
"User"
]
}
}