Marklogic QueryByExample in collection NodeJS - node.js

TLDR
Is there a way to limit queryByExample to a collection in NodeJS?
Problem faced
I have a complex query with some optional fields (i.e. sometimes some search fields will be omitted). So I need to create a query dynamically, e.g. in JSON. QueryByExample seems to be the right tool to use here as it gives me that flexibility to pass a JSON. However my problem is that I would like to limit my search to only one collection or directory.
e.g. I was hoping for something like
searchJSON = {
title: { $word: "test" },
description: { $word: "desc" }
};
//query
db.documents.query(qb.where(
qb.collection("collectionName"),
qb.byExample(searchJSON)
)).result()...
In this case searchJSON could have been built dynamically, for example maybe sometimes title may be omitted from the search.
This doesn't work because the query builder only allows queryByExample to be the only query. But I'd instead like to built a dynamic search query which is limited to a collection or directory.

At present, I think you would have to express the query with QueryBuilder instead of Query By Example using
qb.and([
qb.collection('collectionName'),
qb.word('title', 'test'),
qb.word('description', 'desc')
])
See http://docs.marklogic.com/jsdoc/queryBuilder.html#word
That said, it should be possible for the Node.js API to relax that restriction based on the fixes in MarkLogic 9.0-2
Please file an issue on https://github.com/marklogic/node-client-api

Related

MongoDB legacy uuid query with $in for ids

I am trying to query an old mongoDB collection in node.js with the native driver.
It uses Legacy UUID as its _id field.
In my code I am using Binary values such as: "2u0kLwUZuEWQvjqOjgQU4g=="
and I want to query the collection with find() operation.
When trying to test it directly with mongoDB I am able to use the following query:
db.getCollection('myCollection').find({_id: BinData(3, "2u0kLwUZuEWQvjqOjgQU4g==")})
to find my item.
But what I need to do is to find multiple items.
So I need to use something like this:
db.getCollection('myCollection').find({_ids: { $in: [BinData(3, "2u0kLwUZuEWQvjqOjgQU4g=="), BinData(3, "3u0kLwUZuEWQvjqOjgQU4g==")...]}})
but it does not seem to work and always returns zero records.
I am not sure why? and what might be the correct way to query multiple Legacy UUIDs?

How do I know which fields are indexed in pouchdb if I use query() API?

I am new to pouchdb and I am reading below source code:
db.query('product_index', {
startkey: ["01234"],
endkey: ["01234", {}],
include_docs: false
});
this code executes for a long time. After read some pouchdb document it looks like it builds index on the database when it run the first time. But I don't understand which fields are indexed based on above code.
Below code I can see it builds index on field foo. But how can I understand query API for building index? What is the different between using query and createIndex from index perceptive?
db.createIndex({
index: {
fields: ['foo']
}
})
Have you seen the PouchDB Guide Bulk operations section Please use 'allDocs()'. Seriously.?
Far too many developers overlook this valuable API, because they
misunderstand it. When a developer says "my PouchDB app is slow!", it
is usually because they are using the slow query() API when they
should be using the fast allDocs() API.
When designing your data structures it's very important to bear that in mind. You should define your record id fields to optimize data accessibility through allDocs().

Passing sets of properties and nodes as a POST statement wit KOA-NEO4J or BOLT

