Mongodb findAndModify embedded document - how do you know which one you've modified? - mongodb

findAndModify in mongodb is great, but I am having a little trouble knowing which embedded document I modified.
Here is an example where a Post embeds_many Comments. (I'm using Mongoid ORM but the question is generic to any MongoDB setup).
begin
p = Post.asc(id).where(comments: { '$elemMatch' => {reserved: false} }).find_and_modify({'$set' => {'comments.$.reserved' => true}}, {new: true}
# now i need to find which comment I just reserved
c = p.comments.select{|c| c.reserved }.first
...
ensure
c.update_attribute :reserved, false
end
Ok this sort of works, but if I have multiple processes running this simultaneously my select could choose a comment that another process had reserved (race condition).
This is the closest I have for now (reserving by process id):
begin
p = Post.asc(id).where(comments: { '$elemMatch' => {reserved: nil} }).find_and_modify({'$set' => {'comments.$.reserved' => Process.pid}}, {new: true}
# now i need to find which comment I just reserved
c = p.comments.select{|c| c.reserved == Process.pid }.first
...
ensure
c.update_attribute :reserved, nil
end
Which seems to work. Is this the best way to do this or is there a better pattern?

Was able to solve it by generating a SecureRandom.hex and setting this on the embedded document with find_and_modify. Then you can loop through the embedded documents and see which one has your matching hex, to see which one you are working with.

Related

MongoDB and NextJS: Find a certain data matches regardless if uppercase or lowercase

The goal of this code is to display the current numbers of death, recoveries and critical for covid 19 around the world.
The search function codes are as follows:
const search = (e) => {
e.preventDefault() //to avoid page redirection
const countryMatch = countryCollection.find(country => country.country_name === targetCountry)
if (!countryMatch || countryMatch === null|| countryMatch === 'undefined') {
alert("Country Does Not Exist, use another name.")
setName("")
setTargetCountry("")
} else {
setName(countryMatch.country_name)
setDeathCount(toNum(countryMatch.deaths))
setCriticalCount(toNum(countryMatch.serious_critical))
setRecoveryCount(toNum(countryMatch.total_recovered))
}
}
Our task is to find a country regardless if its in upper or lower case. Eg: Malaysia vs malaysia.
REGULAR EXPRESSION
What you need is regular expression or RegExp. MongoDb supports regular expression for your searches.
In Your case it can be something like
countryCollections.find({'country':new RegExp(countryName,flag)},callback)
flag determines how you want to search
for case insensitive search use 'i'
More about RegExp can be found on mongoDB docs https://docs.mongodb.com/manual/reference/operator/query/regex/
According to your usage of MongoDB, I would say, that this case is an excellent case to using text indexes.
Here is an example for you:
Schema.index(
// making field available for $text search and $meta sorting
{
'field': 'text',
'embedDoc.field': 'text',
},
{
//options of index
weights: // weight for each field
{
'field': 2,
'embedDoc.field': 1,
},
name: 'Countries', // Index Name for Mongo Compass and .explain debug
})
I guess you should try that. It will solve all your potential problems with text search. Like ' or diacritic symbols in searching, lower-uppercase and so on. But please, check the documentation of text indexes, before implementing them, it's quite sensitive and flexible for any cases. But there is no universal silver bullet.

Perl module for Elastisearch Percolator

I'm trying to use the Elasticsearch Percolator with perl and I have found this cool module.
The Percolation methods are listed here
As far as I can tell they're just read methods, hence it is only possible to read the queries index and see if a query already exists, count the queries matched, etc.
Unless I'm missing something it is not possible to add queries via the Percolator interface, so what I did is use the normal method to create a document against the .percolator index as follow:
my $e = Search::Elasticsearch->new( nodes => 'localhost:9200' );
$e->create(
index => 'my_index',
type => '.percolator',
id => $max_idx,
body => {
query => {
match => {
...whatever the query is....
},
},
},
);
Is that the best way of adding a query to the percolator index via the perl module ?
Thanks!
As per DrTech answer the code I posted looks to be the correct way of doing it.

MongoDB: Perform a text-search in a document field (using high-level API)

It may be related to this question
Basic GROUP BY statement using OPA MongoDB high level API.
I want to be able to retrieve a list of documents which "name" field value contains a given string.
Here's my documents list :
{name: "Charles-Hugo"},
{name: "Jean Pierre"},
{name: "Pierre Dupont"},
I want to be able to only retrieve documents which name contains the "Pierre" string: Jean Pierre, and Pierre Dupont.
I know this isn't possible with the MongoDB high-level API.
I've looked in the low-level API functions but I don't know what's the easiest way to retrieve my documents in safe Opa type.
Also I'd like to add skip/limit options to my query.
Any idea ?
The DbGen automation mechanism in Opa has support for this:
DbSet.iterator(/path/data[name =~ pattern])
As #Henri pointed out there is regular expression searching support in Opa since commit [enhance] DbGen: add case insensitive regex operator =~ what is very nice.
Mind that it is using $regex operator, not the full-text index and it may result with some performance loss :( As MongoDB documentation says $regex operator uses indexes in limited way - only for prefix search: pattern ^Jean. Searching for Jean anywhere in text will require full scan.
Personally, I am using full-text index feature of Mongo with Opa's "low-level" API for the $text command like this:
function list({float score, Article.id id}) textSearch(string query) {
function onfailure(failure) {
cat.error("textSearch({{~query}}): {failure}");
[];
}
function onsuccess(success) {
function aux(~{name,value}) {
name == "results";
}
match (List.filter(aux, success)) {
| [] :
// `results` field not found - error
onfailure(success);
| results:
cat.debug("textSearch({~{query}}): {results}");
function ({~score, obj: ~{id}}) {
~{score, id}
}
|> List.map(_, Bson.doc2opa(results) ? []);
}
}
opts = [H.str("search", query), H.doc("project", [H.i32("_id",0), H.i32("id",1)])];
// { search: query, project: {_id:0, id:1}, }
// |> Bson.opa2doc
outcome = MongoCommands.simple_str_command_opts(ll_db, db_name, "text", coll_name, opts);
MongoCommon.outcome_map(outcome, onsuccess, onfailure)
}
Feature is available in Mongo since 2.4 as experimental (you have to turn it on by special configuration option) and in 2.6 as stable (turned on by default).

Using findOne in mongodb to get element with max id

I am trying to retrieve one element from a mongo collection, the one with the greatest _id field. I know this can be done by querying:
db.collection.find().sort({_id: -1}).limit(1)
But it kind of seems unelegant and I was wondering whether there is a way to get that specific element using findOne()
Note: I want to do this because, from what I've read in ObjectId, the first bytes correspond to the miliseconds since the Epoch and thus, the last element being inserted will have the greatest _id. Is there any other way to retrieve the last element inserted in a collection?
You should use find, like you already are, and not aggregation which will be slower since it needs to scan all the values of _id fields to figure out the max.
As comments pointed out there is no difference between using find() and findOne() - functionally or elegance-wise. In fact, findOne in the shell (and in the drivers which implement it) is defined in terms of find (with limit -1 and with pretty print in the shell).
If you really want to do the equivalent of
db.collection.find().sort({_id:-1}).limit(1).pretty()
as findOne you can do it with this syntax:
db.collection.findOne({$query:{},$orderby:{_id:-1}})
You can get max _id using aggregation of mongodb. Find and sort may overkill's.
db.myCollection.aggregate({
$group: {
_id: '',
last: {
$max: "$_id"
}
}
});
with PHP driver (mongodb)
using findOne()
$filter=[];
$options = ['sort' => ['_id' => -1]]; // -1 is for DESC
$result = $collection->findOne(filter, $options);
$maxAge = $result['age']
import pymongo
tonystark = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = tonystark["tonystark_db"]
savings = mydb["customers"]
x = savings.find().sort("_id")
for s in x:
print(s)
$maxId="";
$Cursor =$collection->find();
foreach($cursor as $document) {
$maxid =max($arr=(array($document['id'])));
}
print_r($maxid+1);

mongoDB: unique index on a repeated value

So i'm pretty new to mongoDb so i figure this could be a misunderstanding on general usage. so bear with me.
I have a document schema I'm working with as such
{
name: "bob",
email: "bob#gmail.com",
logins: [
{ u: 'a', p: 'b', public_id: '123' },
{ u: 'x', p: 'y', public_id: 'abc' }
]
}
My Problem is that i need to ensure that the public ids are unique within a document and collection,
Furthermore there are some existing records being migrated from a mySQL DB that dont have records, and will therefore all be replaced by null values in mongo.
I figure its either an index
db.users.ensureIndex({logins.public_id: 1}, {unique: true});
which isn't working because of the missing keys and is throwing a E11000 duplicate key error index:
or this is a more fundamental schema problem in that I shouldn't be nesting objects in an array structure like that. In which case, what? a seperate collection for the user_logins??? which seems to go against the idea of an embedded document.
If you expect u and p to have always the same values on each insert (as in your example snippet), you might want to use the $addToSet operator on inserts to ensure the uniqueness of your public_id field. Otherwise I think it's quite difficult to make them unique across a whole collection not working with external maintenance or js functions.
If not, I would possibly store them in their own collection and use the public_id as _id field to ensure their cross-document uniqueness inside a collection. Maybe that would contradict the idea of embedded docs in a doc database, but according to different requirements I think that's negligible.
Furthermore there are some existing records being migrated from a mySQL DB that dont have records, and will therefore all be replaced by null values in mongo.
So you want to apply a unique index on a data set that's not truly unique. I think this is just a modeling problem.
If logins.public_id is null that's going to violate your uniqueness constraint, then just don't write it at all:
{
logins: [
{ u: 'a', p: 'b' },
{ u: 'x', p: 'y' }
]
}
Thanks all.
In the end I opted to seperate this into 2 collections, one for users and one for logins.
users this looked a little like..
userDocument = {
...
logins: [
DBRef('loginsCollection', loginDocument._id),
DBRef('loginsCollection', loginDocument2._id),
]
}
loginDocument = {
...
user: new DBRef('userCollection', userDocument ._id)
}
Although not what i was originally after (a single collection) It is working niocely and by utilising the MongoId uniquness there is a constraint now built in at a database level and not implemented at the application level.