Mongo search for value of key in nested array [duplicate] - javascript

This question already has answers here:
How to query nested objects?
(3 answers)
Closed 6 years ago.
Here's my document:
"_id" : "dAWcFHJzDPJ2XT9Sh",
"createdAt" : ISODate("2016-04-22T18:03:47.761Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$NYf53o/Uu8PvHPsGllRGA.WLbVpspNM4jk/6FtCzZLW.70.uQ2HXe"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2016-04-22T18:03:47.771Z"),
"hashedToken" : "dECxxuV/QyU2AU+/Zcrqc2Ftq64ZTrdHj5mN/rTGrxU="
}
]
}
},
"emails" : [
{
"address" : "Adammoisa#gmail.com",
"verified" : false
}
],
"profile" : {
"first_name" : "Adam",
"last_name" : "Moisa"
}
I want to search for an email in emails[i]address
Here's what I've tried (I'm using Meteor; Meteor.users.find({}).fetch() returns all users in database as objects formatted like above):
Meteor.users.find({"emails[0]address": "Adam"}).fetch();
I want that to return the above object as "Adam" is an email in emails[0]address
Thanks!

No need to specify the index.
Meteor.users.find({"emails.address": "Adam"}).fetch();

The index doesn't need to be specified, so you can do this:
Meteor.users.find({"emails.address": "Adam"}).fetch();
However if you do want to use a specific index, do this:
Meteor.users.find({"emails[0].address": "Adam"}).fetch();

Figured it out:
Meteor.users.find({"emails.address": "Adammoisa#gmail.com"}).fetch();
You can just do emails.address and it will match any address key with your value from any array in emails.
(Used regex from searchSource to make it work with anything that matches:
function buildRegExp(searchText) {
// this is a dumb implementation
var parts = searchText.trim().split(/[ \-\:]+/);
return new RegExp("(" + parts.join('|') + ")", "ig");
}

Related

Mongo script to update an object if its a certain value

I am trying to update a document in Mongo if it has a certain value within a field.
{
"_id" : ObjectId("myObject"),
"source" : "BW",
"sourceTableName" : "myTable",
"tableName" : "tier",
"type" : "main",
"fieldMappings" : [
{
"sourceField" : "tier_id", <~~~~ If this is "tier_id", I want to change it to "trmls_id"
"reportingField" : "bw_id",
"type" : "integer",
"defaultMap" : {
"shouldErrorOnConvert" : false
}
}
]
}
I tried something like
db.getCollection('entityMap').update({"sourceTableName":"myTable"}, {"fieldMappings.0.sourceField":"trmls_id":{$exists : true}}, { $set: { "fieldMappings.0.sourceField": "tier_id" } })
and I think it is failing on the $exists parameter I am setting.
Is there a more cleaner/dynamic way to do this?
The whole query I am trying to formulate is
In the table myTable
I want to check if there is a sourceField that has the value tier_id
If there is tier_id, then change it to trmls_id
Otherwise do nothing
If there is a similar StackOverflow question that answers this, I am happy to explore it! Thanks!
There is a syntax mistake and you can use positional operator $ in update part because you have already selector field in query part,
db.getCollection('entityMap').update(
{
"sourceTableName": "myTable",
"fieldMappings.sourceField": "tier_id" // if this field found then it will go to update part
},
{
$set: {
"fieldMappings.$.sourceField": "trmls_id"
}
}
)

Firebase filter with pagination

I'm doing an opensource Yelp with Firebase + Angular.
My database:
{
"reviews" : {
"-L0f3Bdjk9aVFtVZYteC" : {
"comment" : "my comment",
"ownerID" : "Kug2pR1z3LMcZbusqfyNPCqlyHI2",
"ownerName" : "MyName",
"rating" : 2,
"storeID" : "-L0e8Ua03XFG9k0zPmz-"
},
"-L0f7eUGqenqAPC1liYj" : {
"comment" : "me second comment",
"ownerID" : "Kug2pR1z3LMcZbusqfyNPCqlyHI2",
"ownerName" : "MyName",
"rating" : 3,
"storeID" : "-L0e8Ua03XFG9k0zPmz-"
},
},
"stores" : {
"-L0e8Ua03XFG9k0zPmz-" : {
"description" : "My good Store",
"name" : "GoodStore",
"ownerID" : "39UApyo0HIXmKPrTOi8D0nWLi6n2",
"tags" : [ "good", "health", "cheap" ],
}
},
"users" : {
"39UApyo0HIXmKPrTOi8D0nWLi6n2" : {
"name" : "First User"
},
"Kug2pR1z3LMcZbusqfyNPCqlyHI2" : {
"name" : "MyName",
"reviews" : {
"-L0f3Bdjk9aVFtVZYteC" : true,
"-L0f7eUGqenqAPC1liYj" : true
}
}
}
}
I use this code below to get all store's reviews (using AngularFire2)
getReviews(storeID: string){
return this.db.list('/reviews', ref => {
return ref.orderByChild('storeID').equalTo(storeID);
});
}
Now, I want to make a server-side review pagination, but I think I cannot do it with this database structure. Am I right? Tried:
getReviews(storeID: string){
return this.db.list('/reviews', ref => {
return ref.orderByChild('storeID').equalTo(storeID).limitToLast(10) //How to make pagination without retrive all data?
});
}
I thought that I could put all reviews inside stores, but (i) I don't want to retrieve all reviews at once when someone ask for a store and (ii) my review has a username, so I want to make it easy to change it (that why I have a denormalized table)
For the second page you need to know two things:
the store ID that you want to filter on
the key of the review you want to start at
You already have the store ID, so that's easy. As the key to start at, well use the key of the last item on the previous page, and then just request one item extra. Then finally, you'll need to use start() (and possibly endAt() for this:
return this.db.list('/reviews', ref => {
return ref.orderByChild('storeID')
.startAt(storeID, lastKeyOnPreviousPage)
.limitToLast(11)
});
Refer this and this documentation.
For the first page:
snapshot = await ref.orderByChild('storeID')
.equalTo(store_id) //store_id is the variable name.
.limitToLast(10)
.once("value")
Store the firstKey (NOT the lastKey) from the above query. (Since you are using limitToLast())
firstKey = null
snapshot.forEach(snap => {
if (!firstKey)
firstKey = snap.key
// code
})
For the next page:
snapshot = await ref.orderByChild('storeID') //storeID is the field name in the database
.startAt(store_id) //store_id is the variable name which has the desired store ID
.endAt(store_id, firstKey)
.limitToLast(10 + 1) //1 is added because you will also get value for the firstKey
.once("value")
The above query will fetch 11 list data which will contain one redundant data from the first page's query.
How it works:
startAt ( value : number | string | boolean | null , key ? : string ) : Query
The starting point is inclusive, so children with exactly the specified value will be included in the query. The optional key argument can be used to further limit the range of the query. If it is specified, then children that have exactly the specified value must also have a key name greater than or equal to the specified key.
endAt ( value : number | string | boolean | null , key ? : string ) : Query
The ending point is inclusive, so children with exactly the specified value will be included in the query. The optional key argument can be used to further limit the range of the query. If it is specified, then children that have exactly the specified value must also have a key name less than or equal to the specified key.
So the query will try to fetch:
storeID >= store_id && storeID <= store_id (lexicographically)
which will equal to
storeID == store_id

MongoDB and Mongoose: How to retrieve nested Object

I have the following documents in my mongodb collection:
{
"current" :
{
"aksd" : "5555",
"BullevardBoh" : "123"
},
"history" :
{ "1" : {
"deleted" : false,
"added" : false,
"date" : "21-08-2014"
}
},
{ "2" : {
"deleted" : false,
"added" : false,
"date" : "01-01-2013"
}
},
"_id" : ObjectId("53f74dad2cbfdc136a07bf16"),
"__v" : 0
}
I have multiple of these documents. Now I want to achieve two things with my Mongoose/Express API.
Query for all nested "current" in each document and retrieve them as JSON objects like such: {"aksd":"5555","BullevardBoh":"123"},{..},{..}.
Retrieve all history revisions (1,2...) where "date" is smaller than a given date.
As you can clearly see this is a kind of versioning I am trying to implement. I would also be interested if this kind of data structure will get indexed by MongoDB and if there is possibly a better way. (e.g. with arrays inside objects?)
This isn't working in MongoDB:
db.ips.findOne({current.aksd: {$exists:true}});
I think the quotes around the field are missing here:
db.ips.findOne({current.aksd: {$exists:true}});
This should work instead:
db.ips.findOne({"current.aksd": {$exists:true}});
While Ritesh's reply was a step in the right direction, I rather wanted to fetch the current object literal and its members inside the document not the whole document.
1.) Query for all nested "current" in each document
db.ips.find({"current":{$exists:true}}, {"current":1});
This is giving back all nested documents where the aksd literal is present:
{ "current" : { "aksd" : "5555", "BullevardBoh" : "123" }, "_id" : ObjectId("53f74dad2cb3dc136a07bf16") }
...
2.) Retrieving history revisions where date is smaller then a given date:
db.ips.find({"history.date": {$lt: "01-01-2014"}},{history:{$elemMatch:{date: {$lt:"01-01-2014"}}}});
Giving back the wanted nested date literal(s):
{ "historie" : [ { "date" : "01-01-2013", "added" : false, "deleted" : false } ], "_id" : ObjectId("53faf20f399a954b2b7736b6") }

Schema for User table with multi strategy sigup

While working on one project faced with problem of storing user information for different passport strategies(local, facebook, twitter).
At the begining my UserSchema had such look:
User = mongoose.Schema(
{
"email" : { type : String , lowercase: true , trim : true , unique : true } ,
"providerId" : { type : String } ,
"providerName" : { type : String } ,
"hashedPassword" : { type : String } ,
"salt" : { type : String } ,
"strategy" : { type : String } ,
"created" : { type : Date , default : new Date().valueOf() } ,
"lastConnected" : { type : Date , default : new Date().valueOf() } ,
"accessToken" : { type : String } ,
"securityToken" : { type : String , default : "" } ,
"roles" : [ { type : String } ]
}
);
But unique email brings problem with emails because two users with twitter stategy will have null email and this leads to error.
I thought about not making email unique but this brings alot( from the first look ) of problems.
One the solutions I see is making different schemas for each strategy but this is very difficalt to maintain.
Is there some other way of solving this issue. What are the best practices?
P.S. I swear I googled but didn't find any solution
It seems like you want to be able to say unique:true and specify allowNulls. This now seems possible, as you can see here: https://stackoverflow.com/a/9693138/1935918

Mongo query in nested fields

I have a DB which has records with nested fields like :
"requestParams" : { "query" : [ "tv9" ] }
I am using the following query to fetch these kind of records. However I am looking for a more general case wherein the field query matches /tv9/ which includes tv9 as part of any search. Like it should also return livetv9, movietv9, etc. Following query does not seem to work:
db.requestLog.findOne({url : /search.json/, requestParams: {$elemMatch: {query: /tv9/}}})
$elemMatch is used to match partial document elements inside an array. Since you want to directly match on a string the use of this operator is inappropriate. Use this instead :
db.requestLog.findOne({url : /search.json/, 'requestParams.query': /tv9/})
Example :
mongos> db.test.save({r:{q:["tv9"]}})
mongos> db.test.save({r:{q:["tv"]}})
mongos> db.test.save({r:{q:["ltv9l"]}})
mongos> db.test.find({'r.q':/tv9/})
{ "_id" : ObjectId("4f5095d4ec991a74c16ba862"), "r" : { "q" : [ "tv9" ] } }
{ "_id" : ObjectId("4f5095deec991a74c16ba864"), "r" : { "q" : [ "ltv9l" ] } }

Categories