save:
var pageListSchema = new Schema({
pid:String,
name:String,
eName:String,
pages:[{name:String,id:String,type:String}]
});
var pageList = db.model('pageList',pageListSchema);
var p = new pageList({pid:getId,name:getName,eName:getEName,pages:[{name:"html",id:"0",type:"0"}]});
p.save();
read:
pageList.find({pages:[{id:"0"}]},function(err,data){
console.log(data);
});
pageList.find({pages:{$elemMatch:{id:"0"}}},function(err,data){
console.log(data);
});
result:
[ { _pid: '510a3e793f30c5980f000001'
name: 'cc',
eName: 'cc',
_id: 510a3e803f30c5980f000002,
__v: 0,
pages: [ '[object Object]' ] } ]
there are two method of read data,but why I got a string rather than a object
console.log calls util.inpsect to format your passed data object into a string. That method only recurses two levels deep into objects, by default, so the contents of pages elements aren't formatted.
To get full output for all levels of data:
console.log(util.inspect(data, false, null));
Related
I'm new to MongoDB and I know this might sound silly to many but is there a way(s) where you can create multiple documents from an array of values. I have written some code for this, but is there any other efficient/shorter way of doing that.
tags.map(async tag => {
const tagDoc = await TagModel.create({ tag, postID })
})
Tag Schema -
const tagSchema = new mongoose.Schema({
postID: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
}],
tag: {
type: String,
required: true,
},
}, { timestamps: true })
Case 1 : Storing Tags in Same Collection
in mysql you have to store tags in different table , but in mongodb you can use same collection to store tags as array of string
in your post schema :
const postSchema = new mongoose.Schema({
...
tags: {
type: [String],
required: true,
}
});
when saving post =>
let post = new Post();
...
post.tags = req.body.tags; // ["Tag1","hello","Testing"];
await post.save();
Case 2 : Storing Tags in Different Collection
if you want to store tags in different collection Tag , then you can use Mongoose InsertMany()
let tags = req.body.tags ; // ["Tag1","hello","Testing"];
tagsArray = tags.map((t)=> { return {postID:post._id , tag:t}});
let tagsSaved = await Tag.insertMany(tagsArray);
console.log('tagsSaved=',tagsSaved);
Hello, I need your help please with 2 questions.
I have 2 Models
One to Many
(One) Customer{ id, names, dni} -> Invoice {id, date, ....customer_id} (Many)
1. How can I get this?
I need to consume the api "GET /api/invoices" and that the json return of this, in turn, returns an array
[{
id: 1,
date: '2022-01-01',
....invoice
customer: {
dni: 1,
names: 'Example'
}
},
{
id: 2,
date: '2022-01-02',
....invoice
customer: {
dni: 2,
names: 'Example 2'
}
},
]
So far what I have found in the sailsjs documentation are only examples with POPULATE, where they only show how to list the User model with its corresponding created ones (hasMany)
//var users = await User.find().populate('pets');
// The users object would look something like the following
// [{
// id: 123,
// firstName: 'Foo',
// lastName: 'Bar',
// pets: [{
// id: 1,
// breed: 'labrador',
// type: 'dog',
// name: 'fido',
// user: 123
// }]
// }]
//---This is not what I need.
Is there a function or configuration that I have not found?
Or would I do something like this?
Invoices.find().exec(async(err, invoices)=>{
if(invoices){
for(i = 0; i< invoices.length; i++){
const customer = await Customer.find({id: invoices[i].customer_id});
invoices[i].customer = customer;
}
});
The point is that this takes much longer than doing a query with join
const invoices = await sails.sendNativeQuery('SELECT * from INVOICE A A inner join CUSTOMER B on A.customer_id=B.id ', []);
But I don't know how to get a JSON with the previous structure if I do it by query
2. What is the best option that can solve my problem?
The populate method works in both directions: oneToMany, manyToMany, and manyToOne:
https://sailsjs.com/documentation/reference/waterline-orm/queries/populate
If any condition is required, you could check the details on the section Populating a collection association:
var usersNamedFinn = await User.find({ name:'Finn' })
.populate('currentSwords', {
where: {
color: 'purple'
},
limit: 3,
sort: 'hipness DESC'
});
I am trying to find out how Bloodhound works (without typeahead).
var engine = new Bloodhound({
local: [{ id: 1, name: 'dog' }, { id: 2, name: 'pig' }],
identify: function(obj) { return obj.id; },
queryTokenizer: Bloodhound.tokenizers.whitespace,
datumTokenizer: Bloodhound.tokenizers.whitespace
});
engine.search('do', function(datums) {
console.log(datums); // results: []
});
In this very basic example, why does my search not return my first item? What I am doing wrong?
Out of the box, Bloodhound tokenizers work for an array of a basic type. You have "complex" data (an object with 2 properties), so you must tell Bloodhoud what to tokenize using the obj tokenizer and passing property names:
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("id", "name"),
In the following link from mongoDB documentation:
https://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/
there is an explanation on how to build a "safe" insert with auto-incremental _id.
My questions are:
Where should i define the function at?
How can i call it later on?
I couldn't quit understand if the function is stored as a JS function is a JS file? or is it stored in the DB? or something else.
Thanks.
Here is the actual function as it is brought in the link above:
function insertDocument(doc, targetCollection) {
while (1) {
var cursor = targetCollection.find( {}, { _id: 1 } ).sort( { _id: -1 } ).limit(1);
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
doc._id = seq;
var results = targetCollection.insert(doc);
if( results.hasWriteError() ) {
if( results.writeError.code == 11000 /* dup key */ )
continue;
else
print( "unexpected error inserting data: " + tojson( results ) );
}
break;
}
}
Implementation approach
Here's an example of how to implement an auto-increment field with mongoose:
var CounterSchema = Schema({
_id: {type: String, required: true},
seq: { type: Number, default: 0 }
});
var counter = mongoose.model('counter', CounterSchema);
var entitySchema = mongoose.Schema({
testvalue: {type: String}
});
entitySchema.pre('save', function(next) {
var doc = this;
counter.findByIdAndUpdate({_id: 'entityId'}, {$inc: { seq: 1} }, function(error, counter) {
if(error)
return next(error);
doc.testvalue = counter.seq;
next();
});
});
Source: https://stackoverflow.com/a/30164636/236660
Please note that {_id: 'entityId'} should be set differently per collection you're using this code with. So if you're generating an ID for entitySchema, you'll have {_id: 'entityId'}. For userSchema you would use {_id: 'userId'}, etc. You need to do this so that every collection has its own auto-incremented sequence.
The code above is thread safe. The findByIdAndUpdate operation is atomic; its consistency and concurrency are handled by MongoDB.
Existing module
Also, there is a mongoose module created specifically for handling auto-incremented IDs. You may actually be better off using it:
https://www.npmjs.com/package/mongoose-auto-increment
I'm using aldeed:simple-schema and here's the code:
Cities = new Mongo.Collection('cities');
Cities.insert({
name: 'Oslo'
});
Cities.insert({
name: 'Helsinki'
});
Contact = new SimpleSchema({
city: {
type: String,
allowedValues: Cities.find().map((e) => e.name) // written ES6-style for readability; in fact, here goes an ES5 anonymous function definition
}
});
What it does is explicitly binds currently existing cities from Cities collection to Contact schema's certain field's allowed values, so it's then impossible to store any other value than "Oslo" or "Helsinki".
But when posting a quickForm, the field (select, actually) has no options.
If I rewrite the mapping function to
(e) => {
console.log(e);
return e.name;
}
then I get
I20150911-18:07:23.334(4)? { _id: 'GLAbPa6N4W4c9GZZh', name: 'Oslo' }
I20150911-18:07:23.333(4)? { _id: 'vb64X5mKpMbDNzCkw', name: 'Helsinki' }
in server logs, which makes me think the mapping function is correct.
At the very same time, doing all this in Mongo console returns desirable result:
production-d:PRIMARY> db.cities.find().map(function (e) { return e.name; });
[ "Oslo", "Helsinki" ]
What do I do wrong? Is it impossible to fill the simple-schema's allowedValues array at the run time?