MongoID : match multiple elements in array and update all - ruby-on-rails

I use MongoID and PostgreSQL on Rails 4 and I have a notification system.
Each user has one notification document. I would like to be able to mark all as read in a controller.
Here is my notification schema :
{
"_id" : ObjectId("53e360156d6163513e000000"),
"unread_counter" : 1,
"user_id" : 1,
"rows" : [
{
"_id" : ObjectId("53e369166d61635328000000"),
"readed" : false,
"created_at" : ISODate("2014-08-07T11:55:02.936Z"),
"author" : {
"_id" : ObjectId("53e369166d61635328010000"),
"username" : "edouard",
"user_id" : 6
},
"subject" : {
"_id" : ObjectId("53e369166d61635328020000"),
"type" : "follower"
}
}
]
}
The problem is that elem_match just match one element in rows array.
Notification.where(:user_id => current_user.id)
.elem_match(rows: { readed: false })
.update_all("$set" => {"rows.$.readed" => true })
I have been inspired by this post : mongoid update elements within array
How can I match all "readed" : false row elements and then update them to true ?

Related

firebase database filtering is not working. Need some assistance

I have a simple test database that I cant get to filter. I indexed the category in the rules:
"questions":{
".indexOn": ["category"]
},
My filter for the quiz app:
/questions.json?orderBy="category"&equalTo="Basics"&print=pretty
and my database:
"-MKoucSP33zm4jC43AnY" : {
"title" : {
"answers" : [ {
"score" : 30,
"text" : "Pineapple"
}, {
"score" : 5,
"text" : "Ham"
}, {
"score" : 20,
"text" : "Yogurt"
}, {
"score" : 10,
"text" : "Crab"
} ],
"category" : "Basics",
"questionId" : "101",
"questionImage" : "",
"questionLink" : "",
"questionText" : "What topping do you like the best on pizza?"
}
}
The category property is nested under the title node, so the property you need to order/filter on is title/category:
/questions.json?orderBy="title/category"&equalTo="Basics"&print=pretty
You'll also need to update your index definition for that path, so:
"questions": { ".indexOn": "title/category" }
Working example: https://stackoverflow.firebaseio.com/64596200/questions.json?orderBy="title/category"&equalTo="Basics"

Extracting a specific part from JSON

Hello I am trying to extract only the id value from a JSON. However if I
print(response!["id"])
result = "not created" is outputted. reponse is already in JSON format.
Is there something that I am doing wrong?
Update 1
[
{
"user" : {
"last_name" : "test",
"email" : "test#test.com",
},
"id" : 902,
"scale" : 7,
"created_at" : "2018-02-24 06:45:33",
},
{
"user" : {
"last_name" : "test",
"email" : "test#test.com",
},
"id" : 903,
"scale" : 7,
"created_at" : "2018-02-24 06:45:33",
},
{
"user" : {
"last_name" : "test",
"email" : "test#test.com",
},
"id" : 904,
"scale" : 7,
"created_at" : "2018-02-24 06:45:33",
},
]
The best way to access the id of any object from the array of response will be:
let id = response[index]["id"]
// Here index is the index of the object you want to access.
I am assuming you want to extract ids from this JSON array. You can do this using
response.map({ $0["id"]})
Which will give you another array containing only ids

How do I associate a firebase sub-entry to a main entry? (explanation in comments)

