Select unique fields from array using MongoDB - mongodb

I have a MongoDB structure which currently looks like this:
[campaigns] => Array (
[0] => Array (
[campaign_id] => 4e8cba7a0b7aabea08000006
[short_code] => IHEQnP
[users] => Array (
)
)
[1] => Array (
[campaign_id] => 4e8ccf7c0b7aabe508000007
[short_code] => QLU_IY
[users] => Array (
)
)
)
What I would like to be able to do, is search for the short code, and just have the relevant array returned. I initially tried:
db.users.find({'campaigns.short_code':'IHEQnP'}, {'campaigns.campaign_id':1})
However that returns all the arrays, as opposed to just the one (or field) that I want.
Is there a way in Mongo to get the correct array (or even field associated with the array)? Or is that something I would have to do on the server? Am using the lithium framework to retrieve the results (in case it helps).
Thanks in advance :)
Dan

When you use a criteria like campaigns.short_code you are still searching the collection, the campaigns is just a property of a document, your find returns documents.
So given this structure you can not achieve what you want directly by a query.
Arrays in MongoDb can be sliced, but not sorted:
db.users.find({}, {campaigns: { $slice : 1}})
This would give you the first campaign, but as you cant sort it so IHEQnP is at top its of no help in this situation.
Read more here.
You could however filter this quite simple in Lithium after retrieving the full document:
$id = 'id to match against';
$result = $user->campaigns->find(function($model) use ($id) {
return $model->campaign_id === $id
});
See docs for Entity::find here
My solution would be to keep it in User if the amount of campaigns is low (fast to sort and filter in PHP as long as document size isnt too big).
If this is expected to grow then look at moving it to its own model/collection or re-think how you modelled your data.

Related

how to query to a collection with array in mongodb

I Have An Array With 4 Object Items I want query to my collection and return 4 items that have this uid's...
myArray = > [{uid : 'test'},{uid : 'test2'},{uid : 'test3'},{uid : 'test4'}]
ProductCollection.find({????},(err,result)=>{})
NOTE : I dont want use any loop
I dont want use any loop
I will assume that's related to query the DB several times, one for each uid value.
Anyway, you can go to the database once to filter elements that match an array of values, like your case, using MongoDB's $in operator.
But you would have to format the uid values to an array of the values themselves instead of the array of objets, this can be accomplished with a simple .map call (don't know if you will consider this a loop) to get the filter value in the correct format.
var uids = myArray.map((item) => item.uid })
// ['test', 'test2', 'test3', 'test4']
And after that you can query your DB with this uids values
ProductCollection.find({'uid': {'$in': uids} },(err,result)=>{})
(Assuming 'uid' it the property you have in your ProductCollection that you are trying to filter by)

How can I sort by multiple fields in mongodb with Perl?

How can I get multiple sort in MongoDB with Perl?
My current approach looks something like this:
my $sort = {"is_instock" => -1, "ua" => 1};
my $resultSet = $collection
->find({moderated => 1, markers => {'$all'=>$obj->{markers}}})
->sort($sort)
->limit(25);
#{$result} = $resultSet->all;
But, i got array sorted by one field(ua). What i did wrong?
The basic problem here is that a "hash" in Perl is ordered by "key" by default. In order to get the "order of insertion" you need to use Tie::IxHash as follows:
use Tie::IxHash;
my %sort;
tie ( %sort, 'Tie::IxHash' );
my $sort = \%sort;
$sort = { "is_instock" => -1, "ua" => 1 };
Then when you use this in your MongoDB query, the keys are considered in the order you inserted them, rather than their lexcial order.
It should have been orderd that way anyhow since the keys are in lexical order, but I suggest you did something wrong and you need to be aware of the insertion order anyway.
The otherwise reason is that "in_stock" does not exist, or is not the true path name to the field. You need to specifiy the full path to the field with "dot notation" otherwise the path is invalid.

Mongoid where greater than sum of two fields

