Elasticsearch, MongoDb, Mongoosastic querying array of objects - mongodb

I am trying to index only the 'particulars' field of boqEntry into an Elasticsearch instance using Mongoosastic and later query them. 'boqList' inside BOQ comprises of boqEntries. I have defined my schema as described in the code below. I am indexing my BOQ document after uploading them via FilePond. Whenever I perform the query (http://localhost:9200/boqss/boqs/_search) onto Elasticsearch.
I get a response like this :
"hits": [
{
"_index": "boqss",
"_type": "boqs",
"_id": "5d9730d83f756d04a8d6dbfe",
"_score": 1.0,
"_source": {
"projectId": "5d860711cf3b052a34a84f6c",
"boqList": [
{
"particulars": "Excavation "
},
{
"particulars": "Providing & laying Rubble Soling"
},
{
"particulars": "Providing & laying PCC M10"
},
{
"particulars": "Providing & Laying Reinforcement With Cutting, Bending & Binding With Binding Wire Etc Complete."
},
{
"particulars": "Providing & Making Double Scaffolding"
},
{
"particulars": "Providing & Casting R.C.C M25"
},
{
"particulars": "Providing & Applying Cement Plaster"
},
...
However I want to be able to retrieve only those specific 'particulars' from the boqList array that match a searchTerm from my search box that I am implementing in my React based front-end, like a full text search. I am relatively new to the MERN stack, Mongoose and Elasticsearch. I would really appreciate any help. Thank you !!
Here is the mongoose schema :
const mongoose = require('mongoose')
const Schema = mongoose.Schema
var mongoosastic = require('mongoosastic')
const boqEntry = new Schema({
particulars : {type : String, es_indexed : true},
quantity : {type : Number},
unit : {type : String},
rate : {type : Number},
amount : {type : Number},
});
const BOQ = new Schema(
{
projectId : { type : String},
boqList : {type : [boqEntry], es_type : 'nested'}
},
{ timestamps: true },
);
BOQ.plugin(mongoosastic)
module.exports = mongoose.model('boqs', BOQ)

Related

mongoose find collection by _id in a list of array

Here's my Schema
var PositiveSchema = new mongoose.Schema({
schoolID: {
type: mongoose.Schema.Types.ObjectId, ref: 'School'
},
name: String,
restrictAwardTo: Object
})
Now restrictAwardTo saves the data in this format
"restrictAwardTo" : [
{
"_id" : "5c31907d908eb8404657cbf0",
"firstName" : "Admin 2a"
},
{
"_id" : "5c1a7677c98da061141475a8",
"firstName" : "Justice"
},
{
"_id" : "5c1a7677c98da061141475a9",
"firstName" : "Itik"
}
],
How can I search inside my document using one of the _id listed under restrictAwardTo? I tried the solutions given below
mongooseJS find docs with IDs in an array
mongoose query: find an object by id in an array but it returns empty.
in Robo3t db.getCollection('positives').find({ 'restrictAwardTo._id' : ObjectId('5c31907d908eb8404657cbf0') })
Update: In Robo3t, this query db.getCollection('positives').find({ 'restrictAwardTo._id' : {$in: ['5c1a7677c98da061141475a7']} }) works. Now I'm making it work for mongoose too.
Here's the mongoose that works for me:
Positive.find({ schoolID: mongoose.mongo.ObjectId(schoolID), "restrictAwardTo._id": { $in: [userID]} })
But I'm not entirely sure of the performance for large records.
You could go through this way.
Positive.findOne({'restrictAwardTo': {$elemMatch: {_id: userID}}},
(err,schoolInfo) => { });

Full Text Search on mLab with mongoose: MongoError: text index required for $text query

I'm trying to perform a full text search on a database on mLab. However, apparently the text indexes are not being created.
Mongoose version is 5.4.16
Schema:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var mySchema = new Schema({
title: {
type: String,
trim: true,
required: true
},
description: {
type: String,
trim: true
}
});
mySchema.index({
title: "text",
description: "text",
});
mongoose.model("Model", mySchema);
module.exports = mongoose.model("Model");
Use following to perform the search:
Model.find({
$text: { $search: query }
}).exec(function(err, codes) {...}```
I get the following error:
MongoError: text index required for $text query
I don't know why but I had the same problem with Mongoose and Mlab.
Manually I did the following to solve it.
1) Connect to mlab using the mongo shell
mongo ds245387.mlab.com:45387/<dbname> -u <dbuser> -p <dbpassword>
2) Run db.products.createIndex( { title: "text", "description": "text" } )
3) db.products.getIndexes() should return something like:
{
"v" : 2,
"key" : {
"_fts" : "text",
"_ftsx" : 1
},
"name" : "title_text_description_text",
"ns" : "database-name.products",
"weights" : {
"description" : 1,
"title" : 1
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 3
}
Note: products is your collection name
4) Try your query again!
MongoDB text-search reference

Cannot query nested document's _id (other fields work)

I want to find all documents where vendor._id has a certain value. Below is the code, I tried, but it returns nothing.
let name = sampleData.name, _id = sampleData._id
Product.find({"vendor._id":ObjectID(_id)}).then((products) => {
//returns empty array
})
With the same method I tried to query a different field and it works. But I want to query with _id because other fields could vary with time.
Product.find({"vendor.name":name}).then((products) => {
//returns all documents that satisfy the condition.
})
Below is a sample document which I want to find
{
"status" : "active",
"connectedFarms" : [
{
"_id" : "5c412c62bf8a6602f04ae0bf",
"status" : "inActive",
"margin" : 10,
"price" : 55
},
{
"_id" : "5c4567bcb3845b0536a4d92e",
"status" : "inActive",
"margin" : 20,
"price" : 60
},
{
"_id" : "5c4567c4b3845b0536a4d931",
"status" : "active",
"margin" : 7,
"price" : 53.5
}
],
"vendor" : {
"_id" : ObjectId("5c3fcc0c7657ee02ac24bc21"),
"name" : "manna"
}
}
And here is the schema for this document.
let ProductSchema = new mongoose.Schema({
vendor:{_id:String, name:String},
connectedFarms:[{_id:String, name:String, status:String, price:Number, margin:Number}],
status:{
type:String,
trim: true,
minlength:1
}
});
Let's take a different approach on this, and make vendor its own schema. Mongoose does not allow you to nest schemas, so you cannot make the vendor._id a true ObjectID.
Vendor Schema
const VendorSchema = new mongoose.Schema({
name: string
});
module.exports = mongoose.model('Vendor', VendorSchema);
Product Schema
const ProductSchema = new mongoose.Schema({
vendor: {
type: mongoose.Types.ObjectID,
ref: 'Vendor'
},
connectedFarms: [{
_id: String,
name: String,
status: String,
price: Number,
margin: Number
}],
status: {
type: String,
trim: true,
minlength: 1
}
});
module.exports = mongoose.model('Product', ProductSchema);
Now when you want to query a product based on the vendors _id, it's very simple! All you need to do is supply the _id of the vendor in the query. NOTE: There is no reason to convert the _id to an ObjectID in the query, as mongoose accepts strings and converts them later on.
Query
const vendorID = myVendor._id;
Product.find({ vendor: vendorID })
.then((products) => {
// Do something with the found products
});
That's it! Much simpler to do, and much cleaner in the database. The vendor field is now easier to reference. You also have the ability to get the full vendor object in a query if desired by populating in the query. The difference is, the population will return the vendor name and _id, rather than just the _id. To do this, run the following:
Product.find({ vendor: vendorID })
.populate('vendor')
.then((products) => {
// Do something with the populated found products
});

MongoDB insert a document for every documents in another collection

I have a collection (users) containing some documents like so :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
I would like to create a new document for every entry in the user collection.
The documents would be created from a notification object like this :
{
type: 'alert',
msg: 'This is important'
}
The collection notifications should look something like this :
{
_id: ObjectId("56d45406be05db3021bf20a1"),
someoldcontent: "This was here before the request"
},
{
_id : ObjectId("56d45406be05db4022be20b1"),
user: ObjectId("56d45406be05db4022be51f9"),
type: 'alert',
msg: 'This is important'
},
{
_id : ObjectId("56d45406be05db3021be32e3"),
user: ObjectId("56d45406be05db3021be32e3"),
type: 'alert',
msg: 'This is important'
}
Is there any way to do this in a mongodb request?
Thanks to professor79 for helping me a lot with the question.
After lots of effort, the query was found. We used the aggregation framework in order to succeed.
The only 2 aggregations needed are $project and $out. The $out will automatically take care of the new _id in the notification document.
Given this collection named user :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
We want to create a notification containing the same msg and type field for every document in the user collection.
Each notifications will be in the notifications collection an will have as a reference its corresponding userId.
Here is the query to achieve such a result :
db.user.aggregate([
{
$project: {
userId: '$_id',
type: { $literal: 'danger' },
msg: { $literal: 'This is a message' },
},
},
{ $out: 'notifications' },
])
The output in notifications collection:
{
"_id" : ObjectId("56d6112e197b4ea11a87de1a"),
"userId" : ObjectId("56d45406be05db4022be51f9"),
"type" : "danger",
"msg" : "This is a message"
},
{
"_id" : ObjectId("56d6112e197b4ea11a87de1b"),
"userId" : ObjectId("56d45406be05db3021be32e3"),
"type" : "danger",
"msg" : "This is a message"
}
As we have some chat to clarify problem:
the steps are needed to perform this operation server side:
first step - match > get users id
project ids to new documents as required
out -> store output in notification collection

How to store array of ObjectID's in Mongo with Sails?

After my initial question:
Handling relationships in Mongo and Sails?
I managed to use this approach, however I now need to resolve this the way I originally thought. For example, I have my Photo and Category models & collections, but now my category also contains addresses and business details.
In my Photo model I have the following:
attributes: {
caption : { type : 'string' },
fid : { type : 'string' },
user : { model : 'user' },
categories : { model : 'category', type : 'array'}
}
In my Category model I have the following:
attributes: {
photo : { model: 'photo' },
category : { type: 'string'},
address : { type: 'string'},
phone : { type: 'integer'},
//Foreign Keys
categories : { collection: 'photo', via: 'categories', type : 'integer'}
}
Now I can get the categories in the Photo collection to show as an ObjectID if I removed the type array and just send a single ObjectID of a category, however in my case, a photo can have more than one Category.
If I try sending an array of ObjectID's in my query, they just show up in the database as a String array. Is there anyway I get store these as an array of ObjectID's instead?
Basically it's many to many relationship. You can use Waterline scheme like this:
Photo Model
attributes: {
caption : { type : 'string' },
fid : { type : 'string' },
user : { model : 'user' },
categories : { collection : 'category', via : 'photos'}
}
Category Model
attributes: {
photos : { collection: 'photo', via: 'categories' },
category : { type: 'string'},
address : { type: 'string'},
phone : { type: 'integer'},
}