The JSON:
{
"coffee-shops" : {
"-KJ4I4D-Jrqrzc9wP42C" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 3.5,
"-KJ4VVB51wx9NpEKtjxQ" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 1,
"coffeeShopReview" : "R",
"coffeeShopReviewerName" : "Charles"
},
"-KJEJ6MpQwOHcay_9k6v" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 4,
"coffeeShopReview" : "B",
"coffeeShopReviewerName" : "Charles"
}
}
},
"users" : {
"02a54e06-9635-4e22-9bb7-c0ddcd9c6f4f" : {
"email" : "charles#gmail.com",
"provider" : "password",
"username" : "thecoffeeguy"
},
"03fe2c17-3c66-442c-a63d-4a1e02fd660c" : {
"email" : "test#gmail.com",
"provider" : "password",
"username" : "Charles"
},
"16a7279f-5478-4f3f-b5f8-2f261d166d92" : {
"email" : "tester#gmail.com",
"provider" : "password",
"username" : "haha"
},
"23275f65-8e16-4ede-9236-21485b7493b9" : {
"email" : "boo#gmail.com",
"provider" : "password",
"username" : "boo"
},
"a5ed6962-76bc-476a-b432-6787e45badfc" : {
"email" : "mesbekmek#gmail.com",
"provider" : "password",
"username" : "mesbekmek"
}
}
}
Some context: I'm making a coffee app and I need to have the reviews be specific to the coffee shop a user is at. Right now, all reviews that have ever been made will show up on my tableview.
This isn't so much a "how to code" question, but me wondering how to approach this and how I might solve it.
This is what I think I should do:
get a specific coffee shop's uuid
iterate over the reviews because they are sub-entries in my coffee shop model
get data from iterating over the reviews, then see if the uuid of the cell(?) selected matches the review uuid
This doesn't really sound right to me, so any help would be great.
You can organize your db in a different way.
coffee-shops:
|--coffeeId1
|--coffeeId2
|--coffeeId3
reviews:
|--coffeeId1
|----reviewId1
|----reviewId2
|--coffeeId2
|----reviewIdX
|----reviewIdXx
When you insert a review you can use the same key of the coffee-shop.
In this way all the reviews of the same coffee shop is under the same ref.
You can achieve it using something like this:
ref(reviews).child(coffeeshop.getKey()).push();

mongodb/rails "exception: can't find special index: 2d for:"

