I'm a beginner in pymongo and I have a users collection that stores a user and a comments list
An instance of a user is like
user = {"Name":"Bill" , "Comments":["nice" , "bad"]}
What I want is to access the "bad" item and change it to "good" inside my Comments list but I am beginner and I have trouble with the pymongo syntax .
This command is used for updating but I do not know how to access the specific list item
users.update_one({'key': 'value'}, {'$set': {'key': 'different value'}})
I would appreciate your help . Thank you in advance
You can use the positional operator($). This will update only the first element that matches the query condition.
db.collection.update_one(
{ 'name': 'Bill', 'ratings': 'bad' },
{ '$set':
{ 'ratings.$': 'good' }
}
)
If you want to update all the occurrences of "bad" to "good", then you should use filtered positional operator($[<identifier>]).
db.collection.update_one(
{ 'name': 'Bill' },
{ '$set':
{ 'ratings.$[elem]': 'good' }
},
array_filters = [{ 'elem': 'bad' }]
)
Related
I'm looking at synching a node e.g. /a/b/c with node /data/c
There is data at node /a/b/c - so I just want to grab a snapshot of /data/c and put that data in to /a/b/c without deleting existing values.
Would an approach like :
Ref.update({
snapshotdata.val()
}).key;
Would this work? I'm not sure how firebase would parse the snapshot, if it takes the whole thing and overwrites or it would take individual values and adds.
Thanks in advance for any clarity.
Best Regards,
Kieran
It depends. Snapshot values are simply JSON objects (or primitives, depending on the node). update() will replace any nodes specified by the keys in the update, but not any that aren't. So if /a/b/c looks like:
{
name: 'old name',
data: {
gonnabe: 'gone'
},
created: 12345679
}
and data/c looks like:
{
name: 'new name',
data: {
nested: true
},
updated: 12345679
}
doing an db.ref('a/b/c').update(dataSnapshot.val()) will result in:
{
name: 'new name',
data: {
nested: true
},
created: 12345679,
updated: 12345679
}
Note that the nested field in data was wiped out by the update, but the non-nested field created was not.
If you want to do deep updates, you'll need to construct an update with slash-delimited fields for each deep node you want to update. So instead of the above, you might have:
{
"name": "new name",
"data/nested": true,
"updated" 12345679
}
This will be non-destructive to the nested data and only update values that are directly referenced, resulting in:
{
name: 'new name',
data: {
gonnabe: 'gone',
nested: true
},
created: 12345679,
updated: 12345679
}
Hope that helps!
Referring to the default sample schema mentioned in https://hasura.io/hub/project/hasura/hello-world/data-apis i.e. to the following two tables:
1) author: id,name
2) article: id, title, content, rating, author_id
where article:author_id has an array relationship to author:id.
How do I make a query to select authors who have written at least one article? Basically, something like select author where len(author.articles) > 0
TL;DR:
There's no length function that you can use in the Hasura data API syntax right now. Workaround 1) filter on a property that is guaranteed to be true for every row. Like id > 0. 2) Build a view and expose APIs on your view.
Option 1:
Use an 'always true' attribute as a filter.
{
"type": "select",
"args": {
"table": "author",
"columns": [
"*"
],
"where": {
"articles": {
"id": {
"$gt": "0"
}
}
}
}
}
This reads as: select all authors where ANY article has id > 0
This works because id is an auto-incrementing int.
Option 2:
Create a view and then expose data APIs on them.
Head to the Run SQL window in the API console and run a migration:
CREATE VIEW author_article_count as (
SELECT au.*, ar.no_articles
FROM
author au,
(SELECT author_id, COUNT(*) no_articles FROM article GROUP BY author_id) ar
WHERE
au.id = ar.author_id)
Make sure you mark this as a migration (a checkbox below the RunSQL window) so that this gets added to your migrations folder.
Now add data APIs to the view, by hitting "Track table" on the API console's schema page.
Now you can make select queries using no_articles as the length attribute:
{
"type": "select",
"args": {
"table": "author_article_count",
"columns": [
"*"
],
"where": {
"no_articles": {
"$gt": "0"
}
}
}
}
I am currently using ReactiveAggregate to find a subset of Product data, like this:
ReactiveAggregate(this, Products, [
{ $match: {}},
{ $project: {
title: true,
image: true,
variants: {
$filter: {
input: "$variants",
as: "variant",
cond: {
$setIsSubset: [['$$variant.id'], user.variantFollowing]
}
}
}
}}
], { clientCollection: 'aggregateVariants' }
As you can see, a variant is returned if user.variantFollowing matches. When a user 'follows' a product, the ID is added to their object. However, if I understand correctly, this is not triggering ReactiveAggregate to get the new subset when this happens. Only on a full page refresh do I get the correct (latest) data.
Is this the correct way to approach this?
I could store the user's ID as part of the Product object, but the way this would be stored would be nested two places, and I think I would need the Mongo 3.5 updates to then be able to accurately update this. So i'm looking for how to do this in Meteor 1.5+ / Mongo 3.2.12
So, I've been able to get there by adding autorun to the subscription of the aggregate collection, like this:
Template.followedProducts.onCreated(function() {
Meteor.subscribe('products');
this.autorun(() => {
Meteor.subscribe('productsFollowed');
});
... rest of function
For context, productsFollowed is the subscription to retrieve aggregateVariants from the original question.
Thanks to robfallows in this post: https://forums.meteor.com/t/when-and-how-to-use-this-autorun/26075/6
So, let's say I have data like this:
{
"events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
text: "Bob says 'My name is Robert'" },
"kjl34jl234j" :
{ creator: "fred#fred.com",
text: "Fred says 'My name is Fredrick'" }
}
"users" : {
"bob#bob.com" : { "paid": true },
"fred#fred.com" : { "paid": false }
}
}
I'm assuming this is the correct way to structure the data. When the data is created, I use the push() method to create a new key for the data, and then store the creator of the data inside it.
I'd like to make it so that:
I can allow anyone from a group of users to access certain data (and disallow others obviously).
The query is "optimized," meaning if I have thousands of records I am not iterating over all the data.
More concretely, for example, I want lizzie#lizzie.com to be able to see the s0d980983s.
I'm confused how to structure the data, and what my Firebase rules should look like.
Would it be something like this?
{ "events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
viewers: { "bob#bob.com": true,
"lizzie#lizzie.com" : true },
text: "Bob says 'My name is Robert'" },
...
}
I don't understand how I can search for events that are viewable by a group of users. I don't believe Firebase supports some kind of wildcard that would make this code work, right?
var ref = firebase.database().ref( "events/*/viewers/lizzie#lizzie.com" ).on(...);
Do I also want to reference the events inside my users table? I'm not sure I understand how to flatten data (denormalize it) and keep references in both places to support a query like this. Should I expect to make multiple queries where I first retrieve a list of events stored in a user object and then retrieve them one by one using their key? But, how do I put that logic into my firebase rules?
{ "events" : {
"s0d980983s" :
{ creator: "bob#bob.com",
viewers: { "[insert bobs id]": true,
"[insert liz id]" : true
},
text: "Bob says 'My name is Robert'" },
...
}
Based on the above structure as you suggested, and if you are using firebase authentication to authenticate your user, you can add another 'read' or 'write' rule for checking whether that user is in the list of your 'viewers'. something like:
{
"rules": {
"users": {
"$uid": {
".write": "auth != null &&
root.child('users').child(auth.uid).child('viewers').child(auth.uid).val() ==
true"
}
}
}
}
This should help. setting firebase security rules at a location/node
My site allows users to apply by connecting their google account, as soon as an account is created they're given a "pending" role (using alanning:roles package). I would like to have a table for admins to see when new applicants have applied, from there the admin can properly manage the users application (change role to accepted, declined, etc.). So, I have created my table, but it's showing all users, I'm wondering if someone knows a way to make it so only users with the "pending" role are shown in my table?
Here is what I have so far:
TabularTables.ManageApplications = new Tabular.Table({
name: 'ManageApplications',
collection: Meteor.users,
allow: function (userId) {
return Roles.userIsInRole(userId, 'admin');
},
autoWidth: false,
oLanguage: {
"sSearch": "Search: "
},
columns: [
{ data: //column details here },
{ data: //column details here },
{ data: //column details here },
{ data: //column details here },
{ data: //column details here },
],
});
This works, but it shows every user (not just users with the "pending" role).
I then created this to try and publish only data for pending users:
Meteor.publish("pendingUsers", function() {
var isAdmin = Roles.userIsInRole(this.userId, 'admin');
if (isAdmin) {
return Roles.getUsersInRole('pending').fetch();
} else {
return null;
}
});
and subscribed by adding pub: "pendingUsers", to my table. This somewhat works, it makes it so it only shows data in the columns for "pending" role users, but, it still lists every user and just has blank spaces where the data would be.
If anyone knows how I can achieve this it would be greatly appreciated if you could give some insight as I've been stuck on this for quite a while... I believe it may have to do with "Displaying Only Part of a Collection's Data Set" in the Tabular readme, but I'm very unsure of how to set this up. Any examples or help is extremely appreciated.
This has been solved by adding the following to my table:
selector: function(userId) {
return {
_id: {$in: Roles.getUsersInRole('pending')
.map(function(user){ return user._id} ) }
}
},
Given the way that publications work it's more than likely that you have another publication and subscription which is giving you the rest of the users but with a different set of keys/fields. Since multiple publications can be running on the same collection at the same time you want to perform the same .find() on the client that your publication is giving you.
Go ahead and add a selector to your table definition as follows:
selector: function( userId ) {
return Roles.getUsersInRole('pending');
}
You don't need the .fetch() in your publication btw, Roles.getUsersInRole() already returns a cursor of Meteor.users.
Use Selector: https://github.com/aldeed/meteor-tabular#modifying-the-selector
If you want to check all the rules of such a group:
selector: function (userId) {
return 'roles.myrole': {$exists: true}};
},
Or with just a few:
selector: function (userId) {
return 'roles.myrole': {$in: ['viewer', 'editor']}};
},