I am building a REST API which connects to a NEO4J instance. I am using the koa-neo4j library as the basis (https://github.com/assister-ai/koa-neo4j-starter-kit). I am a beginner at all these technologies but thanks to some help from this forum I have the basic functionality working. For example the below code allows me to create a new node with the label "metric" and set the name and dateAdded propertis.
URL:
/metric?metricName=Test&dateAdded=2/21/2017
index.js
app.defineAPI({
method: 'POST',
route: '/api/v1/imm/metric',
cypherQueryFile: './src/api/v1/imm/metric/createMetric.cyp'
});
createMetric.cyp"
CREATE (n:metric {
name: $metricName,
dateAdded: $dateAdded
})
return ID(n) as id
However, I am struggling to know how I can approach more complicated examples. How can I handle situations when I don't know how many properties will be added when creating a new node beforehand or when I want to create multiple nodes in a single post statement. Ideally I would like to be able to pass something like JSON as part of the POST which would contain all of the nodes, labels and properties that I want to create. Is something like this possible? I tried using the below Cypher query and passing a JSON string in the POST body but it didn't work.
UNWIND $props AS properties
CREATE (n:metric)
SET n = properties
RETURN n
Would I be better off switching tothe Neo4j Rest API instead of the BOLT protocol and the KOA-NEO4J framework. From my research I thought it was better to use BOLT but I want to have a Rest API as the middle layer between my front and back end so I am willing to change over if this will be easier in the longer term.
Thanks for the help!
Your Cypher syntax is bad in a couple of ways.
UNWIND only accepts a collection as its argument, not a string.
SET n = properties is only legal if properties is a map, not a string.
This query should work for creating a single node (assuming that $props is a map containing all the properties you want to store with the newly created node):
CREATE (n:metric $props)
RETURN n
If you want to create multiple nodes, then this query (essentially the same as yours) should work (but only if $prop_collection is a collection of maps):
UNWIND $prop_collection AS props
CREATE (n:metric)
SET n = props
RETURN n
I too have faced difficulties when trying to pass complex types as arguments to neo4j, this has to do with type conversions between js and cypher over bolt and there is not much one could do except for filing an issue in the official neo4j JavaScript driver repo. koa-neo4j uses the official driver under the hood.
One way to go about such scenarios in koa-neo4j is using JavaScript to manipulate the arguments before sending to Cypher:
https://github.com/assister-ai/koa-neo4j#preprocess-lifecycle
Also possible to further manipulate the results of a Cypher query using postProcess lifecycle hook:
https://github.com/assister-ai/koa-neo4j#postprocess-lifecycle

How to properly use 'exist' function in mongodb like in sql?

I'm using Node.js + mongodb. I have few documents in my collection and i want to know does my collection have any document matched my condition. Of course i can simply use
myModel.find({ myField: someValue }) and check is anything comes or not. But i want to use solution like sql provides exists keyword? Help me, please
Edit: my bad. I forget to tell that "performance first".
MongoDB's $exists actually doesn't help you very much to find out if a certain document exists in your collection. It is used for example to give you all documents that have a specific field set.
MongoDB has no native support for an sql like exists. What you can use, however, is myModel.findOne({ myField: someValue }) and then check if it is null.
To enhance performance you can tell MongoDB to only load the object id via projection, like this:
myModel.findOne({ myField: someValue }, {_id: 1})
There is an exist mechanism in mongodb, I'll demonstrate a sample below.
For example below, I'm looking for records that have tomato.consensus fields and that it's empty, so I can delete them or avoid them. In case I was looking for "tomato.consensus": Dublin, I'd change Null to Dublin, to match that.
I hope this is helpful, if not fire away any questions
tomato
----consensus
db.movieDetails.updateMany({$and: [
{"tomato.consensus": {$exists: true} },
{"tomato.consensus": null} ] },
]})

Can I restrict Easy Search to only return published data?

I'm using matteodem's Easy Search package and have just discovered that instead of returning only published documents, the searches have access to the entire collection.
I've tested this by setting my publish function to return an empty array, and then checking that MyCollection.find().fetch() in the console correctly returns []. But searching MyCollection with Easy Search still returns all matching documents in the collection.
Is there any way to ensure that Easy Search only passes permitted data up to the client? I can't find anything in the documentation.
Easy Search is running the search on the server where it has universal access. According to the docs you can setup a default selector to filter the search by some criteria. In your case you can just copy the selector from your normal publication (the first parameter in your publication's find()) and set that as the default selector for Easy Search.
let index = new EasySearch.Index({
collection: someCollection,
fields: ['name'],
engine: new EasySearch.Minimongo({
sort: () => ['score'], // sort by score
selector: function (searchObject, options, aggregation) {
// selector contains the default mongo selector that Easy Search would use
let selector = this.defaultConfiguration().selector(searchObject, options, aggregation);
// modify the selector to only match documents created by the current user
selector.createdBy = this.userId || options.search.userId; // to run on both client and server
return selector;
}
})
});
From matteodem, the developer of Easy Search, I have a simple answer to my original question of how to ensure that only published data are returned by a search.
The easiest way is to use a client-side search engine such as Minimongo. This accesses data through your publications, so will see exactly the data that you have published, no more no less. No selector is needed.
Example code:
postsIndex = new EasySearch.Index({
collection: Posts,
fields: ['name', 'tags', 'created_by_username'],
defaultSearchOptions: {
limit: 6
},
engine: new EasySearch.Minimongo() // search only on the client, so only published documents are returned
});
Alternatively you could use a server-side search engine such as MongoDB. In this case you should add a selector to control what data are returned.
Unfortunately I haven't been able to get selectors working with MongoDB. You can't use Meteor.userId() because the code runs on both server and client, and when I try the recipe for this in the documentation, I see an error on the server. Using Michel Floyd's construction, options.search.userId exists but is null. If I find out how to do it, I'll update this answer for completeness.

Resources