i have a rails app where i have some problems with indexes. I search locations by name.
First i thought its a problem with the addresses.coords but iam not sure about it.
The relevant parts of the search controller:
#practices = Practice.published
#practices = #practices.where(:"addresses.country" => params[:country].upcase) if params[:country].present?
if params[:location].present? && latlng = get_coordinates
#practices = #practices.near_sphere(:"addresses.coords" => latlng).max_distance(:"addresses.coords" => get_distance )
end
# now find doctors based on resulting practices
#doctors = Doctor.published.in("organization_relations.practice_id" => #practices.distinct(:_id))
The complete crash log:
Moped::Errors::OperationFailure (The operation: #<Moped::Protocol::Command
#length=255
#request_id=646
#response_to=0
#op_code=2004
#flags=[]
#full_collection_name="um-contacts.$cmd"
#skip=0
#limit=-1
#selector={:distinct=>"practices", :key=>"_id", :query=>{"deleted_at"=>nil, "published_at"=>{"$lte"=>2012-11-05 15:17:14 UTC}, "addresses.country"=>"DE", "addresses.coords"=>{"$nearSphere"=>[13.4060912, 52.519171], "$maxDistance"=>0.01569612305760477}}}
#fields=nil>
failed with error 13038: "exception: can't find special index: 2d for: { deleted_at: null, published_at: { $lte: new Date(1352128634313) }, addresses.country: \"DE\", addresses.coords: { $nearSphere: [ 13.4060912, 52.519171 ], $maxDistance: 0.01569612305760477 } }"
See https://github.com/mongodb/mongo/blob/master/docs/errors.md
for details about this error.):
app/controllers/search_controller.rb:16:in `index'
Thats the result of the indexes, not sure how to query them from the addresses which are embedded via has_many.
> db.practices.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "um-contacts.practices",
"name" : "_id_"
}
]
Help would be really appreciated!
Edit: Looks like the indexes for adresses.coords arent created,
db.system.indexes.find()
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.users", "name" : "_id_" }
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.doctors", "name" : "_id_" }
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.collaborations", "name" : "_i
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "um-contacts.practices", "name" : "_id_" }
but should be created within the practice class:
class Practice
...
embeds_many :addresses, cascade_callbacks: true, as: :addressable
...
field :name, type: String
field :kind, type: String
field :slug, type: String
index({"addresses.coords" => '2d'}, { min: -180, max: 180, background: true })
index({name: 1})
index({slug: 1}, { unique: true })
...
Anyone have an idea why its failing?
try to re-create your indexes. for mongoid:
rake db:mongoid:create_indexes

How to join query in mongodb?

I have user document collection like this:
User {
id:"001"
name:"John",
age:30,
friends:["userId1","userId2","userId3"....]
}
A user has many friends, I have the following query in SQL:
select * from user where in (select friends from user where id=?) order by age
I would like to have something similar in MongoDB.
To have everything with just one query using the $lookup feature of the aggregation framework, try this :
db.User.aggregate(
[
// First step is to extract the "friends" field to work with the values
{
$unwind: "$friends"
},
// Lookup all the linked friends from the User collection
{
$lookup:
{
from: "User",
localField: "friends",
foreignField: "_id",
as: "friendsData"
}
},
// Sort the results by age
{
$sort: { 'friendsData.age': 1 }
},
// Get the results into a single array
{
$unwind: "$friendsData"
},
// Group the friends by user id
{
$group:
{
_id: "$_id",
friends: { $push: "$friends" },
friendsData: { $push: "$friendsData" }
}
}
]
)
Let's say the content of your User collection is the following:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"name" : "John",
"age" : 30,
"friends" : [
"userId1",
"userId2",
"userId3"
]
}
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
The result of the query will be:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"friends" : [
"userId3",
"userId1",
"userId2"
],
"friendsData" : [
{
"_id" : "userId3",
"name" : "Bobby",
"age" : 12
},
{
"_id" : "userId1",
"name" : "Derek",
"age" : 34
},
{
"_id" : "userId2",
"name" : "Homer",
"age" : 44
}
]
}
Edit: this answer only applies to versions of MongoDb prior to v3.2.
You can't do what you want in just one query. You would have to first retrieve the list of friend user ids, then pass those ids to the second query to retrieve the documents and sort them by age.
var user = db.user.findOne({"id" : "001"}, {"friends": 1})
db.user.find( {"id" : {$in : user.friends }}).sort("age" : 1);
https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/
This is the doc for join query in mongodb , this is new feature from version 3.2.
So this will be helpful.
You can use in Moongoose JS .populate() and { populate : { path : 'field' } }.
Example:
Models:
mongoose.model('users', new Schema({
name:String,
status: true,
friends: [{type: Schema.Types.ObjectId, ref:'users'}],
posts: [{type: Schema.Types.ObjectId, ref:'posts'}],
}));
mongoose.model('posts', new Schema({
description: String,
comments: [{type: Schema.Types.ObjectId, ref:'comments'}],
}));
mongoose.model('comments', new Schema({
comment:String,
status: true
}));
If you want to see your friends' posts, you can use this.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts' //Collection 3
}})
.exec();
If you want to see your friends' posts and also bring all the comments, you can use this and too, you can indentify the collection if this not find and the query is wrong.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts', //Collection 3
populate:{path:'commets, model:Collection'//Collection 4 and more
}}})
.exec();
And to finish, if you want get only some fields of some Collection, you can use the propiertie select Example:
Users.find().
populate({path:'friends', select:'name status friends'
populate:{path:'comments'
}})
.exec();
MongoDB doesn't have joins, but in your case you can do:
db.coll.find({friends: userId}).sort({age: -1})
one kind of join a query in mongoDB, is ask at one collection for id that match , put ids in a list (idlist) , and do find using on other (or same) collection with $in : idlist
u = db.friends.find({"friends": ? }).toArray()
idlist= []
u.forEach(function(myDoc) { idlist.push(myDoc.id ); } )
db.friends.find({"id": {$in : idlist} } )
Only populate array friends.
User.findOne({ _id: "userId"})
.populate('friends')
.exec((err, user) => {
//do something
});
Result is same like this:
{
"_id" : "userId",
"name" : "John",
"age" : 30,
"friends" : [
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
]
}
Same this: Mongoose - using Populate on an array of ObjectId
You can use playOrm to do what you want in one Query(with S-SQL Scalable SQL).
var p = db.sample1.find().limit(2) ,
h = [];
for (var i = 0; i < p.length(); i++)
{
h.push(p[i]['name']);
}
db.sample2.find( { 'doc_name': { $in : h } } );
it works for me.
You can do it in one go using mongo-join-query. Here is how it would look like:
const joinQuery = require("mongo-join-query");
joinQuery(
mongoose.models.User,
{
find: {},
populate: ["friends"],
sort: { age: 1 },
},
(err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);
The result will have your users ordered by age and all of the friends objects embedded.
How does it work?
Behind the scenes mongo-join-query will use your Mongoose schema to determine which models to join and will create an aggregation pipeline that will perform the join and the query.

Resources