I'm using pouchDB for the first time, and as indicated in the docs I'm using put() so it will automatically handle revisions. However, when the code is running and there's an existing item with the same ID, it's still rejecting even when including a _rev.
Here's my code:
var db = new PouchDB('blog')
...
function saveCategory(category) {
var savedCategory = {
_id: 'category' + category.id,
_rev: '2-' + String(new Date().toISOString()),
name: category.name,
nicename: category.slug,
post_count: category.count,
description: category.description,
link: category.link,
parent: category.parent
}
return db.put(savedCategory).then((response) => {
$log.log(response)
}).catch((error) => {
$log.error('error saving category ',error)
})
}
This is not the purpose of the _rev field. It is always generated by the server and not by your code. To update a document you must pull the entire document (including the _rev field), update the desired fields, and then put the document. The value of _rev should be the same as when you got it from the server.
If you have a new record, you do not need to set _rev.
The pocketDB guide has a very useful section about this.
Related
Let's say I have these three documents:
{ "_id": "11111", "type": "template", "name": "person" }
{ "_id": "22222", "type": "template", "name": "place" }
{ "_id": "33333", "type": "template", "name": "thing" }
I have a cloud database and then I have a device with pouchDB syncing from that database.
These are the steps that I do:
I sync both databases together. So now I have the most recent versions of this document on my device.
I run the below query and I get back all three templates like so:
Code
var template_obj = {};
return device_db.query('filters/templates')
.then((templates) => {
for (let t of templates.rows) templates_obj[t.id] = true;
return templates_obj;
});
filters/templates
function (doc) {
if(doc.type == "template")
emit(doc._id);
}
return
{ "11111": true, "22222": true, "33333": true }
I update template: person on cloud. And then I update it again. So 2 revisions have gone by without syncing to my device.
I sync with my device.
Now when I run the same query and I only get back the document I edited. Which is weird because I haven't touched any of the other documents. The same view returns the expected results on the cloud but not on the device.
return
{"11111": true}
If I do the following code however, all templates come back as normal and the same _rev from the cloud show up on the device. Meaning the sync was successful and view is getting confused.
new code
return device_db.allDocs({conflicts: true})
.then((data) => {
for (let d of data.rows) {
if(d.doc.type == "template") {
templates_obj[d.doc._id] = true;
}
}
return templates_obj;
}).catch((err) => {
console.log(JSON.stringify(err));
})
I'm starting to believe this is a bug because if I destroy my database and do these steps again, I can reproduce this issue.
After realizing you are using React Native, I think this actually has to do with PouchDB in React Native, and it's indeed a bug. There are several reports of that behavior:
https://github.com/pouchdb/pouchdb/issues/7219
https://github.com/pouchdb/pouchdb/issues/7188
https://github.com/pouchdb/pouchdb/issues/7293
[edit: Seems to be a bug in PouchDB with React Native. I leave this answer because it might be helpful in other ways.]
I suspect it's some side effect with the global variable template_obj you are using. Try to console.log(templates.rows) directly instead of storing it in a variable in the top scope, or use Array.reduce() to avoid side effects. Then you'd always get the correct view results.
This is step by step code:
return device_db.query('filters/templates')
.then(templates => templates.rows) // Take only the rows into account.
.then(rows => rows.map(row => row.id) // Extract the id. If you wanted the name instead this would be possible with a slightly different view.
// I think it would suffice to log the result right now,
// but if you really want to have a single object with boolean values,
// you can do the following:
.then(ids => ids.reduce((asObject, id) => { // Use Array.reduce() here to avoid any potential side effects.
asObject[id] = true;
return asObject;
}, {})
.then(asObject => { console.log(asObject); }; // Debug the result.
Or more concise with ES2015+:
return device_db.query('filters/templates')
.then(({rows}) => rows.reduce((acc, {id}) => ({...acc, [id]: true }), {}))
.then(result => console.log(result))
By the way: You could also use other strategies to "filter" your documents, as it's not necessary to emit the _id. Instead you can use the key and/or value for "secondary indexes":
{
"_id": "_design/docs",
"views": {
"byType": "function(doc) { emit(doc.type); }",
"templatesByName": "function(doc) { if (doc.type === 'template') emit(doc.name); }",
"byTypeAndName": "function(doc) { emit([doc.type, doc.name], doc.name); }
}
}
you can use docs/byType as an universal view for other doc types too. Just call it with db.query('docs/byType', { key: 'template' })
If you want the templates sorted by name, use db.query('docs/templatesByName') or db.query('docs/byTypeAndName', { startkey: ['template'], endkey: ['template', {}]}).
A word of caution: This is all untested and just from memory, so some brackets might be missing in the code, or some bugs might hide in there.
It's not a bug in PDB, it's about outdated unfortunately components in pouchdb-react-native.
Confirmed way is to combine pouchdb-react-native yourself like this - then queries work as expected:
import PouchDB from 'pouchdb-core';
PouchDB
.plugin(require('pouchdb-adapter-asyncstorage').default)
.plugin(require('pouchdb-adapter-http'))
.plugin(require('pouchdb-mapreduce'))
.plugin(require('pouchdb-replication'))
.plugin(require('pouchdb-authentication'));
const localDB = new PouchDB(localDBname, {adapter: 'asyncstorage', auto_compaction: true});
This way one can be sure that all components are the latest.
How do I pick the email address value from meteor Mongo user table?
I have written below query to pick the element:
users=Meteor.users.find({},{emails:1})
This the code I have written to fetch the email address, but I don't know how much it's affecting performance in the code:
users = Meteor.users.find({})
users.forEach(function(key,option){
key.emails.forEach(function (key,option){
console.log(key.address)
});
});
In meteor, you should call:
users = Meteor.users.find({}, { fields: { emails: 1 } })
Reference in docs
EDIT
Please remember users is a cursor object. Cursor objects can be handled directly in templates, and must be the return of publications. You can't iterate a cursor directly in a javascript loop.
Example: (remember authorization in production publications)
Meteor.publish('user-emails', function() {
return Meteor.users.find({}, { fields: { emails: 1 } });
});
If you want to directly access the user instances, for example to iterate them in a javascript code, you need to fetch the cursor (reference in docs).
Example:
var users = Meteor.users.find({}, { fields: { emails: 1 } }).fetch();
Now users is an array of users. Feel free to iterate them.
Example (I'm using underscore.js):
var users = Meteor.users.find({}, { fields: { emails: 1 } }).fetch();
_.each(users, function(user) {
console.log(user.emails);
});
Now, if you need a vector only with emails, one on each index, you can pluck the emails from a fetched array with underscore.js (reference of pluck)
var emails = _.pluck(Meteor.users.find({}, { fields: { emails: 1 } }).fetch(), 'emails');
Hope it works :)
if its not working, dont forget to return
return users
I'm using the excellent parse-react library to get Parse and ReactJS to work together nicely (n.b I've only been playing around for a few hours so apologies if I've misunderstood any of the basics of reactjs).
All was going well until I wanted to query a table for all objects created by the current user (Parse.user.current())
The observe method works correctly on load and the view is rendered with the correct objects (the objects created by the current user). However if I mutate the data and add a new object then the view doesn't re-render.
Abstracted code:
module.exports = React.createClass({
mixins: [ParseReact.Mixin],
getInitialState: function() {
return {
selected: null
};
},
observe: function() {
return {
places: (new Parse.Query('Place'))
.equalTo('user', Parse.User.current())
.descending('createdAt')
};
},
clickHandler: function(event) {
var id = event.target.id;
if (id === 'new') {
ParseReact.Mutation.Create('Place', {
name: 'New Place',
user: Parse.User.current()
}).dispatch();
} else if(id.indexOf('Place:') === 0) {
this.setState({
selected: id.substring(6)
});
}
},
render: function() {
var that = this;
var navItems = this.data.places.map(function(place) {
return (
<UserNavItem id={place.id} key={place.id} label={place.name} selected={that.state.selected === place.objectId} onClick={that.clickHandler}/>
);
});
return (
<ul>
{navItems}
<UserNavItem id='new' label='+ New Place' onClick={this.clickHandler} />
</ul>
);
}
});
If I remove the part of the query that specifies the user:
.equalTo('user', Parse.User.current())
Then it works; new place objects appear in the list when added.
Has anyone got any idea what I'm doing wrong?
Am I using Parse queries incorrectly? It always seems strange that getting the data pertaining to the current user is a bit of a pain when this seems like such a common use case?
Thanks!
The solution is to call the .refreshQueries() method of the component when the new object is successfully created in Parse as described here.
My updated example:
ParseReact.Mutation.Create('Place', {
name: 'New Place',
user: Parse.User.current()
})
.dispatch()
.then(function() {
this.refreshQueries();
}.bind(this));
Thanks very much to Joshua Sierles over on the ParseReact github repo for pointing me to the solution. If you are on SO Joshua I'll give you the credit if you post your answer here :D
How to write simple query in couchDB for documents of this type
{
_id: q12312312,
_rev: 1-f591846a021c02a6014702f35cc8627c,
mid: e1e410788853b1cfd7820b9092004686,
uid, e1e410788853b1cfd7820b9092000f60
}
I want to get documents by "uid". In SQL that would be done with this command:
SELECT * FROM users_music WHERE uid="1edsae23wsa"
I have a view in CouchDB
`function(doc)
{
if(doc.uid && doc.mid)
{
emit(doc.uid, doc.mid);
}
}`
When I try to access this view from this link http://localhost:5984/user_music/_design/music/_view/musicview?key=e1e410788853b1cfd7820b9092000f60
I get
{"error":"bad_request","reason":"invalid_json"}
Try
http://localhost:5984/user_music/_design/music/_view/musicview?key="e1e410788853b1cfd7820b9092000f60"
Notice the quotes around the key.
Consider the case:
validatedSchema = new schema({
id : "1454216545154",
name: { type: Number, validate: [
function(v){
return (this.id !== Server.LoggedIn.ID);
}, 'Don't try to change the name of another user!!!!'
]}
})
I haven't yet set up my full server for testing but am in planning stage.
Can we access sibling elements from the validation function, in this case 'id' and 'external' variables? And if so, how?
Thank you
No, you can't. But what you're looking to do here is really access control which doesn't belong in your ORM layer anyway.
But if you really wanted to do this, you could add pre-save middleware to check that the current user can only save changes to their own record:
validatedSchema.pre('save', function (next) {
if (this.id !== Server.LoggedIn.ID) {
next(new Error("Can't change another user's data!"));
} else {
next();
}
});