Hi I'm using mongoid (mongodb) to go a greater than criteria:
Account.where(:field1.gt => 10)
But I was wondering if it was possible to do a criteria where the sum of two fields was greater than some number. Maybe something like this (but doesn't seem to work):
Account.where(:'field1 + field2'.gt => 10)
Maybe some embedded javascript is needed? Thanks!
I'd recommend using the Mongoid 3 syntax as suggested by Piotr, but if you want to make this much more performant, at the expense of some storage overhead, you could try something like this:
class Account
# ...
field :field1, :type => Integer
field :field2, :type => Integer
field :field3, :type => Integer, default -> { field1 + field2 }
index({ field3: 1 }, { name: "field3" })
before_save :update_field3
private
def update_field3
self.field3 = field1 + field2
end
end
Then your query would look more like:
Account.where(:field3.gte => 10)
Notice the callback to update field3 when the document changes. Also added an index for it.
You can use MongoDB's javascript query syntax.
So you can do something like:
Account.collection.find("$where" => '(this.field1 + thist.field2) > 10')
Or in Mongoid 3 the following will work as well
Account.where('(this.field1 + thist.field2) > 10')
As Sammaye mentioned in the comment it introduces the performance penealty since javascript has to be executed for every document individually. If you don't use that query that often then it's ok. But if you do I would recommend adding another field that would be the aggregation of field1 and field2 and then base the query on that field.

Zend_Db_Adapter_Mysqli::fetchAssoc() I don't want primary keys as array indexes!

According to ZF documentation when using fetchAssoc() the first column in the result set must contain unique values, or else rows with duplicate values in the first column will overwrite previous data.
I don't want this, I want my array to be indexed 0,1,2,3... I don't need rows to be unique because I won't modify them and won't save them back to the DB.
According to ZF documentation fetchAll() (when using the default fetch mode, which is in fact FETCH_ASSOC) is equivalent to fetchAssoc(). BUT IT'S NOT.
I've used print_r()function to reveal the truth.
print_r($db->fetchAll('select col1, col2 from table'));
prints
Array
(
[0] => Array
(
[col1] => 1
[col2] => 2
)
)
So:
fetchAll() is what I wanted.
There's a bug in ZF documentation
From http://framework.zend.com/manual/1.11/en/zend.db.adapter.html
The fetchAssoc() method returns data in an array of associative arrays, regardless of what value you have set for the fetch mode, **using the first column as the array index**.
So if you put
$result = $db->fetchAssoc(
'SELECT some_column, other_column FROM table'
);
you'll have as result an array like this
$result['some_column']['other_column']

Get table alias from Zend_Db_Table_Select

I'm working on an Active Record pattern (similar to RoR/Cake) for my Zend Framework library. My question is this: How do I figure out whether a select object is using an alias for a table or not?
$select->from(array("c" => "categories"));
vs.
$select->from("categories");
and I pass this to a "fetch" function which adds additional joins and whatnot to get the row relationships automatically...I want to add some custom sql; either "c.id" or "categories.id" based on how the user used the "from" method.
I know I can use
$parts = $select->getPart(Zend_Db_Select::FROM);
to get the from data as an array, and the table name or alias seems to be in "slot" 0 of said array. Will the table name or alias always be in slot zero? i.e. can I reliably use:
$tableNameOrAlias = $parts[0];
Sorry if this is convolute but hope you can help! :)
Logically, I would think that's how it should work. To be on the safe side, build a few dummy queries using a Select() and dump the part array using print_r or such.
I just performed this test, the alias is the array key, it is not a zero-based numeric array:
$select = $this->db->select()->from(array("c" => "categories","d" => "dummies"));
$parts = $select->getPart(Zend_Db_Select::FROM);
echo '<pre>';
print_r($parts);
echo '</pre>';
Output:
Array
(
[c] => Array
(
[joinType] => inner join
[schema] =>
[tableName] => categories
[joinCondition] =>
)
)
So you would need to reference it as $part["c"]